Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ECS初学者に向けてのTips集

Avatar for mao mao
October 22, 2018

 ECS初学者に向けてのTips集

Unity ECS完全に理解した
登壇資料

https://connpass.com/event/101774/

Avatar for mao

mao

October 22, 2018
Tweet

More Decks by mao

Other Decks in Technology

Transcript

  1. 趣味でECSに関するQiita記事を書いてます。 
 
 ・ 【Unity】ECSで弾幕STGを作ってみたので技術周りについて解説 
 ・ 【Unity】ECS + JobSystemでライフゲームを実装してみた

    
 ・ 【Unity】PureECSの描画周りについて解説してみる 
 ・ 【Unity】PureECSの衝突判定周りについて解説してみる 
 ・ 【Unity】ECS + JobSystemで10万個のドカベンロゴをアニメーションさせてみた 
 ※[Unity ECS]でググると何故か検索上位に出てくる。 
     但し最初に書いた記事の為に実装周りの情報が古いので注意。 
 
 その他にも 「OculusGoで大量のオブジェクトを動かしてみた」 と言うECS + JobSystemをフル活用して 
 OculusGoで ドカベンロゴ2万個 を60fps近くキープしつつ動かすと言うLTを話したりも。 

  2. ▼ 今回話す内容 
 「ECS触り初めの方」 or 「これから触る方」向けに 
 自分が検証できている範囲でのTipsを共有。 
 


    - 触ってみたいけど先ずは何を参考にすれば良いのか?
 - 表示ってどうやるの?
 - アニメーションって出来る?どうやるの?
 スライドは後ほど公開する予定。

  3. ・ 今回話す内容については PureECS を前提とした内容。 
 
 ・ 概念的な部分についてはそこまで触れない予定。 
 -

    最初の方におすすめ出来る資料のリンクを貼っているのでそちらを参照。
 
 ・ 全部趣味開発での検証内容。
   実務で導入してみた~的な内容では無い事を一応追記。
 ▼ 注意点

  4. ▼ 今回話す内容 
 ・ ▽ ECS入門/復習 
 - ▼ 初学者向け

    おすすめ資料リンク集 
 - ▼ 既存のサンプルプロジェクトを動かす前の注意点 
 - ▼ 用語の整理から 
 ・ ▽ どうやって描画するの? 
 ・ ▽ アニメーションってどうするの? 
 ・ ▽ 衝突判定ってどうするの? 
 ・ ▽ その他Tips 

  5. ▼ 初学者向け おすすめ資料リンク集 
 ・ 【GTMF2018TOKYO】ハードウェアの性能を活かす為の、Unityの新しい3つの機能 
 - https://www.slideshare.net/UnityTechnologiesJapan/gtmf2018tokyounity3 


    
 ・ 【Unity】Entity Component System入門(その1)【2018.2】 
 - https://qiita.com/pCYSl5EDgo/items/caac60ee41058cde085a 
 
 ・ Unity:はじめてのECS 
 - https://qiita.com/simplestar/items/17b0886be0170f79aa2e 
 
 ・ EntityComponentSystemSamples/Documentation 
 - https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/Documentation/index.md
 
 

  6. もう少し詳しく 
 ・ ECSはPreviewの為に破壊的変更が多い。 
 - パッケージのverを上げたら一部ComponentDataの霊圧が消えた。
 又は関数名が変わっていてコンパイルエラーが降り注いだ。
 (Unity.Mathematicsとか。後はUnity.Mathematicsとか)
 -

    古いパッケージが2018.2以降だと動かない。逆も然り。
 
 ある程度完全に理解できていれば自己解決可能かもしれないが、
 初学者には敷居が高いかもしれない。故にバージョン合わせておく事をおすすめ。
 後は「破壊的変更があった → 以降その実装は出来ないかもしれない」と言う点も抑えて おくこと。

  7. ▼ 用語の整理から 
 ・ World 
 - ComponentSystem(後述)の所属先
 - インスタンスはWorld1つにつき1つずつ存在する


    
 - 現状だと「Default World」と言うのが自動で生成される
 - 存在する全てのComponentSystemを登録する。 
  (この仕様のおかげで良くも悪くもWorld/Systemの登録を意識せずとも動かすことができる。) 
 - シンボルを定義することで自動生成を止めることも可能。 
 
 - 自分で作ることもできる。
 

  8. [Default World] 
 // ▽ ComponentSystem 
 
 // デフォルトで備わっているSystem

    
 L [TransformSytem] 
 L [MeshInstanceRendererSystem] 
 
 // 自前で実装したSystem 
 L [PlayerInputeSystem](プレイヤー入力) 
 L [GenerateSystem](Entityの生成周り) 
 
 
 [Original World] 
 // ▽ ComponentSystem 
 
 // 自前で実装したSystem 
 L [MyTransformSystem] 
 L [MyRendererSystem] 
 L [PlayerInputeSystem](プレイヤー入力) 
 L [GenerateSystem](Entityの生成周り) 
 
 
 ※上記のComponentSystemの並びは適当( 実行順ではない)。名前も厳密に言うと少し違う。 
 ※Defaultについては他にも登録されるComponentSystemが幾つかあるが↑のは例なので割愛。 
 ・デフォルトで用意されているComponentSystemはオレンジ色表記 
 ・自作のComponentSystemは水色表記。 

  9. ▼ 用語の整理から 
 ・ Component System 
 - Entity(後述)が持つComponentData(後述)を見て、
 動作に必要なものが揃っていたら処理を行う部分。


    
 - 例 : TransformSystem
 EntityがPosition, Rotation, Scaleと言うComponentDataの内、
 どれかを所持していたらLocalToWorldと言うモデル変換行列に値を纏める。
 逆に言うと上記3点全て所持していない場合にはモデル変換行列は生成されない。
 
 ※上記の緑色表記の物は全てデフォルトで定義されているComponentData

  10. 補足 : Entityの取得について 
 Entity (Player) 
 L Position/Rotation/Scale 


    L LocalToWorld
 L MeshInstanceRenderer 
 L PlayerData
 Entity (Enemy) 
 L Position
 L LocalToWorld
 L MeshInstanceRenderer 
 L EnemyData
 Entity (EnemyGenerator) 
 L EnemyGenerateData 
 TransformSystem 
 共通取得条件 : Position/Rotation/Scaleのどれかを持っていたら処 理
 →以下のEntityが処理される。 
 ・Entity(Player) : Position/Rotation/Scale
 ・Entity(Enemy) : Position

  11. 補足 : Entityの取得について 
 Entity (Player) 
 L Position/Rotation/Scale 


    L LocalToWorld
 L MeshInstanceRenderer 
 L PlayerData
 Entity (Enemy) 
 L Position
 L LocalToWorld
 L MeshInstanceRenderer 
 L EnemyData
 Entity (EnemyGenerator) 
 L EnemyGenerateData 
 TransformSystem 
 共通取得条件 : Position/Rotation/Scaleのどれかを持っていたら処理 
 →以下のEntityが処理される。 
 ・Entity(Player) : Position/Rotation/Scale
 ・Entity(Enemy) : Position
 RendererSystem 
 共通取得条件 : LocalToWorldとMeshInstRendererの両方を持っていたら 処理
 →以下のEntityが処理される。 
 ・Entity(Player) : LocalToWorld/MeshInstRenderer
 ・Entity(Enemy) : LocalToWorld/MeshInstRenderer

  12. 補足 : Entityの取得について 
 Entity (Player) 
 L Position/Rotation/Scale 


    L LocalToWorld
 L MeshInstanceRenderer 
 L PlayerData
 Entity (Enemy) 
 L Position
 L LocalToWorld
 L MeshInstanceRenderer 
 L EnemyData
 Entity (EnemyGenerator) 
 L EnemyGenerateData 
 TransformSystem 
 共通取得条件 : Position/Rotation/Scaleのどれかを持っていたら処理 
 →以下のEntityが処理される。 
 ・Entity(Player) : Position/Rotation/Scale
 ・Entity(Enemy) : Position
 RendererSystem 
 共通取得条件 : LocalToWorldとMeshInstRendererの両方を持っていたら 処理
 →以下のEntityが処理される。 
 ・Entity(Player) : LocalToWorld/MeshInstRenderer
 ・Entity(Enemy) : LocalToWorld/MeshInstRenderer
 EnemyGenerateSystem 
 Entity(Generator) の取得条件 : EnemyGenerateDataを持っていたら処 理
 Entity(Enemy) の取得条件 : EnemyDataを持っていたら処理。 
 →以下のEntityが処理される。 
 ・Entity(EnemyGenerator) : EnemyGenerateData
 ・Entity(Enemy) : EnemyData

  13. ▼ 用語の整理から 
 ・ Component Data 
 - ComponentSystemが求めるデータ。
 -

    MonoBehaviourのフィールドに相当(※)
 
 ・ Entity 
 - 実体。よくGameObjectに相当すると言われる。
 - 但しEntity自体はただのIndexでしか無く、
 フィールドに座標や体力と言った情報は一切持たない。

  14. ▼ 必要なComponentSystem 
 Worldに以下のComponentSystemを登録 
 ・ EndFrameTransformSystem 
 - デフォルトで用意されているTransform周りのComponentSystem


    - TrasnformSystemと言う物があるが、こちらは抽象クラス。
 ・ RenderingSystemBootstrap 
 - デフォルトで用意されている描画周りのComponentSystem
 - 正確に言うとMeshInstanceRendererSystemと言う物となるが、
 ↑を経由する事でCameraの設定などを良い感じにやってくれる。
 

  15. ・ EndFrameTransformSystem 
 - EntityがPosition/Rotation/Scaleと言うComponentDataを持っていたら、
 LocalToWorldと言うモデル変換行列に纏めてくれる。
 - ※それ以外にも親子構造などの機能もあるが割愛。 
 


    ・ RenderingSystemBootstrap 
 - EntityがMeshInstanceRenderer(後述)とLocalToWorldを持っていたら
 描画情報とモデル変換行列を元にDrawMesh/Instancedで描画してくれる。
 - 行列の参照の都合もあってか、EndFrameTransformSystemの後に実行される。
 
 ▼ 必要なComponentData 

  16. ・ MeshInstanceRendererSystemで
   描画されるEntityの描画情報。
  → Mesh, Materialなど
 
 ・ ISharedComponentDataを実装。


     → 簡単に説明すると共通情報向けの
    ComponentData。
    メモリ管理周りが少し違った覚え。
 
 ・ [Serializable]属性が付いているので
   Inspectorから値を設定可能。
 ※補足 : MeshInstanceRenderer 

  17. Entity 
 L Position 
 L Rotation 
 L Scale

    
 L LocalToWorld 
 L MeshInstanceRenderer 
 ▼ ComponentDataの設定 
 MeshInstRender : 配管工モデル
 Scale : float(1f, 0.1f, 1f)
 ※モデルイメージ

  18. Entity 
 L Position 
 L Rotation 
 L Scale

    
 L LocalToWorld 
 L MeshInstanceRenderer 
 Transform 
 System 
 ▼ ComponentDataの設定 
 MeshInstRender : 配管工モデル
 Scale : float(1f, 0.1f, 1f)
 →
 LocalToWorldにScaleの結果が反映される。 
 ※この時点では描画されていない
 ※モデルイメージ

  19. Entity 
 L Position 
 L Rotation 
 L Scale

    
 L LocalToWorld 
 L MeshInstanceRenderer 
 Transform 
 System 
 Renderer 
 System 
 ▼ ComponentDataの設定 
 MeshInstRender : 配管工モデル
 Scale : float(1f, 0.1f, 1f)
 →
 →
 LocalToWorldにScaleの結果が反映される。 
 ※この時点では描画されていない
 モデル変換行列と描画情報を元に
 DrawMesh***で描画を行う。
 ※モデルイメージ

  20. ▼ ECSの描画について 
 ・ 細かい点を意識せずに簡単に描画する事が可能。 
 
 ・ 頂点シェーダーで単純なループアニメーションを 


      再生するぐらいならこちらでも対応可能。 
 - e.g. BoidExampleの魚の動き、特定のロゴの回転処理など。
 - ※アニメーションについては後述
 
 ・ 求める結果によっては改造/自作を行うことで 
   高速化/高機能化を求めても良いかも。 
 → 座標計算周りはComputeSahderに投げる
 → 細かなアニメーション制御対応など

  21. ▼ アニメーションについて 
 まだ公式でサポートはされていない。 
 現時点に於いては細かく制御する必要があるなら 
 自作する必要がある。 
 


    ※前の章でも少し記載していたが、
 逆に細かな制御を行う必要のない単純なループアニメーションとかであれば
 頂点シェーダーで動かすだけと言った手はある。
 → 例として「Unity-Technologies/EntityComponentSystemSamples」にある
   BoidExampleの魚はそんな感じで動かしているように見受けられた。
 

  22. ▼ 仕組み
 ・ 今までシェーダー側で_Timeなどを見て動かしていた 
   キーフレーム更新の箇所をECS側から制御できるようにした。 
 
 ・

    右にある様な再生情報を持つ 
   ComponentDataを定義。 
 - Entity毎に再生情報を持てるように。
   

  23. Entity 
 L Position/Rotation/Scale 
 L LocalToWorld 
 L PlayData(キーフレームなど)

    
 Animation 
 System 
 全Entityが1万体居るとして、 
 歩行モーションの配管工が5000体居るとした場合 、
 先ずは歩行モーション5000体分のComputeBufferを生成して PlayData(キーフレームなど)を渡す。 
 →マテリアルは同じなのでDrawMeshInstanceIndirectで一括描画。 
 →
 ※モデルイメージ

  24. Entity 
 L Position/Rotation/Scale 
 L LocalToWorld 
 L PlayData(キーフレームなど)

    
 Animation 
 System 
 Result 
 全Entityが1万体居るとして、 
 歩行モーションの配管工が5000体居るとした場合 、
 先ずは歩行モーション5000体分のComputeBufferを生成して PlayData(キーフレームなど)を渡す。 
 →マテリアルは同じなのでDrawMeshInstanceIndirectで一括描画。 
 →
 →
 後は左の手順をアニメーションタイプ毎 に繰り返すだけ。
 ※モデルイメージ

  25. ▼ アニメーションについて 
 ・ かなり大雑把な説明になったが、 
   ざっとこんな感じで動かしてみた。 
 


    ・ 3D想定の説明ではあるが、 
   2Dゲームの場合はSpriteAnimation等で 
   対応は出来るかと思われる。 
 

  26. ▼ 衝突判定について 
 但し参考例ならある。 
 
 ・【CEDEC2018】CPUを使い切れ! Entity Component System(通称ECS)

    が切り開く新しいプログラミング 
 - https://www.slideshare.net/UnityTechnologiesJapan/cedec2018cpu-entity-component-systemecs
 - ※リンクは貼っていないが講演動画もある。
 
 上記資料の「16. コリジョンを作ってみた」(108ページ目~)にて解説されている。
 リポジトリの方も公開されているので参考にすることが可能。
 - https://github.com/Unity-Technologies/AnotherThreadECS
 
 

  27. ▼ 仕組み
 ・ やっている事は簡単に纏めると総当たりでしか無い。 
 1. Entityに球体形状の衝突プリミティブとなるComponentDataを持たせる 
 → 位置や半径の情報を持っているなど


    2. 衝突判定を行うComponentSystemを実装 
 → 中で衝突判定プリミティブを持つEntityを収集し判定。
 ※全てのEntityを一括りで取るのではなく、
 「プレイヤー」「敵」とEntityの種類ごとに分けて取得。(後述)
 
 ※単純に全てのEntityに対して総当たりをすると言う手もあるが、 
 先程例に出したCEDEC講演の方では空間を分割するアプローチで最適化を行っていた。 

  28. ▼ EntityDebuggerについて 
 ・ 主に以下の要素を参照可能 
 - 各Entityに紐付いているComponentDataの内容 
 -

    各アーキタイプのチャンク使用量 
 - World及び登録されているComponentSystem 
 - ComponentSystemの実行順/処理負荷 
 
 ・ 最初は1~数個のEntityを作って動作確認すると 
   追いやすいのでおすすめ。 

  29. ▼ MonoBehaviourとのデータのやり取り 
 ・ 簡単に考えられる手法としてはstatic経由 
 → 但しBurstCompilerはstatic参照を許可しない ので、
  

      Burstが有効なJobに渡す際には工夫する必要がある。 
   例 : ポインタにして渡すとか。 
 
 ・ 初期化時のみだがComponentSystemの 
   コンストラクタで渡す手もある。(後述) 

  30. ▼ ComponentSystemの登録 
 ComponentSystem自体はclass。 
 CreateManagerのタイミングで中に必要なデータや参照なりを渡しておくことで、 
 staticを経由せずとも 
 MonoBehaviour

    ⇔ ComponentSystemのデータの受け渡しが可能。 
 
 CreateManagerを用いた明示的な初期化タイミングを設けておらず、
 GetOrCreateManagerで必要に応じて生成/取得する運用とかでなければ
 覚えておいても損はないかもしれない。

  31. ▼ PrefabとDisabled 
 「Prefab ComponentData」「Disabled ComponentData」 と言う
 便利なComponentDataがあるので軽く紹介。 
 ※内容自体はQiitaにも記載している。


    
 ・ 【Unity】ECSの「Disabled ComponentData」と「Prefab ComponentData」について 解説してみる 
 - https://qiita.com/mao_/items/89306846075f65ce6b50 
 

  32. ▼ Disabled ComponentData 
 要約するとEntityに紐付けることで以下の機能を付与可能。 
 - Entityが Disabled を持っている間は全てのComponentDataで動作しない。

    
 - RemoveComponentDataでDisabledを外すと再び起動する。 
 
 出来ることを簡単に纏めると、ComponentDataの有無で 
 GameObjectのSetActiveに近い形の運用が可能かと思われる。 
 
 後はDisabled を付けている最中でも 他のComponentDataの値自体は 
 保持されたままとなるので、 Entityの一時停止/再開などにも使えるかと思われる。 

  33. ▼ Prefab ComponentData 
 要約するとEntityに紐付けることで以下の機能を付与可能。 
 - Entityが Prefab を持っている間は全てのComponentDataで動作しない。

    
 - RemoveComponentDataで Prefab を外すと再び起動する。 
 - Prefab を持つEntityを元にInstanciateして生成したEntityは 
 Prefabが外れた状態で複製される。 
 
 特徴としては複製時の挙動。 
 その為に予め共通の初期設定を済ませておいたEntityをPrefabとして生成し、 
 後は必要なタイミングで雛形を元に生成すると言った使いみちが考えられる。