ECSわかった系のスライドを読んでもよくわからなかったので、これはもう実際書いて覚えるしかないと思ったので書いてみました。
今回はGameObjectにつけた自作スクリプトをECSから操作するいわゆる HybridECS をやります。
環境:Unity2019.1.7
ECS preview.33 - 0.0.12
ECSを使えるようにプロジェクト設定
.Net4 を使う設定
メニューから File > Build Settings と選択し、ダイアログで Player Settings をクリック。
Other Settings 欄の Scripting Runtime Version で .Net 4.x Equivalent を選択する。
リスタートのダイアログが出てくるので Restart をクリック。
Entitiesパッケージのインストール
メニューから Windows > Package Manager で パッケージマネージャ を表示し、
- Show preview packages を有効に
- Entities を選択
- Install !
Entityパッケージがインストールされていれば Packages/manifest.json に以下の行が追加されています。
パッケージマネージャからではなく、直接1行追加してもおk。
{ "dependencies": { ... "com.unity.entities": "0.0.12-preview.33" } }
自分の環境だとこのままだとエラーが出ていたので、
Assembly has reference to non-existent assembly 'Unity.PerformanceTesting' (Packages/com.unity.entities/Unity.Entities.PerformanceTests/Unity.Entities.PerformanceTests.asmdef)
Packages/manifest.json の末尾に以下を追加してエラー回避できました。
{ "dependencies": { ... "com.unity.test-framework.performance": "0.1.49-preview" }, "registry": "https://packages.unity.com", "testables": [ "com.unity.entities", "com.unity.test-framework.performance" ]
ECSを使って実装してみる
今回はHybridECSですがECSだけで構成する Pure ECS というものもあるが、とりあえず今は使う予定がない。
Component スクリプトを作成
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ECSTestComponent : MonoBehaviour { public float speed; }
インスペクタでオブジェクトごとにspeedに違う値を設定すると、違う動きにできます。
このスクリプトをふつうに GameObjectにつけます。 また、 Game Object Entity コンポーネントも同時にアタッチする。
ComponentSystemスクリプトを作成
スクリプトを新規で作成し、
- まず using Unity.Entities; を先頭に追加。
- ComponentSystem クラスを継承させる。
- OnUpdate をオーバーライドしたり、操作したいコンポーネントをメンバに持つ構造体を定義したりする。
using System.Collections; using System.Collections.Generic; using UnityEngine; using Unity.Entities; public class ECSTestSystem : ComponentSystem { struct TestComponents { public ECSTestComponent target; } protected override void OnUpdate() { foreach(var e in GetEntities<TestComponents>()) { // オブジェクトごとに回転させる e.target.transform.Rotate( 0, e.target.speed * Time.deltaTime, 0); } } }
これはオブジェクトにアタッチさせる必要はありません。
Worldを自作する
ECSは World という単位でシステムを分けることができ、デフォルトでWorld作成されるが Editor上でも有効 になってしまい、TestRunner でシーンをロードする類のテストを実行したときに以下のようなエラーが吐かれた。
InvalidOperationException: Unity.Entities.SimulationSystemGroup has already been destroyed. It may not be used anymore. Unity.Entities.ComponentSystemBase.CheckExists () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:334) Unity.Entities.ComponentSystemBase.ShouldRunSystem () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:352) Unity.Entities.ComponentSystem.InternalUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:801) Unity.Entities.ComponentSystemBase.Update () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:287) Unity.Entities.ScriptBehaviourUpdateOrder+DummyDelegateWrapper.TriggerUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ScriptBehaviourUpdateOrder.cs:135)
InvalidOperationException: Unity.Entities.PresentationSystemGroup has already been destroyed. It may not be used anymore. Unity.Entities.ComponentSystemBase.CheckExists () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:334) Unity.Entities.ComponentSystemBase.ShouldRunSystem () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:352) Unity.Entities.ComponentSystem.InternalUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:801) Unity.Entities.ComponentSystemBase.Update () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentSystem.cs:287) Unity.Entities.ScriptBehaviourUpdateOrder+DummyDelegateWrapper.TriggerUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ScriptBehaviourUpdateOrder.cs:135)
のでデフォルトのWorldは無効にして自分で作成するのがいいと思います。
Project Settings の Player の Scripting Define Symbols に以下を定義して、デフォルトで作成されないようにする。
UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP;
World はスクリプトで以下のように作成します。
static World MyWorld; static PresentationSystemGroup mySystemGroup; static ECSTestSystem testSystem; public static void CreateWorld() { if (MyWorld == null) { MyWorld = World.Active = new World("MyWorld"); mySystemGroup = MyWorld.GetOrCreateSystem<PresentationSystemGroup>(); testSystem = MyWorld.GetOrCreateSystem<ECSTestSystem>(); mySystemGroup.AddSystemToUpdateList(testSystem); ScriptBehaviourUpdateOrder.UpdatePlayerLoop(MyWorld); } }
PresentationSystemGroup#AddSystemToUpdateList でComponentSystemを登録することでOnUpdateが呼ばれるようになるので忘れずに書きましょう。
不要になったWorldは削除します。
ScriptBehaviourUpdateOrder.UpdatePlayerLoop(null); MyWorld.Dispose(); MyWorld = null; World.Active = null;
ECSの登録を確認する
メニューから Window > Analysis > Entity Debugger とクリックすると、ECSの状態がわかります。
使い方はまだよくわからん。
ゲームを実行する
ひとつの ComponentSystem スクリプトからまとめて複数のオブジェクトを回転させている。
・参考
公式GitHubのECSサンプルとかもあります。