SubSceneで独自のコンポーネントや、まだ変換先の無いコンポーネントをComponentDataに差し替えるアプローチについてです。
GameObjectベースのデータをECSベースへ変換する
ECS 0.0.12 preview 26
からGameObjectをEntityに変換して使用するといったワークフローが充実してきました。今まではGameObjectEntity
など、どちらかと言えば ComponentをEntityに登録するアプローチ、言うなれば GameObjectワークフローをECSで使用する為のアプローチ*1 でした。
そしてpreview 26において*2 GameObjectをECS用に変換するアプローチが登場します。 特に分かりやすい機能の使い方としてはSubSceneで、既存のシーンをECS用のデータに変換するというものです。
GameObjectをEntityへ変換するAPI
GameObjectをECSへ変換するためのAPIは幾つかあります。
まずGameObjectConversionUtility.ConvertGameObjectHierarchy(...)
です。これは指定のGameObjectをEntityへ変換します。基本的に個別の制御になっています。
なお変換後はRenderBounds等が無い為に表示はされませんが、ECSとしては生成されている状態です。複数回呼ぶ場合はキャッシュするなりするのが良さそうに見えます。
Entity entity = GameObjectConversionUtility.ConvertGameObjectHierarchy(prefab, World.Active); World.Active.EntityManager.Instantiate(entity);
このAPIのコンポーネント版が、ConvertToEntity
です。登録したオブジェクトを自動的にEntityへ変換します。
これはGameObjectEntity
と異なりTransform等の変換可能なコンポーネントは全て変換されます。またECSの計算結果をGameObjectへ戻す機能は提供されていません。
それとGameObjectConversionUtility.ConvertScene
がありますが、ランタイムで呼ぶようなものではないのでSubScene
ワークフローで変換したものを使うのが良さそうです。
MonoBehaviourをComponentDataに変換してもらうためのコード
MonoBehaviourを自分で記述しているならば、IConvertGameObjectToEntity
を利用してEntityを変換可能にします。
実装方法はこんな感じです。ComponentDataProxy
と異なり、一つのコンポーネントで複数のComponentDataを追加出来る点がポイントかもしれません。
public class MyDataProxy : MonoBehaviour, IConvertGameObjectToEntity { [SerializeField] int m_Value; public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { // コンポーネントを追加 dstManager.AddComponentData(entity, new PlayerTag()); dstManager.AddComponentData(entity, new MyData { Value = m_Value }); } } [System.Serializable] public struct MyData : IComponentData { public int Value; } public struct PlayerTag : IComponentData { }
このアプローチでは、他のEntityを参照するEntityも作れます。
public class Follow : MonoBehaviour, IConvertGameObjectToEntity { [SerializeField] GameObject obj; public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { var target = conversionSystem.GetPrimaryEntity(obj); // GetPrimaryEntityで対象のEntityを取得 dstManager.AddComponentData(entity, new Target { Value = target }); } } public struct Target : IComponentData { public Entity Value; }
コンポーネントをComponentDataに変換してもらうためのコード
Hierarchyを汚さず、かつコンポーネントのカスタムが出来ない場合はGameObjectConversionSystem
を使います。
例えば下のはBoxColliderを自分自身の定義したMyBoxColliderへ変換するコードです。IConvertGameObjectToEntity
と異なり「コンポーネントの組み合わせで差し替える対象を変更出来る」「既存のコンポーネントを差し替えられる」「実行順番を制御できる」という点で、有利です。
public class ColliderConversionSystem : GameObjectConversionSystem { protected override void OnUpdate() { Entities.ForEach((BoxCollider collider) => { var entity = GetPrimaryEntity(collider); var myBoxCollider = new MyBoxCollider { Size = collider.size, IsTrigger = collider.isTrigger ? 1 : 0 }; DstEntityManager.AddComponentData(entity, myBoxCollider); }); } }
感想
ある意味、実装と設定が明確に別れたといった感じでしょうか。
少し面倒ですが、大分納得できる感じになりました。
現在マニュアルで紹介されているサンプルは全てこのアプローチで構成されている辺り、主流になって欲しいアプローチなのかもしれません。
関連
Entityを生成するサンプル
https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/Samples/Assets/HelloECS/HelloCube_05_SpawnFromMonoBehaviourgithub.com
大規模な場所でConvertToEntityをする場合はSubSceneを使用するのをオススメ