渋谷ほととぎす通信

新しいこと、枯れたこと問わず大庭が興味を持ったものを調査、生活の効率を求める完全趣味の技術ブログ。基礎を大事にしています。

CEDEC2019気になったUnity情報DOTS編


f:id:esakun:20190902025818p:plain CEDEC2019に聞き手として参加させて頂き「Unityではじめるオープンワールド入門 エンジニア編」セッションを元にDOTS関連についてまとめます。

そもそもDOTSとは?

簡単に紹介すると、DOTSとは将来のスタンダードなUnity開発手法になると思われるフレームワークです。

  • NativeContainer
  • Entitiy Compmnent System(以下:ECS)
  • C#JobSystem
  • Burst Compiler

これらの技術を使い、実行時のロード高速化、大量のオブジェクト、GC軽減などの恩恵にあずかることが出来ます。

DOTSと既存システムは組み合わせて使用

セッションではオープンワールド風のステージ + キャラ1体という構成のDEMOをベースに技術紹介されています。

オープンワールド風DEMOのざっくり仕様

  • 1km四方のステージ
  • 主にTerrainで作成(その他Houdini、Maya、SubStance Designerを使用)
  • 4万オブジェクト(LOD含めると21万オブジェクト)
  • 全体で1200万ポリゴン

f:id:esakun:20190908032218p:plain:w450

オープンワールドなので、生い茂った木や草が大量に配置されていて、草は風に揺れている風に動いています。 21万個のGameObjectを動かすというのはまず無理なので、それをDOTSだったらグリグリ動かせる、またモバイルでも動きますよというDEMO担っています。実機のiPhoneXでも30FPS出ていました。

DOTSは草や木のみ使用

オブジェクト タイプ
草・木 エンティティ
キャラ GameObject

ということで、全てのオブジェクトがECSということではなく、適材適所で使用されていました。
エンティティとは、ECSの世界で言うGameObject的なものでGameObjectから機能をそぎ取ったものです。

ECSを少しでも触ったことがある人ならわかると思いますが、現状のUnityエディタにおけるECSの扱い方はとてもハードモードでして、現状のGameObjectベースの開発と比べると、Unity社が掲げている思想「ゲームを民主化する」には程遠い状態です。

ということで、たくさん配置するものはECS、それ以外はGameObjectで作るというのが、現状の選択肢になるのではないかと思います。

シーンビューを活用したECSワークフロー

Unityの良いところの1つはエディタが強力な点です。
ECSで開発するときもエディタのシーンビューを使って、画面を見ながら開発したいため(その方が作りやすい)、SubSceneを使用したワークフローの紹介がされていました。

GameObjectをエンティティに変換したSubScene

f:id:esakun:20190908021244p:plain:w200

Hierarchyから任意のGameObjectを選んでコンテキストメニューからNew SubScene From SelectionをクリックするだけでGameObjectはエンティティに変換されます。


4万個のオブジェクトを一つ一つSubSceneにするわけにはいかないので、ステージを4分割し、4つのSubSceneを作成しています。
4分割の理由はTerrainを4つ使っているので、その単位でSubSceneにしているようでした。

ちなみにSubSceneに変換すると変換前のGameObjectは、変換したSubSceneの子階層に入ります。

SubSceneに変換したGameObjectに更新が入った場合のワークフロー

SubSceneに変換したGameObjectに更新が入った場合は、元のGameObjectを修正してSubSceneのInspectorのRebuild Entity Cacheボタンを押すだけで更新が入ります。
f:id:esakun:20190908021121p:plain:w450

これで、アーティストとの作業分担はできると思います。

【Unity】Scene上に構築したステージを、Entity群に変換してECSで利用可能にする「SubScene」 - テラシュールブログ
SubSceneについてはこちらの記事をご確認ください。

SubSceneになったことによる良い副作用

4万個のオブジェクト(LOD含めると21万個)をECSを使わなかった場合、実行するだけでもとても時間がかかることが想定されます。理由としては21万個のGameObjectのデシリアライズが入るためです。
エンティティ(SubScene)に事前に変換しておくことで、最適化されたメモリ配列になった状態でオブジェクトがロードされるため高速になります。
比較DEMOでもゲーム実行するまでの時間はECSの方が圧倒的に早かったです。


こういうところがECSの良いところなのでしょう。

現状SubSceneの外部リソース化には難あり

SubSceneに変換したオブジェクトは、以下のディレクトリに格納されます。

  • Assets/EntityCache/Resources/
  • Assets/StreamingAssets/EntityCache/

それらを外部リソースとして扱うためには、色々と頑張らないといけないようです。 そもそもStreamingAssetsに入っているバイナリはAssetBundleには出来ないので、そのままリモートにアップロードし、Resources配下のファイルはAssetBundleにしてアップロードします。

問題はパスで以下のEntityScenesPathsクラスにベタベタとStreamingAssetsResourcesと書かれているので、この辺を修正してパスを修正する必要がありそうです(未検証)


※確認バージョン Entity v0.1.1 preview

public static string GetLoadPath(Hash128 sceneGUID, PathType type, int sectionIndex)
{
    if (type == PathType.EntitiesSharedComponents)
        return $"{sceneGUID}_{sectionIndex}_shared";
    else if (type == PathType.EntitiesHeader)
        return GetPath(sceneGUID, type, "");

    var path = GetPath(sceneGUID, type, sectionIndex.ToString());

    if (type == PathType.EntitiesBinary)
        return Application.streamingAssetsPath + "/EntityCache/" + Path.GetFileName(path);
    else if (type == PathType.EntitiesSharedComponents)
        return Path.GetFileNameWithoutExtension(path);
    else
        return path;
}

まだまだPreviewなので、変更されるかもしれません。

端末が熱くならないようにGPU負荷を下げるTips

経緯としてはiPhoneXで動かしたら実機が異常に熱くなり、CEDEC会場で展示するということも踏まえ対策をされたようです。

プロファイルするとGPUに異常な負荷がかかっていたとのことで、以下それを解消するためのTipsです。

VertexShaderがクソ重い問題

VertexShaderが72ms使用していたようで、原因は草などに使用されているカットアウトでした。 ということで、カットアウトをやめるという修正を入れました。モバイルとは相性が悪いカットアウトです。

OnDemandRendering.renderFrameIntervalで描画処理をスキップ

ここでも取り上げたOnDemandRendering.renderFrameIntervalが活躍しています。

実機は展示されるため、触っていないときには負荷を下げて端末の熱が上がらないようにしたいところ。そこでOnDemandRendering.renderFrameIntervalを使い、画面に指がタッチしていないときには描画処理をスキップさせて負荷を下げ、無駄な発熱を抑えることが出来たようです。

Graphics Jobsを使用

f:id:esakun:20190906021623p:plain:w450

PlayerSettingsのGraphicsJobsにチェックを入れます。 するとGPUの処理をRenderThreadに移すことができ、GPU負荷を下げることができます。

CanvasのScreenSpaceOverlay使用しない

CanvasのScreenSpaceOverlayを使用すると毎フレームGPUが動いてしまうため、使用しないことでGPU負荷を下げる、というかGPU負荷を上げないようにしたとのこと。

解像度を下げた

今回実機がiPhoneXなので、DynamicResolutionが対応していたため(Metalは対応している)、解像度を下げてGPU負荷を下げたようです。

その他のトピックス

LightWeight Render Pipelineの名称が変更

Unity2019.3からUniversal Render Pipelineに変更になります。LightWeightという名前がしょぼそうという印象を与えてしまうかららしいです。

f:id:esakun:20190908032133p:plain:w450

最後に

DOTSを実践的に使用したポストモーテム的なセッションで、多くの知見が得られました。
しかし、ECSはこれからもAPIが変わっていきそうな雰囲気がするので、実戦投入はまだまだ先かなという印象です。個人的に遊んでみるのには良いかなと。
また、このオープンワールドのUnityプロジェクトは公開したいと言っていたので、いつかアップされるかもしれません。