テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】ECSを使用した大規模なデモプロジェクト "Unite Austin Technical Presentation"

f:id:tsubaki_t1:20180427001949j:plain

簡単なサンプルではなく、ECSを大規模に使用した技術デモ「Unite Austin Technical Presentation」の紹介です。

 

 

Unite Austin Technical Presentation

Unite Austin Technical Presentationは、Nordeus社が作成したゲームの一部プレビューです。内容は大量のミリオン同士の戦い…という感じです。

実際、数万体*1のミリオンがワールドの中央付近で戦闘を繰り広げています。

f:id:tsubaki_t1:20180427010109g:plain

また1キーや2キーでワールドの中身に介入することも出来ます。
例えばマウスの位置に隕石?を落下する攻撃を打ち込むと、大量のミリオンが一斉に吹き飛ぶのが確認出来ます。

プロジェクトは以下のGithubから入手出来ます。サイズがちょっと大きくて、3.3GBくらいあるので、モバイルWifiから落とす場合には注意が必要です。帯域制限を喰らいます(体験談

github.com

 

ユニットの動作や攻撃にはECSが使用されている

このゲームですが、キャラクターの移動や攻撃、キャラクター表現等、多くの部分にECSが使用されています。

キャラクター毎にEntityが割り当てられ、キャラクターの一体一体が各々で動いています。ミリオンだけでなく、弓の攻撃も一本一本がEntityです。

f:id:tsubaki_t1:20180427012516j:plain

ステージとの連携は、ふっ飛ばされた時などの物理挙動にはRaycastCommandで地面との相対距離等が求められていますが、移動はもっぱらNavMeshQueryでパスを取得してパスの通りに移動するというものみたいです。

 

逆に、判定やレンダリングなど、ユニットの動作に関する部分は多くの部分が自作コードになっていました。これは単純に用意されてないというのもありそうですが、幾つかは特別な最適化を施したいというのも理由の一つかもしれません。
例えば以下のような感じ

  • 当たり判定(判定の精度を下げたい)
  • レンダリング(DrawMeshIndirectで一括表示したい)
  • キャラクターのAI

f:id:tsubaki_t1:20180427010640j:plain

これらの処理は大まかな部分をSystemはJobSystemに移譲しており、ワーカースレッドはソレナリの範囲を埋めています。

特に長いのはMoveJobで、中でNavMeshQueryでパス計算とかしてます。

f:id:tsubaki_t1:20180427011010j:plain

 

キャラクターアニメーションは頂点シェーダー

さて、このプロジェクトは数万体という大量のユニットを同時に表現するためにDrawMeshIndirectでキャラクターを描画しています。
とは言え、DrawMeshIndirectで描画するものは基本的にアニメーションを使用できません。その辺り、このプロジェクトでは、頂点シェーダーでポリゴンを動かすことで実現しています。

f:id:tsubaki_t1:20180427015251g:plain

基本的には Animation Texture Baker と同じ系統の技術のようですが、テクスチャをベイクするというよりはバッファを云々してるように見えます。(ちゃんとコードを読んでいない)。このバッファを塗るのに、UnsafeUtilityもガンガン使うという中々に面白くないコードになっています。

 

 なお、何故かTextureAnimationSystemはComponentフォルダ以下にあります。多分ミスです。Systemフォルダに無かったので焦りました。

f:id:tsubaki_t1:20180427020546j:plain

 

状態の切り替えはComponentDataの追加で

ユニットには複数の状態が設定されています。例えば「移動中」とか「攻撃中」とか、あとは「死亡中」とか。

この状態の切替は、ComponentDataの抜き差しで行っていました。

f:id:tsubaki_t1:20180427025041p:plain

ComponentDataの抜き差し(AddComponent/RemoveComponent)は、ComponentDataが所属するチャンクを移動する事になるので余り効率的では無いように思ってたんですが、各動作毎に要求するデータが大きく違うので、その辺りを嫌がったのかもしれません。

まあチャンク間の移動といっても、数箇所の要素を塗り直すだけですし(新しくチャンクが作られなければ)案外問題は無いのかもしれません。

f:id:tsubaki_t1:20180427030732j:plain

 

ユニット情報はPrefabから取得

ECSを使用しているので殆どがコードで書かれるように思っていましたが、ユニット情報等はECSハイブリットの構造を使用して取得していました。
Prefab(GameObject)をInstantiateするのではなく、PrefabからEntityを作ってEntityをInstantiateする感じです。

これでコードを弄る事なく、複数パターンのEnittyを作成しています。

f:id:tsubaki_t1:20180427022246j:plain

このコードを見てEntityManagerのInstantiateにGameObject割り当てられる事に気づきました。

確かにこの使い方は理にかなってるように感じます。まぁ最終的にはScriptableObjectかEntityをシリアライズしたものに纏めてほしい気もします。

 

ユニットの生成位置はMonobehaviourから取得

ハイブリットと言えば、ユニットの生成位置等の情報はMonoBehaviourを継承したコンポーネントから取得していました。
出現位置などの情報はGameObjectでシーン上に配置(Gizmoでプレビュー)し、実際にUnitを生成するのはComponentSystemで行っている感じです。

f:id:tsubaki_t1:20180427032356j:plain

 

関連

このプロジェクトの紹介です

www.youtube.com

ECSとGameObjectのハイブリット

tsubakit1.hateblo.jpECSについて

tsubakit1.hateblo.jp

このプロジェクトのフォーラム

https://forum.unity.com/threads/unite-austin-technical-presentation.522628/

*1:ゲーム開始時は1万くらい、放置すると2万3万と増えていく