[Unity DOTS]EntityComponentSystemSamplesを補足する
はじめに
数か月前にUnity DOTS ver1.0.0がリリースされたようなので、サンプルを通して勉強してみました。
Readmeにサンプルの説明はありますが、いまいち理解できなかった点などをできるだけ補足します。バーストやジョブについては基礎となる知識はある前提で進めます。
間違ってたらすみません。
サンプルについて
※1.0.0-pre.15を使用しています。まだまだ変更が多いようなのでバージョンの違いには注意してください。
ウォークスルー
プロジェクトのセットアップ
4.Enter Play Mode Settingを変更するあたり
UnityのYoutubeチャンネルでも説明、注意点がありました。
【Unity】開発効率アップ! Enter Play Mode オプション解説 - YouTube
DOTSがstatic変数は使用しない考えで作られているため(使えなくはないらしい)、他のプロジェクトで変更する場合は注意してください。
ステップ1 Authoring Scene
なぜサブシーンを作成するのか
詳しい説明は特になかった気がします。
調べてみましたが、ロード時間を改善できるようです。
ECSのサブシーンがロード時間を改善できるのはなぜですか?- ユニティフォーラム (unity.com)
Colliderの削除について
余談ですが、Unity DOTSでは Physics Shape を使用します。ただしここはUnity Physics の範囲になります。
ステップ2 Turret Rotation
OnCreateについて
Start関数のような名前をしていますが、どうも実行されるタイミングが異なるため同じような使い方はできませんでした。
後のステップで説明がありますが、OnUpdateに処理を書いて、処理が終わる際にenableの値を変更して2度目が走らないようにするようです。
SystemAPI.Query<TransformAspect>()について
オブジェクトのコンポーネントを取得する方法の一つです。使用頻度はかなり高くなります。
全オブジェクトからTransformAspectを持っているオブジェクトを探して実行されるため、タグを設定する前のサンプルの砲台では、全てのオブジェクトが回転してしまっています。
Bakerについて
Unity既存のオブジェクトをECSで扱えるようにするための仕組みです。結構冗長に感じますが、省略できないようです。
ステップ3 Tank movement
ISystemとSystemBaseはどちらがいいのか
基本的にISystemにできるならISystemの方が早いはずです。
マネージドコードがあればコンパイルに失敗するはずなので、「一旦ISystemにしておく」でも大丈夫だと思います。
Entities .WithAll<Tank>().ForEach(Entity entity, TransformAspect transform)
このあたりの書き方自体はSystemAPI.Queryとほぼ変わりません。
WithAllで指定したコンポーネントを含むオブジェクトに対して実行し、引数に特定のコンポーネントを指定することで、検索したオブジェクトのコンポーネントを直接取得できます。
Entities.ForEach()はもう古くなっているようなので注意してください。
ステップ4 Cannon Balls
IAspectについて
他のコンポーネントをまとめるような機能です。Aspectを作らなければならない事例は出会ったことはないです。
ComponentLookupについて
ジョブ内でEntityからコンポーネントを取得できる機能です。(おそらく一番ベターな方法)
OnUpdate内でUpdateを呼ばなければなりません。
ECBについて
本来Entityを作成すると”構造的変更”になるようなので、同期点が必要になります。ECBに入れておくと後でまとめて作成みたいなことをしてくれます。
ジョブ内で構造的変更をする際はECBを使うようにしてください。
BeginSimulationEntityCommandBufferSystemについて
Systemウインドウを見てみると以下のようになっています。
Update -> Simulation System Group ->
Begin Simulation Entity Command Buffer System
の位置に同じ名前のものがあります。
どうやらECBの実行タイミングの指定になっているようです。
Turret Shooting System は Begin Simulation Entity Command Buffer System 以降に実行されているため、実際のタレット生成は1フレーム遅れているかと思います。
ちなみにEndSimulationEntityCommandBufferSystem や BeginInitializationEntityCommandBufferSystem でも特に動作に影響はありませんでした。
[ReadOnly]について
基本的に読み取りしかしないのであればReadOnlyを付けておきます。書き込む可能性がある場合、他のスレッド等で同時に書き込んでいないかを逐一確認する処理が入るそうなので実行速度に影響するようです。
void Execute(in TurretAspect turret)について
引数にTurretAspectをいれておくことでTurretAspectを検索してそれぞれに実行してくれます。
ステップ5 Cannon ball movement
ECB.DestroyEntity(chunkIndex, cannonBall.Self) について
ここでのECBは並列実行のため、EntityCommandBuffer.ParallelWriter が使用されています。
第1引数にsortkeyと呼ばれる引数を受け取っていて実行順のソートに使用されるようです。[ChunkIndexInQuery]によって得られる値が適切だとドキュメントにありますが、おそらく実行する際に同一チャンクである方が実行が早くなるメリットがあるのだと思います。
ステップ6 Spawning many tanks
GetSingletonについて
Singletonパターンを実装しているかは関係なく、指定したコンポーネントが一つしかない場合、それを取得してきます。複数ある場合や一つもない場合はエラーを吐きます。
ステップ7 Colored tanks and cannon balls
SetComponentForLinkedEntityGroupについて
ここで指定されているEntityの子に対してもコンポーネントがセットされます。また、queryMaskを使用してURPMaterialPropertyBaseColorを持っているEntityのみに適用されています。
URPMaterialPropertyBaseColor について
せっかくなので内部の実装も見てみると以下のようになっています。
namespace Unity.Rendering
{
[MaterialProperty("_BaseColor", -1)]
public struct URPMaterialPropertyBaseColor : IComponentData, IQueryTypeParameter
{
public float4 Value;
}
}
マテリアルの_BaseColorの値を変更できるようです。
自作マテリアルでも変更可能でした。(少し設定の変更は必要ですが)
ステップ8 Safe zone
NativeDisableParallelForRestriction について
並列実行の際に行われる安全性の確認を無視することができます。
各々のジョブが被らずコンポーネントにアクセスできるため、安全性を無視しても大丈夫という考え方だと思います。
IEnableableComponent について
ここではマスクに使うような動きのために使われていますが、他の実装例として
・Enableの操作ではなく、コンポーネントを削除する
・コンポーネント内にtrue/falseの値を保持させておき、動作を分岐させる
の2通りもあります。それらと比べてもIEnableableComponent を使用する方がパフォーマンス的に良いそうです。(他のサンプルで試されています。)