Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Cleaning up Entities from MonoBehaviours

Discussion in 'Entity Component System' started by alec100_94, Sep 24, 2020.

  1. alec100_94

    alec100_94

    Joined:
    Jan 9, 2017
    Posts:
    26
    I am still quite new to ECS, I have managed to setup a basic stats system with it that works quite well for my needs, but now I seem to be having a problem calling DestroyEntity. I am creating and destroying my entities from MonoBehaviour "Manager" scripts. It works just fine in normal gameplay, but (often) throws a lot of quite nasty looking errors in the console once I exit play mode, and I'm worried that could be part of a much bigger problem. Here is my code and it is the destroy entity part (in OnDestroy) that is causing the problem.

    Code (CSharp):
    1. public class MonsterStatsManager : MonoBehaviour
    2. {
    3.     public MonsterStats Stats => entity.Index > 0 ? entityManager.GetComponentData<MonsterStats>(entity) : monsterStats;
    4.  
    5.     [SerializeField] MonsterStats monsterStats;
    6.  
    7.     Entity entity;
    8.     static EntityManager entityManager;
    9.     static EntityArchetype monsterArchetype;
    10.  
    11.     void Awake()
    12.     {
    13.         entityManager = entityManager == default(EntityManager) ? World.DefaultGameObjectInjectionWorld.EntityManager : entityManager;
    14.         monsterArchetype = monsterArchetype == default(EntityArchetype) ? entityManager.CreateArchetype(typeof(MonsterStats), typeof(MonsterStatsDispatcher)) : monsterArchetype;
    15.     }
    16.  
    17.     void Start()
    18.     {
    19.         entity = entityManager.CreateEntity(monsterArchetype);
    20.         monsterStats.name = Regex.Replace(gameObject.name, @" ?\(.*?\)", string.Empty).Trim();
    21.         entityManager.SetName(entity, $"MonsterStats ({gameObject.name})");
    22.         entityManager.SetComponentData(entity, monsterStats);
    23.     }
    24.  
    25.     void OnDestroy() => entityManager.DestroyEntity(entity);
    26.  
    27.     public void Dispatch<T>(T dispatch) where T : struct => StatsDispatchHelpers.Dispatch<T, MonsterStatsDispatcher>(dispatch, entity);
    28. }
    The error I am getting is this and sometimes multiple times, happens only when stopping play mode, and sometimes doesn't happen:

    InvalidOperationException: The NativeArray has been deallocated, it is not allowed to access it
    Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckWriteAndThrowNoEarlyOut (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at <2feaf16e80004e0cadae3f2e05f2a3fa>:0)
    Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckWriteAndThrow (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at <2feaf16e80004e0cadae3f2e05f2a3fa>:0)
    Unity.Entities.EntityManager.GetCheckedEntityDataAccess () (at Library/PackageCache/com.unity.entities@0.11.2-preview.1/Unity.Entities/EntityManager.cs:70)
    Unity.Entities.EntityManager.DestroyEntityInternal (Unity.Entities.Entity* entities, System.Int32 count) (at Library/PackageCache/com.unity.entities@0.11.2-preview.1/Unity.Entities/EntityManagerCreateDestroyEntities.cs:341)
    Unity.Entities.EntityManager.DestroyEntity (Unity.Entities.Entity entity) (at Library/PackageCache/com.unity.entities@0.11.2-preview.1/Unity.Entities/EntityManagerCreateDestroyEntities.cs:172)
    PlayerStatsManager.OnDestroy () (at Assets/Scripts/Stats/PlayerStatsManager.cs:34
     
  2. alec100_94

    alec100_94

    Joined:
    Jan 9, 2017
    Posts:
    26
    Think I figured out this one myself, it was my cached EntityManager that was causing the problem, I reworked it a bit to check if the world was null (and also read EntityManager directly from World.DefaultGameObjectInjectionWorld).

    Code (CSharp):
    1. public class MonsterStatsManager : MonoBehaviour
    2. {
    3.     public MonsterStats Stats => entity.Index > 0 ? World.EntityManager.GetComponentData<MonsterStats>(entity) : monsterStats;
    4.  
    5.     [SerializeField] MonsterStats monsterStats;
    6.  
    7.     Entity entity;
    8.     static World World => World.DefaultGameObjectInjectionWorld;
    9.     static EntityArchetype monsterArchetype;
    10.  
    11.     void Awake()
    12.     {
    13.         monsterArchetype = monsterArchetype == default(EntityArchetype) ? World.EntityManager.CreateArchetype(typeof(MonsterStats), typeof(MonsterStatsDispatcher)) : monsterArchetype;
    14.     }
    15.  
    16.     void Start()
    17.     {
    18.         EntityManager entityManager = World.EntityManager;
    19.         entity = entityManager.CreateEntity(monsterArchetype);
    20.         monsterStats.name = Regex.Replace(gameObject.name, @" ?\(.*?\)", string.Empty).Trim();
    21.         entityManager.SetName(entity, $"MonsterStats ({gameObject.name})");
    22.         entityManager.SetComponentData(entity, monsterStats);
    23.     }
    24.  
    25.     void OnDestroy() => World?.EntityManager.DestroyEntity(entity);
    26.  
    27.     public void Dispatch<T>(T dispatch) where T : struct => StatsDispatchHelpers.Dispatch<T, MonsterStatsDispatcher>(dispatch, entity);
    28. }