Notifications
Article
ECS + JobSystem で BOID 実装した話
Updated a month ago
347
0
本記事は、Unity #3 Advent Calendar 2018 15日目の記事です。
Unity #3 Advent Calendar 2018
ECS を勉強するとなにやら「大量のモノを高速に動かすことができるらしい」
というあやしい理解からはじまり, 「ECS で BOIDシミュレーションをやってみたい!」と思い立って,学んだ過程と実装したことを簡単に紹介したいとおもいます。

BOIDとは

ボイド(人工生命)@Wikipedia
BOID は,鳥の群体をシミュレーションするプログラムのことで,大量のムクドリが夕焼けの空を埋め尽くすときの群れとしての挙動や,カモが数羽整列して飛ぶといった挙動を簡単な計算にもかかわらず自然な動きをつくることができます。

公式のECSのサンプルにも魚群のシミュレートをするサンプルがあります。
EntityComponentSystemSamples
(この公式サンプルは他者との位置関係の評価を個別に行わずに,群全体の指揮者を置く形で,それなりの動きを実現しています。ゲームに組み込むとなると,このごまかし方はとても良い設計で感動ものです!)

1. ECS とはなにか

ECS という言葉をよく聞くようになり,気になったので調べました。幸い日本語の解説記事がたくさん見つかります。 詳しい解説はまだ理解が怪しいので丸投げします(笑)
【Unity】Unity 2018のEntity Component System(通称ECS)について(1)
【Unity】ECS + JobSystemで10万個のドカベンロゴをアニメーションさせてみた

2. データをモデリングする

BOID を実現するのに必要なことを ECS の文脈で振る舞いを決めてモデリングしてみます。

個々の鳥の飛行

  • 座標 (Position)
  • 向き (Rotation)
  • 速度 (AgentData)

個々の鳥の表示

  • 大きさ (Scale)
  • レンダラ (MeshInstanceRenderer)
このようなデータを想定して,具体的には以下のような Archetype を作りました。
// create ArchType entityArchetype = entityManager.CreateArchetype( typeof(Position), typeof(Rotation), typeof(AgentData), typeof(Scale), typeof(MeshInstanceRenderer), typeof(SharedAgentData));
最後の行の SharedAgentData は次で解説します。

群全体に共通したパラメータ (SharedAgentData)

BOIDシミュレーションを行うためには,様々な定数が必要となるので,これらを ISharedComponentData として定義します。これらは,個々の鳥毎に定義する必要は無く,全体で共通したものを使います。
[Serializable] public struct SharedAgentData : ISharedComponentData { public SimulationTime SimulationTime; public float2 VelocityRange; public float3 Prey; public float CohesionFactor; public float SeparationFactor; public float AlignmentFactor; public float PreyFactor; public float MaxCohesionDistance; public float MaxAlignmentDistance; public int MaxNeighborCount; }
  • シミュレーション時間の進め方を決める定数 (SimulationTime)
  • 個体の最小,最大速度 (VelocityRange)
  • 移動目標(餌)の位置 (Prey)
  • 群れの中心に集まる強さ (CohesionFactor)
  • 他の個体と近づきすぎないように距離をとる強さ (SeparationFactor)
  • 他の個体と同じ向きに飛ぼうとする強さ (AlignmentFator)
  • 各個体の目の良さ
  • 群れの中心を認識する最大距離 (MaxCohesionDistance)
  • 群れの移動する向きを認識する最大距離 (MaxAlignmentDistance)
  • 最大何体の他個体を評価対象にするか (MaxNeighborCount)

3. モデリングの手順まとめ

  • ECS は「データ指向設計」と説明されていたので,まず実現したい個体(Entity)の振る舞いからデータを割り出すモデリング作業を最初にするのが良さそう。
  • 全体で共通のデータは ISharedComponentData を継承して定義する。

4. 並列計算させる

個々の鳥の飛行処理は JobSystem を使って並列処理を行うことでパフォーマンスの向上が期待できます。 ECS には,JobComponentSystem という JobSystem が組み込まれた基底クラスが用意されているのですが,これをそのまま使った場合,分割された Job 内では ComponentData 配列はスライシングされて他の個体の位置や速度を見ることができません。BOID のシミュレーションでは,ある個体の目から見た場合,その他の個体すべてに対して状態を見る可能性があるため問題となります。その対策として一工夫必要だったため紹介します。
(※ BOIDは真面目に計算すると O(n^2) の計算量なので ECS + JobSystem といえども重たい処理となります)
ComponentData の配列はコード上扱えるように見えるのですが,いざ中身を見ようとすると以下のように IndexOutOfRangeException が出ます。JobSystem では Job のスケジュール時に指定する ArrayLength 毎にデータ列は分割されてしまうようです。
IndexOutOfRangeException: Index 747 is out of restricted IJobParallelFor range [600...649] in ReadWriteBuffer.ReadWriteBuffers are restricted to only read & write the element at the job index. You can use double buffering strategies to avoid race conditions due to reading & writing in parallel to the same elements from a job.

配列全体にアクセスする

各ジョブから ComponentDataArray 全体にアクセスするには NativeDisableParallelForRestriction Attribute を使います。この属性が指定された ComponentDataArray は上記の JobIndex 毎に課せられるアクセス制限の対象から外されます。
internal struct AgentJob : IJobParallelFor { [NativeDisableParallelForRestriction] private ComponentDataArray<Position> positions; private ComponentDataArray<Rotation> rotations; [NativeDisableParallelForRestriction] private ComponentDataArray<AgentData> agents; ... ... public void Update(AgentCollection agentCollection, SharedAgentData sharedAgentData) { positions = agentCollection.Positions; rotations = agentCollection.Potations; agentData = sharedAgentData; } ...
あとは,ComponentSystem の OnUpdate 毎に Jobのインスタンスに対して Update を呼んであげることでステップ毎に全ての他Agent の状態を見ることができるようになりました。
ただし,よく調べてはいませんが,ランダムアクセスが頻発することになるのでパフォーマンス面でデメリットがありそうな気がします。他に方法がわからなかったので今回はこの方針で進めました。

5. コードの置き場所

https://github.com/mewlist/Boid
今回実装した BOID シミュレーションのコードは上記レポジトリで公開しております。

6. 参考にした本

作って動かすALife――実装を通した人工生命モデル理論入門

7. 最後に

今回は,BOIDをECS+JobSystem で実装する際にわかったモデリングの方針と,他Entityの影響し合うケースで必要な対応をご紹介させていただきました。

Hidenori Doi
Aiming Inc. Lead Software Engineer / Manager - Manager
7
Comments