Advertisement
Advertisement

More Related Content

Slideshows for you(20)

Advertisement

More from Unity Technologies Japan K.K.(20)

Recently uploaded(20)

Advertisement

Unityではじめるオープンワールド制作 エンジニア編

  1. Unityではじめるオープンワールド制作 エンジニア編 Tatsuhiko Yamamura @ Unity Takeshi Oshita @ Unity
  2. Unityではじめるオープンワールド制作 /2 アーティスト編 エンジニア 編 広域で大量のオブジェクトを扱うオープンワールド風フィールドをUnityで、 どう作る? どう動かす?
  3. オープンワールド風な世界を表現したい 広いステージ 多いオブジェクト スムーズな
 ロード・アンロード
  4. を、モバイルで。
  5. DOTS (Data-Oriented Technology Stack) • ECS • オブジェクトの管理と処理の制御 • JobSystem • 並列処理 • Burst • 高速なアセンブリを出力するコンパイラ •連続して同じ処理を行いたい •素早くオブジェクトを集めたい •並列で処理したい •データを効率的に処理したい
  6. 最終的な結果 広いステージ 多いオブジェクト スムーズな
 ロード・アンロード 1km ✕ 1kmの広域を表現 合計20万 Entity (4万LOD) メインスレッドのスパイク2ms以下
  7. LODの切り替えが酷いのは自分の手抜き
  8. • 超広域マップにおける誤差問題は
 今回はノータッチ • ECSベースの
 物理演算(Unity Physics)は未使用
 ColliderはGameObjectで配置 • キャラクターの挙動制御は
 従来のMonoBehaviourを使用 なお…
  9. 構成はハイブリットECS キャラクター挙動 物理演算 ステージロード プレイヤー
 座標 オブジェクト配置 カメラ制御 UI GameObject ECS 大量のオブジェクト
 制御に強力 個別制御の
 実装が簡単 描画
  10. 1.ECSベースのシーンへ変換 2.シーンをロード 3.広範囲を描画 4.その他、モバイル対策 今回の話
  11. • 大量のGameObjectは嫌なので、 Entityに変換 • シーン上に配置したオブジェクトの 配置を、そのまま使用したい • Sceneエディターを使いたい GameObjectからECSへ変換
  12. ECSとGameObjectではデータが異なる GameObject Translation Mesh Renderer Transform Mesh Filter Rotation Scale RenderMesh Transform
 Matrix Entity Position Scale Rotation Mesh Material Shadow Lighting
  13. 対応するコンポーネントへコンバート Translation Rotation Scale RenderMesh Transform
 Matrix EntityGameObject Mesh Renderer Transform Mesh Filter Position Scale Rotation Mesh Material Shadow Lighting Conversion
 System Conversion
 System
  14. GOからEntityへ変換するシステムを用意 変換するGO一覧から
 「Light」を持つモノを抽出 LightComponentを 作り中身を埋める Entityに
 LightComponentを登録
  15. パッケージの幾つかはコンバーターに対応UnityPhysics Rigidbody Collider PhysicsBody PhysicsShape HybridRenderer MeshRenderer LODGroup Light LightComponent MeshLODGroup Component RenderMesh 変換前 変換後
  16. ConvertToEntityでGOからEntityへ変換 • GameObjectをEntityへ変換 • 既存のコンポーネントを活用する場合は
 コチラを使用する e.g. Translationの位置でAnimatorのパラメーターを変更する等。
 TranslationはECSで計算し、結果をAnimatorに注入する
  17. ConvertToEntityワークフロー EditorDisk Scene GameObjects GameObjects Entities/ ComponentGame GameObjects Entities/ Component • 実行時にGameObject毎に変換処理 • 量が多いと困る事も Convert Convert GameObjects
  18. • エディターでGameObjectをEntityへ変換 • ECSのEntity/Componentエディターという立ち位置も • SubScene以下のオブジェクトは
 全てEntityへ変換される SubSceneワークフロー
  19. SubSceneワークフロー EditorDisk Scene GameObjects GameObjects Game GameObjects Entities/ Component Convert
  20. SubSceneワークフロー EditorDisk Scene GameObjects GameObjects Entities/ Component SubScene Save Game Convert GameObjects GameObjectを変換し
 SubSceneとして保存
  21. SubSceneワークフロー EditorDisk Scene GameObjects GameObjects Entities/ Component SubScene Entities/ComponentsLoad Merge Game GameObjects エディターで結果を確認するため
 現在のワールドにオブジェクトを登録 GameObjectをデシリアライズせず
 画面のプレビューが可能
  22. SubSceneワークフロー EditorDisk Scene GameObjects GameObjects Entities/ Component SubScene Save Game Convert GameObjects SubSceneの内容を変更したい場合、
 元となるGameObjectを編集
  23. SubSceneワークフロー EditorDisk Scene GameObjects GameObjects Entities/ Component SubScene Entities/Components Game GameObjects Entities/ Component Merge Load ゲームプレイ時も
 同じデータを使用
  24. サブシーン変換手順
  25. 変換後のファイル SubSceneデータ本体 SubSceneが参照するデータ
 (大体がアセットを参照する用途)
  26. 全部を含めると広域をロードしすぎる・・・ Root OBJ 1 OBJ 2 OBJ 3 OBJ 4 OBJ 5 OBJ 6 OBJ 7 OBJ 8 …
  27. fff f f f SubScene
 (0, 0) SubScene
 (1, 0) SubScene
 (2, 0) SubScene
 (2, 1) SubScene
 (2, 2) SubScene
 (1, 2) SubScene
 (0, 2) SubScene
 (1, 1) SubScene
 (0, 1) グリット毎にGOを分割してSubSceneに変換 Root1 OBJ 1 OBJ 2 Root2 OBJ 3 OBJ 4 Root3 OBJ5 OBJ 6 Root4 OBJ 7 OBJ 8 …
  28. •All Game Object
 約 1分35秒 •ECS
 約 8秒 ゲームプレイ開始までの時間も短く ECSでもSubScene編集時は
 開いているオブジェクトの数だけ
 起動時間が伸びる
  29. コンバーターは無ければ変換されない GameObject Translation Rotation Transform
 Matrix Renderer TreeTag Mesh Renderer Transform TreeTagC Mesh Filter Grid LOD Generator Scale Convert Convert Convert • 変換先がなければ
 コンポーネントは
 パフォーマンスに
 影響を及ぼさない • オブジェクトに拡張
 用コードを登録しても
 特に問題は無い
  30. Entity ≒ オブジェクト? Entity Translation Rotation Collider Entity Translation Rotation Collider Entity Translation Rotation Collider Player Entity Translation Rotation Collider
  31. Entity毎にコンポーネントが纏められるように見えるが… var pos = EntityManager.GetComponentData<Translation>(entity); Entity Translation Rotation Collider Entity Translation Rotation Collider Entity Translation Rotation Collider Entity Translation Rotation Collider Entity Translation Rotation Collider Rigidbody Rigidbody
  32. オブジェクト間のメモリは連続して置かれる Translation Translation Translation Translation Translation RotationRotationRotationRotationRotation Health Health Health RigidbodyRigidbodyRigidbody Transform
 Matrix Transform
 Matrix Transform
 Matrix Renderer Transform
 Matrix Transform
 Matrix Renderer Renderer Renderer Renderer Player Enemy Enemy Translation Rotation Health Rigidbody Transform
 Matrix Renderer Enemy Tree Tree Translation Translation RotationRotation Transform
 Matrix Transform
 Matrix Renderer Renderer Tree Tree
  33. 実態は構造体の配列 Translation Translation Translation Translation Translation RotationRotationRotationRotationRotation Health Health Health RigidbodyRigidbodyRigidbody Transform
 Matrix Transform
 Matrix Transform
 Matrix Renderer Transform
 Matrix Transform
 Matrix Renderer Renderer Renderer Renderer Player Enemy Enemy Translation Rotation Health Rigidbody Transform
 Matrix Renderer Enemy Tree Tree Translation Translation RotationRotation Transform
 Matrix Transform
 Matrix Renderer Renderer Tree Tree Array[] Array[]
  34. EntityはデータにアクセスするためのID Translation Translation Translation Translation Translation RotationRotationRotationRotationRotation Health Health Health RigidbodyRigidbodyRigidbody Transform
 Matrix Transform
 Matrix Transform
 Matrix Renderer Transform
 Matrix Transform
 Matrix Renderer Renderer Renderer Renderer Player Enemy Enemy Translation Rotation Health Rigidbody Transform
 Matrix Renderer Enemy Tree Tree Translation Translation RotationRotation Transform
 Matrix Transform
 Matrix Renderer Renderer Tree Tree Entity Array[] Array[]
  35. 配列にIDを渡して参照するイメージ Translation Translation Translation Translation Translation RotationRotationRotationRotationRotation Health Health Health RigidbodyRigidbodyRigidbody Transform
 Matrix Transform
 Matrix Transform
 Matrix Renderer Transform
 Matrix Transform
 Matrix Renderer Renderer Renderer Renderer Player Enemy Enemy Translation Rotation Health Rigidbody Transform
 Matrix Renderer Enemy Tree Tree Translation Translation RotationRotation Transform
 Matrix Transform
 Matrix Renderer Renderer Tree Tree TransformMatrix [ Entity ]
  36. Chunk Chunk Chunk コンポーネントの組み合わせでチャンクを分割 Translation Translation Translation Translation Translation RotationRotationRotationRotationRotation Health Health Health RigidbodyRigidbodyRigidbody Transform
 Matrix Transform
 Matrix Transform
 Matrix Renderer Transform
 Matrix Transform
 Matrix Renderer Renderer Renderer Renderer Player Enemy Enemy Translation Rotation Health Rigidbody Transform
 Matrix Renderer Enemy Tree Tree Translation Translation RotationRotation Transform
 Matrix Transform
 Matrix Renderer Renderer Tree Tree 実際にはChunkという単位で分割
  37. チャンク単位でのアクセス Translation Translation Translation Translation Translation RotationRotationRotationRotationRotation Health Health Health RigidbodyRigidbodyRigidbody Transform
 Matrix Transform
 Matrix Transform
 Matrix Renderer Transform
 Matrix Transform
 Matrix Renderer Renderer Renderer Renderer Player Enemy Enemy Translation Rotation Health Rigidbody Transform
 Matrix Renderer Enemy Tree Tree Translation Translation RotationRotation Transform
 Matrix Transform
 Matrix Renderer Renderer Tree Tree Chunk Chunk Chunk 「特定の組み合わせ」を持つデータを素早く取得
  38. ArchetypeArchetypeArchetype • Chunk
 (データ構造) • Entity
 (チャンク内のデータにアクセスするキー) • Archetype
 (Entityが持っているデータの組み合わせ) • Shared Component Data
 (複数のEntityから参照できるデータ) SubSceneの中身 Chunk Chunk Chunk Chunk Shared Component Data Type Manager Entities
  39. ArchetypeArchetypeArchetype • Chunk
 (データ構造) • Entity
 (チャンク内のデータにアクセスするキー) • Archetype
 (Entityが持っているデータの組み合わせ) • Shared Component Data
 (複数のEntityから参照できるデータ) SubSceneの中身 Chunk Chunk Chunk Chunk Shared Component Data Type Manager Entities SubScene
  40. “実際にゲームでロードする流れ”
  41. • ロードとアンロードを早くしたい
 • メインスレッドに対して負荷を掛けたくない どうしたいか Load UnLoad Loaded
 SubScene 早ければ、近くでロードしても間に合う ゲーム進行を止めたくない
  42. • SubSceneのAutoLoadSceneにチェックを入れる (簡単)
 • SceneDataを持つEntityにRequestSceneLoadを追加
 (距離に応じて段階的にSubSceneをロード・アンロードしたい場合等) どうやってロードするのか? or OR
  43. Terrain&他 SubScene 0.8秒 3.7秒 &Terrain&他 GameObject 8.2秒
  44. 約20万Entity メインスレッドの負荷
 約2ms以下
 ※ShaderCompile、Colliderの生成は除く
  45. SubScene ロードすべきもの ArchetypeArchetypeArchetype Chunk Chunk Chunk Chunk Shared Component Data Entities アセット郡 Prefab
  46. チャンクのロード Chunk Chunk Chunk ArchetypeArchetypeDisk
  47. チャンクのロード Chunk Chunk Chunk ArchetypeArchetypeDisk Chunk MemCopy ほぼコピーするだけ
  48. SubSceneB ChunkはSubScene毎に独立している SubSceneA Chunk Chunk Chunk Chunk SubSceneID (B) SharedComponentData SubSceneID (A) SharedComponentData チャンクが独立しているので、 既にあるChunkに値をマージはしなくても良い
  49. マネージドメモリを挟まずデータをロード
  50. Assets SharedComponentData • Shared Component Dataが参照する
 Prefabを経由してアセットを参照 • 通常と同様のアセット読込 アセットのロード RenderMesh SubSceneRenderMesh RenderMesh Prefab
  51. Strage 読込はAyncUploadPipelineを活用 Memory VRAM Strage Memory VRAM ヒープに読み終わるまで待機 リングバッファ分を読込 アップロード完了まで待つ 固定タイムスライスで
 少しずつアップロード AUP無し AUP 非同期読込 非同期読込
  52. Entityのロード Entities Entities Staging World Main World 使用できるEntityに間隔がある
  53. Entities Entities EntityのIDをリマップ Staging World Main World Remap
  54. Main WorldにEntityをマージ Entities Entities Staging World Main World Remap
  55. SubSceneを非同期でロード Main World Staging World Staging World Staging Wor
  56. SubSceneを非同期でロード Main World Staging World Staging World Staging Wor Load Request
  57. SubSceneを非同期でロード Main World Staging World Staging World Staging Wor Load Shared Components Load Entities ECS Data Load Async Load Async メモリ的に独立してるので
 別スレッドでも無問題
  58. SubSceneを非同期でロード Main World Staging World Staging World Staging Wor Load Shared Components Load Entities ECS Data Move メインワールドに統合
 マージだけなので
 短期間で終わる
  59. SubSceneのアンロード SubSceneBSubSceneA Chunk Chunk Chunk Chunk SubSceneID (B) SharedComponentData SubSceneID (A) SharedComponentData SubSceneIDを参照しているチャンクを開放する
  60. “ロード終了、次は描画周り”
  61. • 全体で4万LOD • 遠い距離のオブジェクトも表現 • ついでにポリゴンも多い
 (初期画面、1200万ポリゴン) たくさんのオブジェクトを表現したい
  62. • ECSで使えるレンダラー • 同じタイプのメッシュを連続して描画(バッチング) • カリング、LOD、HLOD付き • MeshRendererとMeshFilterからの変換 • 描画はBatchRendererGroupを使用 ハイブリットレンダラー
  63. MeshRendererを変換したEntity Translation Rotation Scale LocalToWorld
 Matrix RendererMesh Render
 Bounds WorldRender
 Bounce Entity Transform Transformから得られる行列 描画判定の大きさ 実際の描画判定 描画するメッシュの情報
  64. 描画のバッチング Translation Rotation Scale Transform
 Matrix RendererMesh Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix RendererMesh RendererMesh RendererMesh RendererMesh RendererMesh RendererMesh RendererMesh Render
 Bounds WorldRender
 Bounds Render
 Bounds Render
 Bounds Render
 Bounds Render
 Bounds Render
 Bounds Render
 Bounds Render
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds
  65. ChunkChunk 描画のバッチング Translation Rotation Scale Transform
 Matrix RendererMesh Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix RendererMesh Render
 Bounds Render
 Bounds Render
 Bounds Render
 Bounds Render
 Bounds Render
 Bounds Render
 Bounds Render
 Bounds RenderMeshの種類でチャンクを分割 WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds
  66. ChunkChunk 描画のバッチング Translation Rotation Scale Transform
 Matrix RendererMesh Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix RendererMesh Render
 Bounds WorldRender
 Bounds Render
 Bounds WorldRender
 Bounds Render
 Bounds WorldRender
 Bounds Render
 Bounds WorldRender
 Bounds Render
 Bounds WorldRender
 Bounds Render
 Bounds WorldRender
 Bounds Render
 Bounds WorldRender
 Bounds Render
 Bounds WorldRender
 Bounds 一気に描画(最大1023インスタンス) Render Batch Render Batch
  67. 描画したいオブジェクトが交互に配置されている … RenderMesh 異なるメッシュの草が交互に配置
  68. 通常の描画 … 青を描画 赤を描画 青を描画 赤を描画 青を描画 見える範囲
  69. RenderBatchで描画している場合 … 青を描画 赤を描画 青を描画 赤を描画 青を描画 Chunk RendererMesh(青) Chunk RendererMesh(赤) バッチングが優先される
  70. Instancingが有効なら、よくまとまる … 青をまとめて描画(Instancing) 赤をまとめて描画(Instancing) Chunk RendererMesh(青) Chunk RendererMesh(赤) ※半透明の描画は
 注意が必要
  71. • 親子構造で動く事は無い • バウンディングボックスを 更新しなくても良い ステージのオブジェクトは (ほぼ)動かない 動かない 動かない 動かない 動く
  72. オブジェクトの親子構造 Root Grid1 LOD LOD0 LOD1 LOD2 LOD LOD0 LOD1 LOD2 Grid2 ワールド座標を決める為に
 親の座標を知る必要 描画されるべき範囲を決めるため
 ワールド座標が必要
  73. オブジェクトの親子構造をフラットに Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix Translation Rotation Scale Transform
 Matrix RendererMesh Render
 Bounds WorldRender
 Bounds Render
 Bounds WorldRender
 Bounds Render
 Bounds WorldRender
 Bounds Render
 Bounds WorldRender
 Bounds Transform
 Matrix Transform
 Matrix Transform
 Matrix Transform
 Matrix RendererMesh WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds
  74. ステージの親にStaticOptimizeEntityを追加 Root Grid1 LOD LOD0 LOD1 LOD2 LOD LOD0 LOD1 LOD2 Grid2 子オブジェクトに対して最適化
  75. チャンク単位でバウンディングボックスを設定 ChunkBounds Transform
 Matrix Transform
 Matrix Transform
 Matrix Transform
 Matrix RendererMesh WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds Transform
 Matrix Transform
 Matrix Transform
 Matrix Transform
 Matrix RendererMesh WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds WorldRender
 Bounds
  76. ChunkBounds ChunkBounds ChunkBounds ChunkBounds ChunkBounds ChunkBounds ChunkBounds チャンク毎に
 描画範囲を持つ
  77. ChunkBounds ChunkBounds ChunkBounds ChunkBounds ChunkBounds ChunkBounds ChunkBounds チャンク毎に
 描画するか
 判断できる
  78. ChunkBounds ChunkBounds ChunkBounds ChunkBounds ChunkBounds ChunkBounds ChunkBounds インスタンス毎のカリングで
 画面外を除外
  79. できるだけ広範囲を描画したい 遠くの小さいオブジェクトも表現
  80. 視界の範囲を描画 描画されるオブジェクト 描画されないオブジェクト 視界
  81. 遠距離を描画すると負荷が跳ね上がる 見える範囲が広がると
 描画回数も大幅に増える
  82. 遠距離の描画 結合すれば描画命令は抑えられるが、
 描画すべき項目が増えてしまう…
  83. 遠距離の描画(HLOD) 遠距離では結合したメッシュを表示 1 描画命令 1 描画命令
  84. 遠距離の描画 カメラを近づけると
 HLOD結合前のモデルになる
  85. HLODの構築 LOD 0 LOD 1 LOD 2 HLOD 1 HLOD 0 LOD 0 HLOD用モデルはエディター拡張で作成
  86. SubScene HLODの段階的ロード HLOD RenderMesh RenderMesh RenderMesh RenderMesh LOD LOD RenderMesh RenderMesh RenderMesh LOD HLOD RenderMesh RenderMesh RenderMesh LOD LOD
  87. RenderMesh RenderMesh RenderMesh LOD LOD RenderMesh RenderMesh RenderMesh Section HLODの段階的ロード セクションで分割 必要に応じてロード SubScene HLOD RenderMesh LOD HLOD RenderMesh RenderMesh RenderMesh LOD LOD
  88. Sectionの例 HLOD0ロード済 HLOD0を未ロード
  89. • バッチの特性上、透明は描画順で問題を起こす
 (奥の透明が手前に描画される事も) • Bakeしたライトマップは使えない
 LightProbeとリアルタイムシャドウを使う • BatchRendererGroupが
 SRP Batcherと相性が悪い ハイブリットレンダラーの問題点
  90. “モバイルで動かそう”
  91. • CPU的には超余裕 • 発熱やばい • GPUがやばい 動かしてみた iPhoneX
  92. • Vertex Shaderに72ms • だいたいCutoutのせい • 絵的に許せる物はShaderをTransparentへ • 草をポリゴン切り抜きにしたかった(希望 • トライアングルをLODで減らす
 12M → 4Mへ • LODのバイアス 2 → 1 頂点シェーダーの負荷
  93. https://github.com/Unity-Technologies/UnityMeshSimplifier LOD化されてないメッシュはUnityMeshSimplifireを使用
  94. • ビルボード • mpostorsを使うと、頂点数を
 大幅に減らせる • モデルによっては絵が
 破綻する事も Impostors
  95. • 解像度を減らそう(SRPで) • UIだけ高解像度、ゲーム画面は低解像度 フィルレート
  96. • 描画処理を
 複数のスレッドに分割 Graphics Jobs RenderThread RenderThread WorkerThread WorkerThread WorkerThread
  97. • 放置すると、あっさり発熱アップ • 熱が上がるとパフォーマンスが下がる • レンダリング頻度をへらす機会を作る • OnDemandRendering.renderFrameIntervalを使う
 (SRPでレンダリングをスキップする、PlayerLoopを操作する等でも可) 発熱対策
  98. • GPUを動かさなければ発熱はかなり抑えられる
 タイミングを見つけてフレームスキップ • ScreenSpaceOverlayのUIはGPUを動かし続ける点に注意
  99. • 広いステージ • 多いオブジェクト • 早いロード・アンロード まとめ • 効率的なバッチ処理 • 広域描画時に使えるHLOD • 積極的な処理のスキップ • ステージの段階的なロード・アンロード • 高速でスパイクの少ないロード処理 • 多くの処理の並列化
  100. 1.オブジェクトをシーンに配置 2.SubSceneに変換 3.SubSceneをロード 4.チャンク単位でバッチ描画 5.HLODで描画負荷を減らす まとめ データの塊を動かすスタイルで
 色々と面白い最適化が出来る SceneViewで配置するスタイルでも
 結構高速で動くモノが作れる
  101. まとめ ゲーム内で「量が多いオブジェクト」はECSに変換すると効率的
 それ以外は今まで通りGameObjectベースで開発してOK! ECSはデータの管理を便利にしてくれる。
 ただ、自分でメモリ管理できるならば、
 JobSystemとBurstだけ活用するのも良い選択肢
Advertisement