1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Unity DOTS】親Entityの削除時に子Entityも一緒に削除する方法

Last updated at Posted at 2019-12-30

はじめに

親子関係のあるオブジェクトを削除する時、従来のUnityであれば親オブジェクトを削除すれば子オブジェクトも併せて一緒に削除されます。
しかし、ECSでは親Entityを削除しても子Entityは削除されず、データが書き変わって残り続けてしまいます。
そのような問題を解決する方法をこの記事に書きました。

実行環境

  • Unity 2019.3.0f3
  • Entities 0.4.0 preview.10
  • Hybrid Renderer 0.3.1 preview.10

注意

  • 本記事ではpreview packageを多く使用しています。今の実装と本記事の実装が大幅に異なる可能性があるのでご注意下さい。
  • 本記事ではGameObject/Component を使用したHybrid ECSを前提としています。

問題

下図のように2つのオブジェクトがある場合を考えます。
白くて大きい立方体のオブジェクトをParentCube
黒くて小さい立方体のオブジェクトをChildCubeと呼ぶことにします。

スクリーンショット 2019-12-30 午後0.54.16.png

これら2つのオブジェクトには親子関係があり、ParentCubeChildCubeの親オブジェクトである、とします。
スクリーンショット 2019-12-30 午後0.54.41.png

ここで、これらのオブジェクトをEntity化することを考え、ParentCubeにのみConvertToEntityをAddします。

実行してEntityDebuggerを確認してみると、
スクリーンショット 2019-12-30 午後1.07.36.png
確かにParentCubeChildCubeもEntityに変換されます。

ここからが本題ですが、ここでParentCubeを削除しようと思います。

まず、ParentCubeを識別するためのタグと、その「削除」を行うタイミングを知らせるためのタグを作成します。

ParentCube.cs
using Unity.Entities;

/// <summary>
/// ParentCubeを識別するためのタグ
/// </summary>
public struct ParentCube : IComponentData
{
}
Destroyable.cs
using Unity.Entities;

/// <summary>
/// 削除するタイミングを知らせるためのタグ
/// </summary>
public struct Destroyable : IComponentData
{
}

次のようなスクリプトを作成し、ParentCubeにアタッチします。

ParentCubeAuthoring.cs
using UnityEngine;
using Unity.Entities;

public class ParentCubeAuthoring : MonoBehaviour, IConvertGameObjectToEntity
{
    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddComponentData(entity, new ParentCube());
    }
}

そして、ParentCubeを削除する(そしてそれによりChildCubeも削除されることを期待する)、DestroyCubesSystemを作成します。

コードを見る
DestroyCubesSystem.cs
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Transforms;

public class DestroyCubesSystem : JobComponentSystem
{
    private EntityCommandBufferSystem _bufferSystem;

    protected override void OnCreate()
    {
        _bufferSystem = World.GetExistingSystem<EndSimulationEntityCommandBufferSystem>();
    }
    
    [BurstCompile]
    private struct DestroyCubesJob : IJobForEachWithEntity<ParentCube, Destroyable>
    {
        public EntityCommandBuffer.Concurrent CommandBuffer;

        public void Execute(Entity entity, int index, [ReadOnly] ref ParentCube c0, [ReadOnly] ref Destroyable c1)
        {            
            CommandBuffer.DestroyEntity(index, entity);
        }
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var jobHandle = new DestroyCubesJob
        {
            CommandBuffer = _bufferSystem.CreateCommandBuffer().ToConcurrent(),
        }.Schedule(this, inputDeps);

        return jobHandle;
    }
}

これで実行してみると、

スクリーンショット 2019-12-30 午後1.46.36.png スクリーンショット 2019-12-30 午後1.46.48.png

確かにParentCubeは削除されたのですが、ChildCubeは削除されません。

さらに、(今回のケースではあまり関係ないですが)LocalToWorldのデータがLocalToParentのデータに置き換わってしまっているので、予期せぬ場所に子Entityが出現したりして、かなり困ることもあるかもしれません。

解決法

子Entityの参照が、親Entityに付いているChildというBufferElementDataに入っているので、それを使って子Entityを削除します。

DestroyCubesSystem.cs
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Transforms;

public class DestroyCubesSystem : JobComponentSystem
{
    private EntityCommandBufferSystem _bufferSystem;

    protected override void OnCreate()
    {
        _bufferSystem = World.GetExistingSystem<EndSimulationEntityCommandBufferSystem>();
    }
    
    [BurstCompile]
    private struct DestroyCubesJob : IJobForEachWithEntity<ParentCube, Destroyable>
    {
        public EntityCommandBuffer.Concurrent CommandBuffer;
        
        // 追加
        [ReadOnly] public BufferFromEntity<Child> ChildBufferFromEntity;

        public void Execute(Entity entity, int index, [ReadOnly] ref ParentCube c0, [ReadOnly] ref Destroyable c1)
        {
            // 追加
            var childEntityBuffer = ChildBufferFromEntity[entity];
            
            // 追加
            for (var i = 0; i < childEntityBuffer.Length; i++)
            {
                CommandBuffer.DestroyEntity(index, childEntityBuffer[i].Value);
            }
            
            CommandBuffer.DestroyEntity(index, entity);
        }
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var jobHandle = new DestroyCubesJob
        {
            CommandBuffer = _bufferSystem.CreateCommandBuffer().ToConcurrent(),
            // 追加
            ChildBufferFromEntity = GetBufferFromEntity<Child>()
        }.Schedule(this, inputDeps);

        return jobHandle;
    }
}
これで子オブジェクトも削除されるはずです。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?