Entity Component System
Yasumichi Onishi
Unity Developer Relations Manager/ Engineer
大西 康満
株式会社バンダイナムコスタジオにエンジニアとして
18年在籍した後、5年ほどUnityを使ったモバイルゲー
ム開発に注力。2016にユニティ・テクノロジーズ・ジャパ
ン合同会社に入社、ディベロッパーリレーションマネー
ジャー・エンジニア(DRM/E)として活動中。過去の作品
は『Pac-Man C.E.』、『鉄拳』、『エースコンバット』など。
Unity / Developer Relations Engineer
5〜32倍
5〜32倍
• Mono x GameObject 比のスピード向上率 (ECS x JobSystem x Burstコンパイラ)
ECSとは

記号(色の意味)
• ユーザーがする操作
• 対応するUnityの現機能を「言うなれば」で書くとき
• Unityエンジンが自動でやること
ECSスタイル
• ECSスタイルでコードを書く
• GameObject列の処理速度アップ
背景 (近年の計算機の特徴)
• Memory Cache機構
• core数、スレッド数の増加
• ベクトル演算器の搭載
背景 (近年の計算機の特徴)
• Memory Cache機構
• core数、スレッド数の増加
• ベクトル演算器の搭載
• タイトなメモリレイアウトになるようコントロールしたい
• マルチスレッド処理したい
• Vectorizationしたい
背景 (近年の計算機の特徴)
• Memory Cache機構
• core数、スレッド数の増加
• ベクトル演算器の搭載
• タイトなメモリレイアウトになるようコントロールしたい
• マルチスレッド処理したい
• Vectorizationしたい
• C#で普通にClassをInstanceするとメモリ空間のあちこちにAllocされる
• Instanceへの参照列をIterateするとCache効率が悪い
• GameObject/ MonoBehaviourスタイルも例外ではない
C#でも
• Structの配列ならメモリ配置は連続になる
• Structの各メンバの配列の形式にして、かつ処理を局所的(関連メンバを絞る)にすれば
さらにフェッチ効率は上がる
• JobSystemとの相性も良い
• Auto Vectorizationが効くケースも増える
ECSスタイル
• Structの配列ならメモリ配置は連続になる
• Structの各メンバの配列の形式にして、かつ処理を局所的(関連メンバを絞る)にすれば
さらにフェッチ効率は上がる
• JobSystemとの相性も良い
• Auto Vectorizationが効くケースも増える
• ↑ECSスタイルの基本的なアイデア
ECSスタイル
• Structの配列ならメモリ配置は連続になる
• Structの各メンバの配列の形式にして、かつ処理を局所的(関連メンバを絞る)にすれば
さらにフェッチ効率は上がる
• JobSystemとの相性も良い
• Auto Vectorizationが効くケースも増える
• ECSスタイルでゲームコードを書くだけで これらを自然と実現できる
ECSとはなんぞや
• Entity: 速いGameObject
• Component: MonoBehaviourのデータ部分
• System: MonoBehaviourの振る舞い部分
public struct RotationSpeed : Component
{
public float Speed;
}
public struct Energy : Component
{
public int energy;
}
class EnemyAISystem : System
{
override protected OnUpdate()
{
float deltaTime = Time.deltaTime;
for (各GameObjectに対して)
{
//振る舞い
}
}
}
ECSとはなんぞや
• Entity: 速いGameObject
• Component: MonoBehaviourのデータ部分
• System: MonoBehaviourの振る舞い部分
• Object-orientedスタイル → データと振る舞いを分離する新スタイル
キーとなる機能
Entity(データを複数合わせたもの)
var entity = EntityManager.CreateEntity(); //Entityの生成
EntityManager.AddComponent(entity, new MyComponentData1()); //ComponentDataを追加できる
EntityManager.AddComponent(entity, new MyComponentData2()); //ComponentDataは複数持てる
EntityManager.SetComponent(entity, new MyComponentData2()); //Setもできる
ComponentData
Entity
データと振る舞いの関係
データ
振る舞い
要求リスト
データと振る舞いの関係
データ
振る舞い
処理される
処理される
Inject
public struct RotationSpeed : IComponentData
{
public float Speed;
}
データの記述
• Struct
• Blittable型のみで構成 (C++とC#でメモリ上の表現が同じになる型)
//Dataのみ
振る舞いの記述
class RotatorSystem : ComponentSystem
{
struct Group //要求リスト (全ての型を含むEntityがマッチ)
{
public int Length;
public ComponentDataArray<MyRotation> myrotation;
public ComponentDataArray<RotationSpeed> rotationSpeed;
}
[Inject] private Group m_Data; //マッチング結果のArrayが自動設定される
override protected OnUpdate() //1Fに一回呼ばれる
{
float deltaTime = Time.deltaTime;
for (int i = 0; i < m_Data.Length; ++i) //同じindexで同じEntityのComponentを参照できる
{
m_Data.mytrotation[i].Rotation *= Quaternion.AxisAngle(m_Data.rotationSpeed[i].Speed * deltaTime, Vector3.up);
}
}
}
振る舞いの記述 • マッチング条件を記述
• [Inject]
• OnUpdate()でマッチング結果を処理する
• Job化するのが常套手段
class RotatorSystem : ComponentSystem
{
struct Group //要求リスト (全ての型を含むEntityがマッチ)
{
public int Length;
public ComponentDataArray<MyRotation> myrotation;
public ComponentDataArray<RotationSpeed> rotationSpeed;
}
[Inject] private Group m_Data; //マッチング結果のArrayが自動設定される
override protected OnUpdate() //1Fに一回呼ばれる
{
float deltaTime = Time.deltaTime;
for (int i = 0; i < m_Data.Length; ++i) //同じindexで同じEntityのComponentを参照できる
{
m_Data.mytrotation[i].Rotation *= Quaternion.AxisAngle(m_Data.rotationSpeed[i].Speed * deltaTime, Vector3.up);
}
}
}
メモリレイアウト
EntityArchType
• Prefabのようなもの
• ComponentDataの組み合わせに他ならない
• メモリレイアウトの最適化に利用される
Chunk Memory
• Entityのインスタンス実体
• 1Chunk Memoryは L1キャッシュ並の大きさ
• 例えばstructのメンバが
• A(4bytes), B(2bytes), C(2bytes)のとき
• 各Memory Chunkは自分のEntityArchTypeを知っており効率よく配置される
Jobの記述Classと実行順序
下の方が固定機能であるが記述量は少ない
• ComponentSystem (前述) InjectionされてOnUpdate()が呼ばれる
• JobComponentSystem +自動でJob化される
• JobProcessComponentData + JobのExecute()の中身を書くだけ
振る舞い記述用Class三種()
• 内部的に依存関係グラフが管理されている
• 各ComponentSystemに着目し、WriteしているJob→ReadのみのJobの順に実行され
るよう依存が設定される
• [ReadOnly]属性を明示的につけないとWrite判定になる
• 自分で依存関係を指定することも可能
Jobの実行順序
Write
ReadOnly
開発状況
開発状況
• 絶賛開発中
• 2018.1? (2かも) でexperimental (β機能)
• https://github.com/Unity-Technologies/EntityComponentSystemSamples
• ↑最新状況を知るには当面上記githubが良い
• 色々なAPI方式を試しながら更新されている
• README.md→DocumentにもAPI仕様が書かれ更新されている
• EntityComponentSystemSamples/TwoStickShooter/PureというUnityプロジェ
クトの改造から始めるがお勧め
• このページから落とせるUnityとセットでないと動かないので注意
開発状況
• 2018 4月時点
• 色々な仕様の組み合わせを試している状況、細かい仕様は高頻度で変わる
• Β版でフィードバックを集め中
• 作りやすい仕様を選択していく
開発状況
• 今後GameObjectと似た感覚で操作できるように整備していく
• Hierarchy window やInspector windowで表示、編集可能に
• Save Scene / Open Scene / Prefabs感覚で使えるように
• さらに Geometry操作、Animation機能、Physicsクエリなどを用意していく...
• つなぎ期間用 Hybrid 方式
• 既存のMonoBehaviourコードをECSに移植するとき用
• GameObjectと同じEntityがOnEnable時に作れたり
• Monobehaviourへのアダプター

【Unite Tokyo 2018 Training Day】C#JobSystem & ECSでCPUを極限まで使い倒そう ~Entity Component System 編~