Editor scripting with ECS

Hi,
I’m thinking of writing editor code using ECS archtecture, which is quite interesting. I wonder if it’s possible to do it without running play mode?

I played around it for a while and stuck with this code below. However it seems ComponentSystem doesn’t update. In entity debugger, everything is just fine - there is an Entity 0 in TestEditorSystem and it says, that system is running.
So, maybe I’m doing something wrong?

public struct SomeData : IComponentData
{
    public float2 someFloat;
}

public class TestEditorSystem : ComponentSystem
{
    struct SomeGroup
    {
        public int Length;
        public ComponentDataArray<SomeData> someDatas;
    }
    [Inject] SomeGroup someGroup;

    protected override void OnCreateManager(int capacity)
    {
        Debug.Log("OnCreateManager");  // It's printed
        Debug.Log(someGroup.Length);   // "1"
        base.OnCreateManager(capacity);
    }

    protected override void OnUpdate()
    {
        Debug.Log("OnUpdate");  // It's not printed
    }
}
public class SomeEditorWindow : EditorWindow
{
    [MenuItem("CustomMenu/Window")]
    public static void OpenWindow()
    {
        GetWindow<SomeEditorWindow>("Some", true);
    }

    private void OnEnable()
    {
        World world = new World("SomeWorld");
        World.Active = world;
        EntityManager entityManager = world.GetOrCreateManager<EntityManager>();

        var entity = entityManager.CreateEntity(ComponentType.Create<SomeData>());
        entityManager.SetComponentData(entity, new SomeData { someFloat = new float2(4, 8) });
        world.CreateManager<TestEditorSystem>();
    }

    private void OnDisable()
    {
        World.Active.Dispose();
    }
}

have you tried [ExecuteInEditMode]?

Well, yeah, tried this but it didn’t fix the issue.

apparently you need to

ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world);

to insert the world updates into Unity Update loop.
it’s done automatically only in playmode (see AutomaticWorldBootstrap.cs in the package)

don’t know if player loop works in edit mode though.
worst case you manually Update() your system in EditorApplication.update

Hmm… this works:

private void OnGUI()
{
    ScriptBehaviourUpdateOrder.UpdatePlayerLoop(EditorWorld);
}

But yeah, you can’t call any gui function outside OnGUI and editorgui’s functions throws null reference, so I don’t really know how could I manage gui layout from systems.
I’ve got idea about something like this, however I don’t think it’s correct hack. As far as I checked it prevents checks for entity debugger.

private void OnGUI()
{
    foreach (var behaviour in EditorWorld.BehaviourManagers)
    {
        behaviour.Update();
    }
}

wait, are you calling GUI/GUILayout functions in your update? I don’t think it’s a clean way to do it. you can use UIElements to do editor GUI stuff outside OnGUI instead

also UpdatePlayerLoop should be called once at initialization (or when you change worlds) as it seems to do some heavy stuff (I poked at the source, and it builds the dependency graph for all systems in your world – the UpdateBefore/UpdateAfter stuff)

Ok, I just checked that, it’s not that good as I thought :confused: Unfortunately ScriptBehaviourUpdateOrder.UpdatePlayerLoop(EditorWorld); on enable doesn’t work.

What UIElements do you mean? Are there any other editor functions for editor windows than editorgui/gui? I’ve always used GUI’s.

you create the tree in OnEnable and attach it to the window.

it is experimental, though.

I took a look at this, amazing, I’ll dive into it. As I think about editor scripting with ecs now, I’m not so sure if I should use this design for editor scripting, however thank you for reply!

I wonder we can use ECS and Job system to make Editor script too.

1 Like

Here is my working solution :

 [ExecuteAlways]
    public class NoiseEditor : MonoBehaviour
    {
        NoiseSystem _noiseSystem;

        public void OnEnable()
        {
            Debug.Log("---------------------------------------------------");//helps see wich logs are from the last compile
            if (EditorApplication.isPlaying || _noiseSystem != null) return;

            DefaultWorldInitialization.DefaultLazyEditModeInitialize();//boots the ECS EditorWorld

            _noiseSystem = World.Active.GetOrCreateSystem<NoiseSystem>();

            EditorApplication.update += () => _noiseSystem.Update();//makes the system tick properly, not every 2 seconds !
        }

        public void OnDestroy()
        {
            //extra safety against post-compilation problems (typeLoadException) and spamming the console with failed updates
            if (!EditorApplication.isPlaying)
                EditorApplication.update -= () => _noiseSystem.Update();
        }
1 Like

There was also InitializeOnLoadMethod and RuntimeInitializeOnLoadMethod attribute that I would use for ecs system