Shutdown ECS/Scene switching

I want to switch to a non-ECS scene, i.e Main Menu. Every time I try to clean things up, I get a ton of errors.

This is what I have so far

    private void OnDestroy()
    {
        if (_blobStore != null) {
            _blobStore.Dispose();
        }

        _entityManager.DestroyEntity(_entityManager.UniversalQuery);
        _world.DestroyAllSystemsAndLogException();
        _world.Dispose();

        _gameObjectSettings = null;
        _world = null;
    }

I’ve tried various combinations, but I still receive multiple errors:
ArgumentException: The World has already been Disposed.
Unity.Entities.World.Dispose () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/World.cs:268)
Startup.OnDestroy () (at Assets/Source/Startup.cs:58)

InvalidOperationException: object is not initialized or has already been destroyed
Unity.Entities.ComponentSystemBase.CheckedState () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/ComponentSystemBase.cs:331)
Unity.Entities.ComponentSystem.Update () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/ComponentSystem.cs:86)
Unity.Entities.ScriptBehaviourUpdateOrder+DummyDelegateWrapper.TriggerUpdate () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/ScriptBehaviourUpdateOrder.cs:196)

Before, I only had the blob asset dispose. Everything was fine. Now If I only dispose of the blob asset reference I get this error:

InvalidOperationException: The BlobAssetReference is not valid. Likely it has already been unloaded or released.
Unity.Entities.BlobAssetReferenceData.ValidateNonBurst () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/Blobs.cs:212)
Unity.Entities.BlobAssetReferenceData.ValidateNotNull () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/Blobs.cs:228)
Unity.Entities.BlobAssetReference1[T].get_Value () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/Blobs.cs:309) Unity.Physics.Broadphase+PrepareDynamicBodyDataJob.ExecuteImpl (System.Int32 index, System.Single aabbMargin, Unity.Mathematics.float3 gravity, System.Single timeStep, Unity.Collections.NativeSlice1[T] rigidBodies, Unity.Collections.NativeSlice1[T] motionDatas, Unity.Collections.NativeSlice1[T] motionVelocities, Unity.Collections.NativeArray1[T] aabbs, Unity.Collections.NativeArray1[T] points, Unity.Collections.NativeArray1[T] filtersOut) (at Library/PackageCache/com.unity.physics@0.3.2-preview/Unity.Physics/Collision/World/Broadphase.cs:810) Unity.Physics.Broadphase+PrepareDynamicBodyDataJob.Execute (System.Int32 index) (at Library/PackageCache/com.unity.physics@0.3.2-preview/Unity.Physics/Collision/World/Broadphase.cs:790) Unity.Jobs.IJobParallelForExtensions+ParallelForJobStruct1[T].Execute (T& jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at <94c5f4c38cdc42d2b006f8badef04394>:0)

1 Like

I looked at this post: ECS Tear Down and Restart

And tried this:

private void OnDestroy()
    {
        _entityManager.DestroyEntity(_entityManager.UniversalQuery);

        if (_world.IsCreated) {
            _world.Dispose();
            _world.DestroyAllSystemsAndLogException();
        }

        if (_blobStore != null) {
            _blobStore.Dispose();
        }
    }

I now only have one error:
InvalidOperationException: object is not initialized or has already been destroyed
Unity.Entities.ComponentSystemBase.CheckedState () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/ComponentSystemBase.cs:331)
Unity.Entities.ComponentSystem.Update () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/ComponentSystem.cs:86)
Unity.Entities.ScriptBehaviourUpdateOrder+DummyDelegateWrapper.TriggerUpdate () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/ScriptBehaviourUpdateOrder.cs:196)

I guess I’ll try to find a workaround. Maybe just destroying the entities, and never switching the scene. Just add the menu scene on top.

I’m going to assuming that no one has had these issues.

Hi!
I have the same issues in my unit tests. I haven’t found a way to properly dispose a world and recreate one after.

I assumed it was a bug and didn’t have the time to properly debug it. Let me know if you found a workaround!

Out of curiosity why do you need to destroy the world?

If you destroy all entities with the universal query, then you shouldn’t need to dispose of the world. I know that the systems will still exist, but with no entities to process I doubt they would any kind of noticeable performance impact.

Now if you need to a different world (i.e. a custom bootstrap) for different levels then I could see the need for disposing a world. I’m also curious to know how to do this for this use case.

Never tried something like that, but shouldn’t you need to use ScriptBehaviourUpdateOrder.RemoveWorldFromCurrentPlayerLoop(_world) before destroying all systems?

EDIT: also, I think you need to destroy the systems before disposing the world

private void OnDestroy()
{
    if (_world.IsCreated) {
        _entityManager.DestroyEntity(_entityManager.UniversalQuery);
        ScriptBehaviourUpdateOrder.RemoveWorldFromCurrentPlayerLoop(_world);
        _world.DestroyAllSystemsAndLogException();
        _world.Dispose();
    }
    if (_blobStore != null) {
        _blobStore.Dispose();
    }
}

(Disclaimer: Opinion)
This might not be a solution to your problem, but I agree with the notion that you probably shouldn’t need to destroy the whole world. Systems shouldn’t contain any state to make that necessary. Spawning a set of entities should not lead to different results depending on wether or not similar archetypes existed previously.

1 Like

Or you can clean the state inside OnStopRunning. If it is a System which always update then it may be needed a different approach, but I still think that destroying the World is unnecessary unless you want to create a totally different one later.

1 Like

I was using unity version 2019.3.2f1, and upgraded to 2020.1.0f1 and everything is fine. I think there is a bug with that version.

@brunocoimbra I did have try what you had before posting to the forums, and it would still throw exceptions. I think the unity version was the problem.

Oh, right, actually ScriptBehaviourUpdateOrder.RemoveWorldFromCurrentPlayerLoop was introduced in Entities 0.13, which is only supported on 2020.1

1 Like

Nope; World.Dispose() will destroy all its systems, and its EntityManager (which will destroy all the World’s Entities). So, you shouldn’t need to do any of those things when destroying a World.

World.Dispose() does not call ScriptBehaviourUpdateOrder.RemoveWorldFromCurrentPlayerLoop(), because it’s actually a fairly expensive function. If you manually Dispose() a World, you should manually remove it from the player loop. Leaving disposed Worlds in the player loop isn’t (puts on shades) the end of the world (yeaaaaaaaaaah!), as it will check if the World’s systems are null before trying to update them. But it does clutter up the player loop with extra entries, which could have an impact on performance after a while.

10 Likes

Would this be an good approach for using ScriptBehaviourUpdateOrder.RemoveWorldFromCurrentPlayerLoop()? Because just deleting entities and disposing world still gives some errors in my project.

var entitymanager = World.DefaultGameObjectInjectionWorld.EntityManager;
entitymanager.DestroyEntity(entitymanager.UniversalQuery);
ScriptBehaviourUpdateOrder.RemoveWorldFromCurrentPlayerLoop(World.DefaultGameObjectInjectionWorld);
DefaultWorldInitialization.Initialize("Default World", false);
SceneManager.LoadScene("World", LoadSceneMode.Single);

After applying this code the systems the hierachy is lost in resetted entities created from the conversion… I can see all child enities in the hierachy non have an parent. but maybe this could be an bug from DOTS editor