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

ECS初学者に向けてのTips集

08a7d188d046f35b2501e686616f71bd?s=47 mao
October 22, 2018

 ECS初学者に向けてのTips集

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

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

08a7d188d046f35b2501e686616f71bd?s=128

mao

October 22, 2018
Tweet

Transcript

  1. Unity ECS完全に理解した (10/22)

  2. 自己紹介 株式会社ブラストエッジゲームズ 所属 mao ( @TEST_H_ ) ・ ゲームプログラマー

  3. 趣味で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を話したりも。
  4. ▼ 今回話す内容 「ECS触り初めの方」 or 「これから触る方」向けに 自分が検証できている範囲でのTipsを共有。 - 触ってみたいけど先ずは何を参考にすれば良いのか? - 表示ってどうやるの?

    - アニメーションって出来る?どうやるの? スライドは後ほど公開する予定。
  5. ・ 今回話す内容についてはPureECSを前提とした内容。 ・ 概念的な部分についてはそこまで触れない予定。 - 最初の方におすすめ出来る資料のリンクを貼っているのでそちらを参照。 ・ 全部趣味開発での検証内容。   実務で導入してみた~的な内容では無い事を一応追記。

    ▼ 注意点
  6. ▼ 今回話す内容 ・ ▽ ECS入門/復習 - ▼ 初学者向け おすすめ資料リンク集 -

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

  8. ▼ 初学者向け おすすめ資料リンク集 ・ 【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
  9. 既存のサンプルプロジェクトを動かす前の注意点

  10. ▼ 既存のサンプルプロジェクトを動かす前の注意点 ECSには公式のサンプル/公開プロジェクトや、 技術解説記事に伴うサンプル等が存在するかと思われますが、 動かす際には注意点があります。 結論から言うと動かすだけならUnityのバージョンと entitiesパッケージのバージョンは合わせておくことが無難です。 ※entitiesパッケージ : ECSのパッケージ。PackageManagerから取得可能。

  11. もう少し詳しく ・ ECSはPreviewの為に破壊的変更が多い。 - パッケージのverを上げたら一部ComponentDataの霊圧が消えた。 又は関数名が変わっていてコンパイルエラーが降り注いだ。 (Unity.Mathematicsとか。後はUnity.Mathematicsとか) - 古いパッケージが2018.2以降だと動かない。逆も然り。 ある程度完全に理解できていれば自己解決可能かもしれないが、

    初学者には敷居が高いかもしれない。故にバージョン合わせておく事をおすすめ。 後は「破壊的変更があった → 以降その実装は出来ないかもしれない」と言う点も抑えて おくこと。
  12. 用語の整理から

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

    World」と言うのが自動で生成される - 存在する全てのComponentSystemを登録する。  (この仕様のおかげで良くも悪くもWorld/Systemの登録を意識せずとも動かすことができる。) - シンボルを定義することで自動生成を止めることも可能。 - 自分で作ることもできる。
  14. [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は水色表記。
  15. ▼ 用語の整理から ・ Component System - Entity(後述)が持つComponentData(後述)を見て、 動作に必要なものが揃っていたら処理を行う部分。 - 例

    : TransformSystem EntityがPosition, Rotation, Scaleと言うComponentDataの内、 どれかを所持していたらLocalToWorldと言うモデル変換行列に値を纏める。 逆に言うと上記3点全て所持していない場合にはモデル変換行列は生成されない。 ※上記の緑色表記の物は全てデフォルトで定義されているComponentData
  16. 補足 : 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
  17. 補足 : 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
  18. 補足 : 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
  19. ▼ 用語の整理から ・ Component Data - ComponentSystemが求めるデータ。 - MonoBehaviourのフィールドに相当(※) ・

    Entity - 実体。よくGameObjectに相当すると言われる。 - 但しEntity自体はただのIndexでしか無く、 フィールドに座標や体力と言った情報は一切持たない。
  20. 座標情報とスコア情報を持つ「プレイヤー」の生成例 → 左がECSでの生成、右がGameObjectの生成例 「Entity」は「Entity」であり、 「PlayerEntity」と言った何かを継承したクラス定義などの必要は無い。

  21. どうやって描画するの?

  22. ▼ ECSの描画について デフォルトで描画をする為の仕組みは用意されている。 → MeshInstanceRendererSystem 以下のQiita記事にも纏めている内容をざっくりと解説。 【Unity】PureECSの描画周りについて解説してみる - https://qiita.com/mao_/items/c4a217b33a0a94d5d52f

  23. ▼ 必要なComponentSystem Worldに以下のComponentSystemを登録 ・ EndFrameTransformSystem - デフォルトで用意されているTransform周りのComponentSystem - TrasnformSystemと言う物があるが、こちらは抽象クラス。 ・

    RenderingSystemBootstrap - デフォルトで用意されている描画周りのComponentSystem - 正確に言うとMeshInstanceRendererSystemと言う物となるが、 ↑を経由する事でCameraの設定などを良い感じにやってくれる。
  24. ・ EndFrameTransformSystem - EntityがPosition/Rotation/Scaleと言うComponentDataを持っていたら、 LocalToWorldと言うモデル変換行列に纏めてくれる。 - ※それ以外にも親子構造などの機能もあるが割愛。 ・ RenderingSystemBootstrap -

    EntityがMeshInstanceRenderer(後述)とLocalToWorldを持っていたら 描画情報とモデル変換行列を元にDrawMesh/Instancedで描画してくれる。 - 行列の参照の都合もあってか、EndFrameTransformSystemの後に実行される。 ▼ 必要なComponentData
  25. ※補足 : Position/Rotation/Scale 左がECSのComponentData。右はおなじみのやつ ⇔

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

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

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

    MeshInstanceRenderer Transform System ▼ ComponentDataの設定 MeshInstRender : 配管工モデル Scale : float(1f, 0.1f, 1f) → LocalToWorldにScaleの結果が反映される。 ※この時点では描画されていない ※モデルイメージ
  29. 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***で描画を行う。 ※モデルイメージ
  30. ▼ ECSの描画について ・ 細かい点を意識せずに簡単に描画する事が可能。 ・ 頂点シェーダーで単純なループアニメーションを   再生するぐらいならこちらでも対応可能。 - e.g.

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

  32. デフォルトで用意されているアニメーションについて

  33. ▼ アニメーションについて まだ公式でサポートはされていない。 現時点に於いては細かく制御する必要があるなら 自作する必要がある。 ※前の章でも少し記載していたが、 逆に細かな制御を行う必要のない単純なループアニメーションとかであれば 頂点シェーダーで動かすだけと言った手はある。 → 例として「Unity-Technologies/EntityComponentSystemSamples」にある

      BoidExampleの魚はそんな感じで動かしているように見受けられた。
  34. ▼ アニメーションについて と言う事で作ってみた。これについて軽く解説。 ・ mao-test-h/ECS-AnimationSample - https://github.com/mao-test-h/ECS-AnimationSample

  35. ▼ 仕組み 基本的には頂点シェーダーで動かしている。 AnimationClipの頂点の動き(位置、法線)をTextureにベイク。 頂点シェーダーで上記のテクスチャを参照して動かすアプローチ。 ※ちなみに[Unity-Technologies/UniteAustinTechnicalPresentation] と言う  公式のデモプロジェクトの方も似たような形でアニメーションを制御してました。 - https://github.com/Unity-Technologies/UniteAustinTechnicalPresentation

    参考 : 【Unity】ECSを使用した大規模なデモプロジェクト "Unite Austin Technical Presentation" - http://tsubakit1.hateblo.jp/entry/2018/04/27/023856
  36. ▼ 仕組み サンプルでは[sugi-cho/Animation-Texture-Baker]を 使わせて頂きました。 - https://github.com/sugi-cho/Animation-Texture-Baker こちらについてはテラシュールブログさんの 以下の記事でも解説されてます。 ・【Unity】シェーダーで3Dモデルのアニメーションを行う Animation

    Texture Baker - http://tsubakit1.hateblo.jp/entry/2017/09/03/225713
  37. ▼ 仕組み 左の画像の赤丸で囲んでいる箇所が ベイクした頂点情報。 ※今回はアニメーションごとにマテリアルを作成し、 Entityが持つアニメーションタイプ(Idle, Walk, Run)に応じて該当するマテリアルを参照して、 DrawMeshInstanceIndirectで一括で描いている。(詳細は後述)

  38. ▼ 仕組み ・ 今までシェーダー側で_Timeなどを見て動かしていた   キーフレーム更新の箇所をECS側から制御できるようにした。 ・ 右にある様な再生情報を持つ   ComponentDataを定義。

    - Entity毎に再生情報を持てるように。  
  39. ▼ 仕組み 再生されているAnimationType毎にComputeBufferを生成し、 ECS側で更新した再生情報を設定。 DrawMeshInstancedIndirectでAnimationType毎に一括描画。

  40. ▼ 仕組み 頂点シェーダーにて”instanceId : SV_InstanceID”を用いて 渡ってきたBufferを参照し、 キーフレームに応じたTextureの位置をフェッチして座標を更新。

  41. Entity L Position/Rotation/Scale L LocalToWorld L PlayData(キーフレームなど) ※モデルイメージ

  42. Entity L Position/Rotation/Scale L LocalToWorld L PlayData(キーフレームなど) Animation System 全Entityが1万体居るとして、

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

    全Entityが1万体居るとして、 歩行モーションの配管工が5000体居るとした場合、 先ずは歩行モーション5000体分のComputeBufferを生成して PlayData(キーフレームなど)を渡す。 →マテリアルは同じなのでDrawMeshInstanceIndirectで一括描画。 → → 後は左の手順をアニメーションタイプ毎 に繰り返すだけ。 ※モデルイメージ
  44. ▼ アニメーションについて ・ かなり大雑把な説明になったが、   ざっとこんな感じで動かしてみた。 ・ 3D想定の説明ではあるが、   2Dゲームの場合はSpriteAnimation等で

      対応は出来るかと思われる。
  45. 衝突判定ってどうするの

  46. デフォルトで用意されている衝突判定に(ry

  47. ▼ 衝突判定について まだ公式ではサポートされていない。 故に自作する必要がある。 ※近々搭載されるとの噂はあったりする。

  48. ▼ 衝突判定について 但し参考例ならある。 ・【CEDEC2018】CPUを使い切れ! Entity Component System(通称ECS) が切り開く新しいプログラミング - https://www.slideshare.net/UnityTechnologiesJapan/cedec2018cpu-entity-component-systemecs

    - ※リンクは貼っていないが講演動画もある。 上記資料の「16. コリジョンを作ってみた」(108ページ目~)にて解説されている。 リポジトリの方も公開されているので参考にすることが可能。 - https://github.com/Unity-Technologies/AnotherThreadECS
  49. ▼ 衝突判定について 自分の方でもCEDECの講演資料/リポジトリを参考にしつつ、 Qiita記事に衝突判定について纏めてみた。 詳細についてはこちらを参照。 ※衝突プリミティブの形状としては球体形状を前提。 【Unity】PureECSの衝突判定周りについて解説してみる - https://qiita.com/mao_/items/ad8a09a9c6403a2e84cf

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

    → 中で衝突判定プリミティブを持つEntityを収集し判定。 ※全てのEntityを一括りで取るのではなく、 「プレイヤー」「敵」とEntityの種類ごとに分けて取得。(後述) ※単純に全てのEntityに対して総当たりをすると言う手もあるが、 先程例に出したCEDEC講演の方では空間を分割するアプローチで最適化を行っていた。
  51. ※ Entityの取得について 例えば汎用的なComponentDataとして「Position」と言った TransformSystem周りで使うデータなどが挙げられるが、 ComponentSystemにてEntityを取得する際にPositionだけが検索条件だと 「プレイヤー」「敵」「アイテム」とPositionを持ち得るEntityを全て集めかねない。 ※極端な例ではあるが... 衝突判定に絡んだ話ではないが、 小テクとして仕分ける方法について軽く共有。

  52. ※ Entityの取得について 手法の1つとして、 識別用の空のComponentDataを Entityに持たせる手がある。

  53. ※ Entityの取得について 後はComponentSystem内で Entityを取得する際の検索条件に 定義した空の識別ComponentDataを 使うことで識別可能。 ※内部的にも空のComponentDataで 識別する手法は結構使われていたりす る。

  54. ▼ 衝突判定について Qiita記事に纏めたサンプルの方では空間分割しなくとも 意外とパフォーマンスは出た。 (寧ろ微々たる差ではあるが総当たりの方が早かった。) こちらは内容によって変わるかもしれないので 一概に「総当たりのほうが早い」とは言えない。 要検証項目。

  55. その他Tips集

  56. ▼ EntityDebuggerについて ・ entitiesパッケージを入れるとついてくる   ECS向けのデバッグツール。

  57. ▼ EntityDebuggerについて ・ 主に以下の要素を参照可能 - 各Entityに紐付いているComponentDataの内容 - 各アーキタイプのチャンク使用量 - World及び登録されているComponentSystem

    - ComponentSystemの実行順/処理負荷 ・ 最初は1~数個のEntityを作って動作確認すると   追いやすいのでおすすめ。
  58. ▼ MonoBehaviourとのデータのやり取り ・ 簡単に考えられる手法としてはstatic経由 → 但しBurstCompilerはstatic参照を許可しないので、     Burstが有効なJobに渡す際には工夫する必要がある。  

    例 : ポインタにして渡すとか。 ・ 初期化時のみだがComponentSystemの   コンストラクタで渡す手もある。(後述)
  59. ▼ ComponentSystemの登録 自前でWorldを作ってComponentSystemを登録する際に World.Active.CreateManagerを経由することで ComponentSystemのコンストラクタに引数を渡すことが可能。 → 内部的には”Activator.CreateInstance”が呼ばれている。

  60. ▼ ComponentSystemの登録 ComponentSystem自体はclass。 CreateManagerのタイミングで中に必要なデータや参照なりを渡しておくことで、 staticを経由せずとも MonoBehaviour ⇔ ComponentSystemのデータの受け渡しが可能。 CreateManagerを用いた明示的な初期化タイミングを設けておらず、 GetOrCreateManagerで必要に応じて生成/取得する運用とかでなければ

    覚えておいても損はないかもしれない。
  61. ▼ PrefabとDisabled 「Prefab ComponentData」「Disabled ComponentData」と言う 便利なComponentDataがあるので軽く紹介。 ※内容自体はQiitaにも記載している。 ・ 【Unity】ECSの「Disabled ComponentData」と「Prefab

    ComponentData」について解説し てみる - https://qiita.com/mao_/items/89306846075f65ce6b50
  62. ▼ Disabled ComponentData 要約するとEntityに紐付けることで以下の機能を付与可能。 - EntityがDisabledを持っている間は全てのComponentDataで動作しない。 - RemoveComponentDataでDisabledを外すと再び起動する。 出来ることを簡単に纏めると、ComponentDataの有無で GameObjectのSetActiveに近い形の運用が可能かと思われる。

    後はDisabledを付けている最中でも他のComponentDataの値自体は 保持されたままとなるので、Entityの一時停止/再開などにも使えるかと思われる。
  63. ▼ Prefab ComponentData 要約するとEntityに紐付けることで以下の機能を付与可能。 - EntityがPrefabを持っている間は全てのComponentDataで動作しない。 - RemoveComponentDataでPrefabを外すと再び起動する。 - Prefabを持つEntityを元にInstanciateして生成したEntityは

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