Callbacks After AssetDatabase.Refresh

Hey everyone, I am creating a c# script file in an editor script, then i want to create asset from that script. My created c# file is simple SO script like public class TestClass : Scriptable Object { }, and after creation, i want to be able to create a SO asset from that. IN summary, what i want to do is: Create a C# class derived from Scriptable Object within editor with given name. I basically create C# class by WriteAllText(); and give template inside of it. (Successfully did.) Create an SO asset with that type. (I have the method to create, but it should be work after AssetDatabase.Refresh(), this is where i STUCK) This is how i create my SO file with that created class text. I am calling this method after AssetDatabase.Refresh, but its not working. But if i assign it to a button, and simply click button after reloading domain, it works. But i want to automatize it.

    private void CreateSO()
   {
       // Create SO instance
       var asset = CreateInstance(_channelClassText);
       string path = AssetDatabase.GenerateUniqueAssetPath(_currentFolder + "/" + _channelClassText + ".asset");
       AssetDatabase.CreateAsset(asset, path);
       EditorUtility.FocusProjectWindow();
   }

IF i call it after Refresh(), since it directly call the method and dont wait for refresh… gives an error. Instance of TestClassChannel couldn’t be created because there is no script with that name.

Also tried this, But compilation finished, there are no class existing i think.

CompilationPipeline.compilationFinished += TryCreateAsset;

I also called this one before Refresh() call but nothing seems to happen. CreateAsset simply isnt called.

AssemblyReloadEvents.afterAssemblyReload += CreateAsset;

Here is my full method:

    private void TryCreateObjects()
    {
        var listenerClassText = _listenerClassField.value;
        _channelClassText = _channelClassField.value;
        var typeText = _typeField.value;

        // Check empty fields
        if (string.IsNullOrEmpty(listenerClassText) || string.IsNullOrEmpty(_channelClassText) ||
            string.IsNullOrEmpty(typeText))
        {
            Debug.LogWarning("You have empty fields. Please fill them.");
            return;
        }

        CreateListenerClass(listenerClassText, typeText);
        CreateChannelClass(_channelClassText, typeText);


        AssemblyReloadEvents.afterAssemblyReload += CreateAsset;
        // Refresh it.
        AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
    }

First:

  • Implement a ScriptableSingleton that controls the process.

  • The SS will automatically restore its state after a script compilation (this is what the manual refers to as “survives domain reload”).

  • Use a [InitializeOnLoadMethod] to have a hook for when script compilation finished. The persisted SS state will allow you to either ignore it or act on the new file having been created.

  • Note: I don’t think assembly reload or script compile events will be persisted in SS because events cannot be serialized (easily anyway) - but give it a try, maybe I’m wrong.

The basic outline of the asset creation process would be:

  • Write the script file with File.WriteAllText as you already do, followed by:

  • AssetDatabase.ImportAsset(scriptFilePath)

  • wait for compilation to finish (it should compile automatically)

  • if for whatever reason it does not auto-compile call CompilationPipeline.RequestScriptCompile

  • Create an asset for this new script

  • This requires having the type. You can find it by name in the TypeCache.

  • AssetDatabase.CreateAsset(theNewAssetObject)

There is no need to call AssetDatabase.Refresh (aka “ImportAllPotentiallyExternallyModifiedAssetsAndUnloadUnusedResources”) in this process, neither after writing the script file nor after creating the asset. That will only serve to make the process slower and consume more resources.

1 Like