Successfully reported this slideshow.
Your SlideShare is downloading. ×

【Unite Tokyo 2019】たのしいDOTS〜初級から上級まで〜

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad

Check these out next

1 of 112 Ad

【Unite Tokyo 2019】たのしいDOTS〜初級から上級まで〜

Download to read offline

2019/9/25-6に開催されたUnite Tokyo 2019の講演スライドです。
安原 祐二(ユニティ・テクノロジーズ・ジャパン合同会社)

こんな人におすすめ
・そろそろDOTSを学んでおきたい方
・DOTSに取り組む余裕はないが現状を確認しておきたい方
・Unity.Physicsの概要を知りたい方

受講者が得られる知見
・DOTSの基本
・DOTSの内部構造
・Unity.Physicsの概要

Unityのイベント資料はこちらから:
https://www.slideshare.net/UnityTechnologiesJapan/clipboards

2019/9/25-6に開催されたUnite Tokyo 2019の講演スライドです。
安原 祐二(ユニティ・テクノロジーズ・ジャパン合同会社)

こんな人におすすめ
・そろそろDOTSを学んでおきたい方
・DOTSに取り組む余裕はないが現状を確認しておきたい方
・Unity.Physicsの概要を知りたい方

受講者が得られる知見
・DOTSの基本
・DOTSの内部構造
・Unity.Physicsの概要

Unityのイベント資料はこちらから:
https://www.slideshare.net/UnityTechnologiesJapan/clipboards

Advertisement
Advertisement

More Related Content

Slideshows for you (20)

Advertisement

More from UnityTechnologiesJapan002 (20)

Recently uploaded (20)

Advertisement

【Unite Tokyo 2019】たのしいDOTS〜初級から上級まで〜

  1. 1. たのしいDOTS 〜初級から上級まで〜 ユニティ・テクノロジーズ・ジャパン 安原 祐二
  2. 2. DOTSとは Data Oriented Technology Stack Unityの新しい基盤
  3. 3. DOTSとは Data Oriented Technology Stack Unityの新しい基盤 なぜ今なのか? データ指向って?
  4. 4. DOTSとは Data Oriented Technology Stack Unityの新しい基盤 現在はプレビュー版 これから使いやすくなる なぜ今なのか? データ指向って?
  5. 5. DOTSとは Data Oriented Technology Stack Unityの新しい基盤 現在はプレビュー版 いますぐ始めなくていい これから使いやすくなる なぜ今なのか? データ指向って? あとで見返そう
  6. 6. DOTSとは Data Oriented Technology Stack Unityの新しい基盤 現在はプレビュー版 いますぐ始めなくていい これから使いやすくなる この技術は おもしろい! なぜ今なのか? データ指向って? あとで見返そう
  7. 7. 前半: メモリの戦い 後半: 驚異のUnity Physics
  8. 8. メモリの戦い 〜その歴史〜
  9. 9. 限られた土地を使いまわそう
  10. 10. 役所 役所は確保状況を把握 ただし中身は関知しない 確保(allocation)
  11. 11. 確保(allocation)役所
  12. 12. 解放(free) 解放を報告 役所
  13. 13. 再利用 役所
  14. 14. メモリも ストレージも 事情は同じ
  15. 15. メモリも ストレージも 事情は同じ メモリは格段に複雑な事情を抱えている 利用者にアドレスを渡してしまっているので・・・
  16. 16. アドレスが埋まっている 問題は解放
  17. 17. 解放! !? 歴史上、さまざまな取り組みが 事故発生
  18. 18. 解放! C#「GCにお任せする」
  19. 19. 解放! C#「GCにお任せする」 マネージドメモリ (managed memory) 解放を呼ばせない
  20. 20. ポインタとはこれ  のこと マネージドメモリ (managed memory)
  21. 21. ポインタとはこれ  のこと int マネージドメモリ (managed memory) int a;*
  22. 22. マネージドメモリ (managed memory) ゲームでこんな複雑な必要あるだろうか?
  23. 23. Unmanaged Memoryが要求される マネージドメモリ (managed memory) Unmanaged Memory Chunk NativeContainer Chunk Chunk Chunk NativeContainer NativeContainer
  24. 24. ネイティブコンテナのトリック 〜Unmanagedに挑戦〜
  25. 25. 〜ネイティブコンテナ(NativeContainer)とは〜 Unmanaged Memory Chunk NativeContainer Unmanagedメモリを保持する機構 ただし自分で解放する必要
  26. 26. 〜ネイティブコンテナ(NativeContainer)とは〜 Unmanaged Memory Chunk NativeContainer ただし自分で解放する必要 オブジェクトではなく、アルゴリズムで確保する (生存期間をアルゴリズムで決める) 秩序は守られる! Unmanagedメモリを保持する機構
  27. 27. 代表例:NativeArray NativeArray structの配列 必ずstruct(値型)! class(参照型) はC#の機構により マネージドメモリになってしまう
  28. 28. 代表例:NativeArray NativeArray structの配列 必ずstruct(値型)! class(参照型) はC#の機構により マネージドメモリになってしまう var a = NativeArray<float>(8, Allocator.Persistent) C#の特徴を利用 structならメモリがひとかたまりに!
  29. 29. ネイティブコンテナもstruct リファレンスマニュアルより ところで struct って恐くないですか?
  30. 30. structの罠 aはコピーされてfuncに渡される var a = new Vector3(1, 2, 3); func(a); funcの中で変更してもaに影響なし
  31. 31. structの罠 aはコピーされてfuncに渡される var a = new Vector3(1, 2, 3); func(a); var a = new NativeArray<float>(32); func(a); aはコピーされてfuncに渡される あれ? これは大丈夫? funcの中で変更してもaに影響なし
  32. 32. ネイティブコンテナの実装は必ずポインタ! unsafe struct NativeArray<T> { void ptr; } ポインタをコピーしても参照先は同じ Unmanaged Memory var a = new NativeArray<float>(32); func(a); funcの変更が伝わる! *
  33. 33. ベテランでも引っかかるstructの罠 struct MyNativeArray<T> { NativeArray<T> na; int Length; } よーし ネイティブコンテナ 自作しちゃうぞー
  34. 34. ベテランでも引っかかるstructの罠 struct MyNativeArray<T> { NativeArray<T> na; int Length; } これは大失敗! よーし ネイティブコンテナ 自作しちゃうぞー なぜでしょう?
  35. 35. struct MyNativeArray<T> { NativeArray<T> na; int Length; } Unmanaged Memory Length Length var a = new MyNativeArray<float>(32); func(a);
  36. 36. struct MyNativeArray<T> { NativeArray<T> na; int Length; } Unmanaged Memory Length Length Lengthが 共有されてない!! var a = new MyNativeArray<float>(32); func(a); コンパイルエラーも出ないし プログラムは正しいけれど 100%バグになる
  37. 37. struct MyNativeArray<T> { NativeArray<T> na; int Length; } Unmanaged Memory Lengthも ポインタの先に置く Length ネイティブコンテナ の中は必ずポインタ! unsafe struct MyNativeArray<T> { void ptr; } *
  38. 38. 最重要!チャンクの設計 〜ゲームのためのメモリ〜
  39. 39. ゲームのオブジェクトを     で扱いたいUnmanaged Memory マネージドメモリ (managed memory) Unmanaged Memory Chunk NativeContainer Chunk Chunk Chunk NativeContainer NativeContainer
  40. 40. 現代はメモリが広い! マネージドメモリ (managed memory) Unmanaged Memory Chunk NativeContainer Chunk Chunk Chunk NativeContainer NativeContainer Unityの登場時(2005)とは事情が異なる
  41. 41. 現代はメモリが広い! メモリの再利用をギリギリまで追求する必要はない マネージドメモリ (managed memory) Unmanaged Memory Chunk NativeContainer Chunk Chunk Chunk NativeContainer NativeContainer Unityの登場時(2005)とは事情が異なる 増えたメモリは主にアセットに利用される ゲームロジックのメモリは大きく変わっていない
  42. 42. もし、確保するサイズが一律で同じだったら 確保・解放にまつわる 多くの問題が解決 ところで・・・
  43. 43. ある物体は3つのコンポーネントで構成されているとする CBA ABCはサイズが異なる
  44. 44. ある物体は3つのコンポーネントで構成されているとする CBA ABCはサイズが異なる この物体専用の領域を用意できれば・・・
  45. 45. ある物体は3つのコンポーネントで構成されているとする CBA CBA ある程度のメモリの無駄は許容する ABCはサイズが異なる 確保単位が一定に! この物体専用の領域を用意できれば・・・
  46. 46. A 必ず16KiB CB これがチャンクの姿 struct Entity CBA コンポーネント種別に並べる
  47. 47. 別の物体は4つのコンポーネントで構成 CB チャンクサイズ16KiBは変わらない A 割り振りがさっきと異なる このチャンクには3つ格納できる D 格納できる数が変わる
  48. 48. CB アーキタイプABCA 組み合わせをアーキタイプ(Archetype)と呼ぶ CBA D アーキタイプABCD アーキタイプABC用チャンク アーキタイプABCD用チャンク
  49. 49. 生成 A CB このチャンクはABCD用なので無関係! CBA 専用チャンクに格納(満杯ならもう一本追加)
  50. 50. あるオブジェクトを削除する場合 たったの2ステップ 削除 削除 削除 削除
  51. 51. チャンクに格納されているオブジェクト数を減らす 3→2 ステップ1
  52. 52. 最後の要素を消したい場所に上書きコピー 削除完了! ステップ2
  53. 53. 最後の要素を消したい場所に上書きコピー 削除完了! ステップ2 順序は保たれていない 決して歯抜けにならない
  54. 54. 他のオブジェクトに直接参照させない ねえねえ マネージャ 通して
  55. 55. 他のオブジェクトに直接参照させない マネージド Unmanaged Chunk NativeContainer Chunk Chunk Chunk NativeContainer NativeContainer ねえねえ マネージャ 通して
  56. 56. Systemの概念 〜データ指向とは〜
  57. 57. データは用意できた System Chunk Chunk Chunk Chunk Chunk Chunk Chunk Chunk どうやって処理させよう?
  58. 58. “System”を記述する Chunk Chunk Chunk Chunk Chunk Chunk Chunk Chunk System • チャンクを選ぶ • 入力と出力を決める • 処理する “System”の3つの働き
  59. 59. 使用コンポーネントを決める アーキタイプABC用チャンク アーキタイプABCD用チャンク アーキタイプBD用チャンク 条件:BDをもつチャンク query = GetEntityQuery(new EntityQueryDesc() { All = new ComponentType[] { ComponentType.ReadOnly<B>(), ComponentType.ReadWrite<D>(), }, });
  60. 60. 使用コンポーネントを決める アーキタイプABC用チャンク アーキタイプABCD用チャンク アーキタイプBD用チャンク 条件:BDをもつチャンク query = GetEntityQuery(new EntityQueryDesc() { All = new ComponentType[] { ComponentType.ReadOnly<B>(), ComponentType.ReadWrite<D>(), }, });
  61. 61. 使用コンポーネントを決める アーキタイプABC用チャンク アーキタイプABCD用チャンク アーキタイプBD用チャンク 条件:BDをもつチャンク query = GetEntityQuery(new EntityQueryDesc() { All = new ComponentType[] { ComponentType.ReadOnly<B>(), ComponentType.ReadWrite<D>(), }, }); アーキタイプBD用チャンク
  62. 62. 処理する System Bと Dを
  63. 63. 処理する System Bと Dを
  64. 64. 処理する バラバラに処理しているように見えるが System きっちりオブジェクト単位で渡っている
  65. 65. Jobを発行 メモリの連続が考慮されている Job Job Job System
  66. 66. 入出力を明示 Job Job Job B:入力(書き込まない) D:出力(書き込む) System
  67. 67. DSystem データの流れ B
  68. 68. DSystem データの流れ System B A Native Container ネイティブコンテナは アルゴリズムで使う
  69. 69. DSystem データの流れ System B A System Rotation Native Container ネイティブコンテナは アルゴリズムで使う
  70. 70. データに注目 Update { if (position.y > 0f) { rotation = objA.func(1f); return; } else if (position.y > 4f) { if (position.z > 0f) { m_C = objB.func(2f); } else m_C = objB.func(-1f); } rotation = m_C; } D System System B A System Rotation いつもの書き方 Native Container
  71. 71. Update { if (position.y > 0f) { rotation = objA.func(1f); return; } else if (position.y > 4f) { if (position.z > 0f) { m_C = objB.func(2f); } else m_C = objB.func(-1f); } rotation = m_C; } D System System B A System Rotation いつもの書き方 Native Container テストが 容易!
  72. 72. Update { if (position.y > 0f) { rotation = objA.func(1f); return; } else if (position.y > 4f) { if (position.z > 0f) { m_C = objB.func(2f); } else m_C = objB.func(-1f); } rotation = m_C; } D System System B A System Rotation いつもの書き方 Native Container テストが 容易!
  73. 73. データに注目 Update { if (position.y > 0f) { rotation = objA.func(1f); return; } else if (position.y > 4f) { if (position.z > 0f) { m_C = objB.func(2f); } else m_C = objB.func(-1f); } rotation = m_C; } D System System B A System Rotation いつもの書き方 Native Container
  74. 74. 参照はエンティティ 〜ポインタの代わりに〜
  75. 75. マネージドメモリ (managed memory) 参照の問題をいかにして解決するか
  76. 76. オブジェクトには必ずEntityがある チャンクにも入っている Entity Entity
  77. 77. 他のオブジェクトはEntityで参照する でも、Entityはポインタではない
  78. 78. Entityの正体はインデクス(ある配列中の位置) 4 5 5 8 2 5 8
  79. 79. Entityの正体はインデクス(ある配列中の位置) 4 5 5 8 2 5 8 1 2 3 4 5 チャンクポインタ チャンク内インデクス バージョン Entity 5 の情報 EntityManagerが持つテーブル
  80. 80. Entityの正体はインデクス(ある配列中の位置) 4 5 5 8 2 5 8 1 2 3 4 5 チャンクポインタ チャンク内インデクス バージョン Entity 5 の情報 EntityManagerが持つテーブル →ポインタ同様に高速
  81. 81. チャンクポインタ チャンク内インデクス バージョン 4 5 5 8 2 5 8 Entity 5 の情報 削除は? 削除されたインデクスが次の生成で使いまわされたらアウトだよね 1 2 3 4 5
  82. 82. チャンクポインタ チャンク内インデクス バージョン+1 4 5 5 8 2 5 8 Entity 5 の情報 削除は? 削除! 1 2 3 4 5
  83. 83. 5 1 実はEntityはインデクスとバージョン(計64bit) インデクス(Index) バージョン(Version)
  84. 84. 4 0 5 1 5 1 8 1 2 4 5 1 8 0 チャンクポインタ チャンク内インデクス バージョン+1 Entity 5 の情報 生成時に与えられた バージョン1が・・・ 削除時に増加して2になっているので・・ 1 2 3 4 5
  85. 85. チャンクポインタ チャンク内インデクス バージョン+1=2 4 1 5 1 5 1 8 1 2 1 5 1 8 1 Entity 5 の情報 あなたの知っている5番はもういません! バージョン1? このEntityの 情報ください 1 2 3 4 5
  86. 86. マネージドメモリ (managed memory) Unmanaged Memory Chunk NativeContainer Chunk Chunk Chunk NativeContainer NativeContainer Unmanagedな世界でも・・・ 速度を兼ね備えた見事な管理システムを実現
  87. 87. 驚異のUnity Physics
  88. 88. C#で物理シミュレーションを フル実装しちゃおうぜ 正気かしら
  89. 89. Unity Physics 誕生 ・決定的(deterministic) ・シミュレーションループ独立 こういうのを 待っていた! ・ステートレス
  90. 90. 通常の物理エンジン 約15fps Unity Physics 約60fps キューブ13824個 on high-end PC
  91. 91. ワールドを分離して自在にループ実行
  92. 92. https://github.com/Unity-Technologies/DOTS-Shmup3D-sample
  93. 93. Unity Physicsは素晴らしい! が、現時点(Ver.0.2.4)では気配りが足りない けっこう突き放してくる! これから使いやすくなっていきます
  94. 94. 注意点その1 静的なコライダーをスクリプトから動かす場合 コリジョンの更新は PhysicsVelocity があることが条件 AddComponentでPhysicsVelocityを付加しよう
  95. 95. 外力(AddForce)がない その代わり力積(ApplyLinearImpulse)がある = m dv dt dt = mdv 外力にdtを掛けて力積(ApplyLinearImpulse)を呼べばいい 外力 力積 注意点その2 F F
  96. 96. public static void ApplyLinearImpulse(ref this PhysicsVelocity velocityData, PhysicsMass massData, float3 impulse) { velocityData.Linear += impulse * massData.InverseMass; } 実装の確認は避けられない(現時点では) dv = Fdt 1 m つまり
  97. 97. public static void ApplyLinearImpulse(ref this PhysicsVelocity velocityData, PhysicsMass massData, float3 impulse) { velocityData.Linear += impulse * massData.InverseMass; } 実装の確認は避けられない(現時点では) Force velocityData.ApplyLinearImpulse(force*deltaTime); Impulse velocityData.ApplyLinearImpulse(impulse); Acceleration VelocityChange velocityData.Linear += acceleration*deltaTime; velocityData.Linear += velocity; ForceModeの対応: dv = Fdt 1 m つまり
  98. 98. public static void ApplyAngularImpulse(ref this PhysicsVelocity velocityData, PhysicsMass massData, float3 impulse) { velocityData.Angular += impulse * massData.InverseInertia; } 注意点その3 massData.InverseInertiaは行列ではなくベクトル AddTorqueは?
  99. 99. public static void ApplyAngularImpulse(ref this PhysicsVelocity velocityData, PhysicsMass massData, float3 impulse) { velocityData.Angular += impulse * massData.InverseInertia; } 注意点その3 massData.InverseInertiaは行列ではなくベクトル 直交座標系の回転要素がない慣性テンソル AddTorqueは? (対角化された状態)
  100. 100. public static void ApplyAngularImpulse(ref this PhysicsVelocity velocityData, PhysicsMass massData, float3 impulse) { velocityData.Angular += impulse * massData.InverseInertia; } 注意点その3 慣性テンソル空間でトルクを与える必要 massData.InverseInertiaは行列ではなくベクトル AddTorqueは? (対角化された状態) 直交座標系の回転要素がない慣性テンソル
  101. 101. public static void ApplyAngularImpulse(ref this PhysicsVelocity velocityData, PhysicsMass massData, float3 impulse) { velocityData.Angular += impulse * massData.InverseInertia; } 注意点その3 慣性テンソル空間でトルクを与える必要 特殊な形状でなければAddRelativeTorqueと考えて良い massData.InverseInertiaは行列ではなくベクトル AddTorqueは? (対角化された状態) 直交座標系の回転要素がない慣性テンソル
  102. 102. 参考:慣性テンソルについての解説動画 https://learning.unity3d.jp/1167/ 〜Unity道場〜 物理シミュレーション完全マスター 34:20から
  103. 103. 注意点その4 軸単位の拘束(Constraint)の実現方法 拘束しないとこうなるよね
  104. 104. 物体の位置・姿勢の更新は すべてApply*Impulse経由 public static void ApplyAngularImpulse(ref this PhysicsVelocity velocityData, PhysicsMass massData, float3 impulse) { velocityData.Angular += impulse * massData.InverseInertia; } という実装になってるので
  105. 105. 物体の位置・姿勢の更新は すべてApply*Impulse経由 InverseInertia public static void ApplyAngularImpulse(ref this PhysicsVelocity velocityData, PhysicsMass massData, float3 impulse) { velocityData.Angular += impulse * massData.InverseInertia; } の設定で回転拘束を実現 例: new PhysicsMass { … InverseInertia = new float3(0, 1, 0); … } Y軸以外の角速度を 発生させない という実装になってるので 慣性テンソルに回転がある場合は注意
  106. 106. まとめ
  107. 107. • ゲーム実装を強く意識   汎用的だったメモリ管理をゲームの特徴に合わせる DOTSの必然性
  108. 108. • ゲーム実装を強く意識   汎用的だったメモリ管理をゲームの特徴に合わせる DOTSの必然性 • 現代のCPUを強く意識   64bit CPUを前提にできつつある
  109. 109. • ゲーム実装を強く意識   汎用的だったメモリ管理をゲームの特徴に合わせる DOTSの必然性 • 現代のCPUを強く意識   64bit CPUを前提にできつつある • テスタビリティ・スケーラビリティを強く意識   データの一意性+マルチコア実行
  110. 110. • ゲーム実装を強く意識   汎用的だったメモリ管理をゲームの特徴に合わせる DOTSの必然性 DOTSはどんどん使いやすくなっていく そのときに始めれば十分 ご期待ください! • 現代のCPUを強く意識   64bit CPUを前提にできつつある • テスタビリティ・スケーラビリティを強く意識   データの一意性+マルチコア実行
  111. 111. おしまい

×