概要
掲題の通りです。
Unity ECS を扱うにあたってちょっとハマったので備忘として掲載しておきます。
ちなみにECSは全然詳しくないです。使えたら強力なのでしょうが、バージョン変わるたびに色々と変わってサードパーティのライブラリが利用できなくなったりしちゃうんで、まだまだ発展途上な気はしますね・・・
また情報があまりなく試行錯誤でやってるため、もっとスマートなやり方もあるかも。
動作環境
- Windows 11
- Unity 6
- com.unity.entities v1.3.9
- com.unity.entities.graphics v1.3.2
- com.unity.collections v2.5.2
- com.unity.physics v1.3.9
やりたいこと
MonoBehavior側からEntityManagerを呼び出し、ECSのWorldにEntityを追加したい。
たとえばボタンを押下した時にEntityを作るというようなことですね。とてもよくあるユースケースと思います。
最初やってたこと
先人たちの方法を倣って以下のようにしてました。
public void OnClickButton() {
// EntityManagerの取得
var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
// Entityを作成
var entity = entityManager.CreateEntity();
// Componentを追加
entityManager.SetName(entity, "SolderSpawnCommand");
entityManager.AddComponentData(entity, new SolderSpawnCommand {
MilitaryType = MilitaryType.archar,
Count = 10
});
}
これでも作成はされますが、SubSceneの外側に作成されてしまいます。
すぐに削除するような一時的なコンポーネントならそれでも問題はないのですが、一定時間存在し続ける場合に問題が発生します。
何が問題か
シーン遷移した際にも、SubSceneの外側に作成されたEntityは存在し続けます。
SubScene内のEntityであれば、シーンのライフサイクルと同期してくれるのか、シーン遷移時に合わせて削除されます(※)。
しかしSubSceneの外側に作成されたEntityは、シーン遷移しても残り続けるため、また同じシーンを読みだした時に副作用が発生してしまいます。
※ ただ、スクリプト上からSubSceneを作成した場合はシーン遷移時に手動で削除するようなことしないといけないのかもですが(未検証)
解決方法
どうも、SubSceneが作成される際、 というコンポーネントが作成されます(こちらのライフサイクルはSubSceneのロードとアンロードと同期してます)。SceneSectionData
そして、新たに作成する Entity に以下のComponentを追加することで、SubScene内のデータとして扱われるようになるようです。
- SceneSection
- SceneTag
(2/11 22時修正)
SubSceneで階層ウィンドウのチェックを入れてる場合はなぜかSceneSectionDataができないみたい。。なので代わりにSceneReferenceとSceneEntityReferenceを取得するようにします
なので、上記のコードを以下に書き換えます。
public void OnClickButton() {
// EntityManagerの取得
var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
// Entityを作成
var entity = entityManager.CreateEntity();
// SceneSectionDataの取得
// SubSceneが一つだけの場合はそれぞれSingletonとして取得できる
var sceneQuery = entityManager.CreateEntityQuery(typeof(SceneReference));
var entityQuery = entityManager.CreateEntityQuery(typeof(SceneEntityReference));
var entityReference = entityQuery.GetSingleton<SceneEntityReference>();
var sceneReference = sceneQuery.GetSingleton<SceneReference>();
// Componentを追加
entityManager.SetName(entity, "SolderSpawnCommand");
entityManager.AddComponentData(entity, new SolderSpawnCommand {
MilitaryType = MilitaryType.archar,
Count = 10
});
// 以下を追加
entityManager.AddSharedComponent(entity, new SceneSection() {
SceneGUID = sceneReference.SceneGUID
});
entityManager.AddSharedComponent(entity, new SceneTag() {
SceneEntity = entityReference.SceneEntity
});
}
これで実行することで、SubSceneの管理下におかれるようになります。