
【cocone TECH TALK VOL.5・前編】Unity Dotsを使ってみた【イベントレポート】
-
2022年4月7日
こんにちは!cocone tech blog編集長のYです!
今回は2022年2月25日に行われた「cocone TECH TALK vol.5 ~ cocone x cluster ~」でお話した内容のうち、前編としてココネから発表させていただいた内容をまとめたものとなります。
本記事は「Unity Dotsを使ってみた」というテーマについての発表を記事にしたものになります。
初めまして、ココネ株式会社より登壇させていただく田村です。
今回は「Unity Dotsを使ってみた」というテーマでお話させていただければと思います。よろしくお願いします。
今回はこのように5つ構成となります。
DOTSに関する簡単な説明
まずはDotsに関する簡単な説明です。
こちらは公式サイトのトップ画像のスクリーンショットです。
すごくざっくりと要約すると、どうやら動作が早くなるそうです。
Unity Dotsのサイトの内容を要約するとこのような3つの特徴があるそうです。
続いてUnity Dotsは以下のような構成となっています。
physicsなどを扱う場合は4つになります。
今回はこれらの基本の3種類のうち、ECSについてお話させていただこうと思います。
データ指向とECSの簡単な説明
まずはじめに、データ指向型設計についてお話します。
データ指向というものは、オブジェクト指向などとは違い、複数のデータに着目した設計思想となっています。
元来CPUとメモリの処理速度には大きな差が存在し、その処理速度の差を埋めるためにメモリを考慮して設計を行います。
簡単に言うと、メモリに入っているデータが読み込みの際にいい感じの順番になっていないと時間がかかってしまうので、それをプログラミング側で解決しようというお話です。
続いてECSの話ですが、主題の通りMonobehaviorをあまり使わない設計となっています。
完全にMonobehaviorを使用しない、といのも可能ではあるのですが今回は使っていこうと思います。
こちらがECSの基本的な作りとなります。
ECSでは”SystemBase”や”ComponentSystem”を継承して”Entity”を作成します。
なのでMonobehaviorなどは基本的には継承しない、ということになります。
また、クラスの属性に”UpdateAfter”などを追加して、クラスの”OnUpdate”がどのタイミングで呼ばれるかの制御を行います。
例えば上記画像の右側のソースのように、OnUpdate内のForEachにComponentDataなどを継承した引数が存在した場合、処理が行われます。
なおインスペクターからデータを登録したい場合、通常通りmonoを継承した後、IConvertGameObjectToEntity を継承すれば大丈夫です。
次にサンプルとしてECSの作り方などを解説していこうと思います。
はじめに、SystemBaseやComponentSystemなどを継承して作成したメインキャラクターのEntityを初期化します。
今回はMainCharacter、つまりplayerに必要な各種コンポーネントを追加します。
続いてNPCの方も作っていきます。
こちらはPlayerTagの代わりにNpcTagを追加します。
また、基本的にNPCを操作することはないのでInputsも外します。
仮にNPC以外を一括で処理したい場合、スライドのような書き方を行います。
この時、先ほど追加したNpcTagが生きてきます。
WithNoneを指定することで、指定したコンポーネントを持っていないEntityに対して、ForWach内の処理を適用する事ができます。
この場合、”EntityがNpcTagを持っていない時” && “EntityがTranslationを持っている時” を満たすときに実行されます。
このようにいくつかオプションもあります。
これらを用いて状況に合わせて条件の変更を行います。
初期化のサンプルはこのようになります。
初期化に必要なパラメータがあるコンポーネントを追加し、初期化が完了するとそのコンポーネントを除外します。
まとめると、指定したコンポーネントデータをEntityが持っている場合にのみ、処理が行われます。
また、上記の組み合わせによって処理したくないEntityはWithNoneで除外します。
そして、小さい関数ブロックのようなものがたくさん出来ます。
今回テストした内容について
続いて今回UnityDotsを利用してどのようなテストを行ったのかご紹介します。
今回は32~64人で遊ぶ3DスマホアクションゲームをUnityDotsを活用して作りました。
まずは普段のやりかたでプロトタイプを作成してみました。
するとスライドのような5つの問題点が浮上しました。
これらの問題点を解決するために、Dotsを利用する事となりました。
今回使用したパッケージはこのスライドの通りで、Dotsを利用する場合はRenderPipelineが必須となります。
上記のパッケージを導入するにあたっての注意事項を説明したいと思います。
パッケージを全て導入すると、System.Memoryとcom.unity.searcher@4.7.0がバッティングするので、解決方法の部分を実行してください。
続いて、ゲームシーンの初期化についてです。
基本的にUnityEcsならびにUnityDotsについてはサブシーンがUnity内に生成されていますので、そちらに基本的なゲームオブジェクトなどを追加する形になります。
サブシーンに追加したものは自動的にEntityに変換されるので便利です。
その後はIConvertGameObjectToEntityを継承したスクリプトを追加し、必要なパラメータをインスペクターで入力したら完了となります。
こちらは実際に使用したソースコードとインスペクタのスクリーンショットになります。
ScenInitializationAuthoringというクラスがあり、こちらにPlayerやNPC・カメラなどのPrefabを追加し、初期化時に生成するという形になっております。
クラス構造はこのようになっています。
少し略しているのですが、RigitTrans MCSpwnなどはTransformのことでスポーン場所のpositionを指定するものです。
Entity MCPrefabはスポーン場所にPrefabを生成するという形になります。
基本的にEntityなどでPlayerやNPCなどを生成する場合はスポーンシステムのようなPrefabをスポーンさせるための構造が必要となってきます。
スポーンはこのような形となっています。
PlayerのEntityをインスタンス化し、必要なTranslation、Rotation、PlayerTag、InputsをAddComponentDataで付与します。
カメラのセットアップなども終了したら、この工程は1度通ればいいので、最後にRemoveComponentでSceneInitializationを除外します。
以上である程度の実装は完了したので、次にテストしたい項目を追加しました。
どの程度高速化されたのか
追加していった結果、普通に作成したプロジェクトと比べてどの程度高速化されたのかについて話します。
比較方法はこの通りです。
まずはECS等関係なしのプロジェクトで、こちらはMonoBehaviorと若干のUniTaskで作成したプロジェクト。
次にECSのみを適用したプロジェクト。
そしてECS+Burstコンパイルを適用したプロジェクトとなります
今回は厳密な計測ではなく、ある程度の計測となりますが、スライドのようにECS+Burstでは47~60fps程度出るようになりました。
計測結果を表にまとめるとこのようになります。
全ての項目について高速化が実現されました。
現状で使うとしてどのような場面に使いたいか
Dots関係を使用する場合、変更がとても多いことに留意する必要があります。
なので、3Dのコアの部分など変更が大変な部分は避け、ネットワーク関係の処理などはシステムに依存する事ができるので、通信処理や同期処理、Dots側でイベントを発行しMono側へ移すなどといった処理をすることで、実装が容易になると思います。
アンセーフコードが多くなるのでおすすめはしませんが、Physicsの計算などを行うことも出来ます。
最後に参考文献等のご紹介です。
これは押さえておきたいですね。
こちらはあまり日本ではあまり有名ではないのですが、SpatialOSというものがあります。
こちらはUnity内でサーバーを立てる事が可能で、通信を行うものなのですが、この通信の一部にDotsが利用されているので参考になると思います。
Unity DOTS 入門 (1) – 従来のUnityの設計をDOTSの設計に変換
以上で発表を終わります。ありがとうございました。
スライド
いかがでしたか?
Unity Dotsを上手く利用する事でかなりの高速化が出来ました。
ぜひ皆さんもUnity Dotsを導入して、高速化に挑戦してみましょう!
後編ではクラスター株式会社様より「AvatarMakerを支える技術」というテーマでお話していただいた内容についてお届けいたします!
お楽しみに!
また、ココネでは一緒に働く仲間を募集中です。
ご興味のある方は、ぜひこちらの採用特設サイトをご覧ください。