[翻訳・記事紹介]Roblox開発におけるパフォーマンス最適化について Part4(ペットと物理演算)

参照元リンク:https://devforum.roblox.com/t/real-world-building-and-scripting-optimization-for-roblox/3127146

前回の記事はこちら

Part4では記事のChapter6から最終章のChapter7までを紹介します。


Chapter6 : ペットと物理演算

ロブロックスのゲームにおけるペットは、通常はプレイヤーを追いかけながら風景の上を滑るように動く、浮いている箱のバリエーションです。

このプロジェクトでは、最大12人のサーバーで1人のプレイヤーにつき30匹のペットのため、12人のサーバーでは合計360匹のペットが表示されることになります。

そこで採用した方法は、第5章でのコインの実装と似たもので、ペットはサーバー上ではデータテーブルとしてのみ存在し、リモートイベントによってペットの追加、削除、制御が行われます。

リモートイベントはペットに現在のタスク(状態)を割り当てるために使用され、移動の同期には全く帯域幅を消費しません

クライアント側では、レイキャストと数学を使ってペットの位置を計算し、毎フレームそのモデルを :PivotTo 関数で配置します。

注: ペットの簡単な実装方法として、衝突判定にフルのヒューマノイドリグを使用する方法がありますが、これはパフォーマンス(帯域幅とCPUの両方)の点で非常に負荷が大きいため、この安価な近似法が採用されています。


クライアントでのペットロジックはPetModuleに記載しています。

ペットのスポーンと削除

ペットは完全にRemoteEventで制御されています。プレイヤーはRemoteEventを通して他のプレイヤーのペットを含む、すべての生成や削除イベント、そしてペットの状態(何をしているのか)を知らされます。生成のライフサイクルは以下のようになります。

サーバー側:
• サーバーはプレイヤー追加されると、そのプレイヤーのペットのレコードを全て作成します。
• サーバーは、接続している全プレイヤーにそのペットの生成をリモートイベントを通じて知らせます。
• プレイヤーが離脱すると、ペットの削除メッセージが残りのプレイヤー全員に送信されます。

クライアント側:
• クライアントはペット生成イベントを受け取ると、ローカルにペットのレコードを作成し、ペットモデルを複製します。
 クライアントがペット削除イベントを受け取ると、それを破棄してレコードを削除します。

ペットの制御

ペットは基本的に次の2つの動作しか行いません: オーナーのキャラクターモデルにある「スロット」を追いかける(アタッチメントを使用)か、採掘スポットで掘るかのどちらかです。

クライアント側:
• プレイヤーが何かをクリックすると、そのターゲットイベントがサーバーに送信されます。

サーバー側:
• プレイヤーのターゲットイベントが妥当かどうかを処理し、問題がなければ、全プレイヤーにそのペットが採掘可能なオブジェクト(コインの山)にターゲットを変更するよう知らせます。
• 採掘可能なオブジェクトの採掘が終了すると、ペットにプレイヤーを再び追いかけるよう指示するイベントを送信します。

ペットの移動

このシステムではペットの移動は各デバイスで同期されません!通常、Robloxでパーツが動くと大量の帯域幅を消費しますが、数百のペットが動いている場合、それは許容できません。代わりに、ペットは現在のターゲットに基づいて移動し、そのフレームごとの移動はクライアント側で完全に計算されます。

各クライアントフレーム:
• クライアントは、現在のターゲットに基づいて各ペットの新しい位置を計算し、他のプレイヤーのペットも含めて処理します。
・ 各ペットは、レイキャストを使って地面からの高さを計算します。
• 各ペットは、現在の位置に対する動きのアニメーションオフセットを計算します。
• 最後に、各ペットは :PivotTo() 関数で新しい位置に移動されます。

最適化

完全にイベントベースのシステムであることに加えて、帯域幅のコストをほとんどかけないほか、ペットシステムの速度を向上させるいくつかの重要な最適化があります:

・ ペットは、前のフレーム以降に移動していない場合、レイキャストを行いません。
• ペットは、前のフレーム以降に新しいCFrameがない場合、:PivotTo() による移動処理をしません。
• ペットの背中にあるパワーレベルを表示するSurfaceGuiは、最大距離値が設定されており、素早く消えるようになっています。

Chapter6のまとめ

• ペットはRemoteEventを使って全体の動作を制御されており、同期されるパーツではないため、ほとんど帯域幅を消費しません。
• ペットはクライアント側でレイキャストと数学を使って動作し、CPUコストが非常に少なくなっています。
• 重い関数(:PivotTo() やレイキャストなど)を回避するための工夫がされており、さらに高速に実行されます。


Chapter7 : ボーナスエフェクト

マップの周りで、いくつかの小さな追加効果を見つけたかもしれません。これらはデモに必要なものではありませんが、あえて作りました。これらのエフェクトには、通常は何もしないパーツに「視覚的なスポット効果」を与えるために、CollectionService を使用しています。

以下に、いくつかのエフェクトの簡単な説明をまとめます。

水中の霧

霧の仕組みは非常にシンプルです。カメラが「Water」とタグ付けされたパーツの内部にある場合、霧がオンになり、それ以外の場合はオフになります。

水面のスクロール

水面エリアは、テクスチャインスタンスを使用してスクロールします。これもまた、CollectionService がすべての作業を担当しています。

揺れるウォータースパウト

こちらも同様のトリックで、sin() や cos() を使ってウォータースパウトのサイズを変化させています。これも CollectionService を利用しています。

Chapter7のまとめ

• CollectionService を使用して、マップにスポットエフェクトを追加します。
・この章は非常に短いため、特に詳細なまとめは必要ありませんでしたが、いくつか面白い効果が紹介されています。 :)

最後に

Cardboard Box Simulator」のツアーに参加していただき、ありがとうございます!

この記事で何かがきっかけとなり、将来のRobloxプロジェクトで高パフォーマンスを実現するためのヒントを得られたなら幸いです。

この記事は、2024年のRDCでの「最適化とパフォーマンス」に関する私のトークのパートナー記事です(YouTubeで見つけられます!)。そのトークでは、ドローコールやバッチ処理の仕組みについてさらに詳しく説明し、プロジェクトのメモリやインスタンス数、起動時間などの健康状態についても触れています。

2つの主要ポイント:

1. 事前に計画する - 高パフォーマンスなRobloxプロジェクトを作るには、特にポリゴン数やドローコールの予算を立ててから作業を始めると、より快適な作業ができます。
2. 定期的にテストする - プロジェクトの健康状態を定期的に確認しましょう。すべてのプロファイリングツールはライブゲームでも機能するので、実際の状況下で簡単にチェックできます。

Appendixes

Appendix A - リンク集

今回紹介に使用しているゲーム: Cardboard Box Simulator - Roblox 47

:PivotTo() 公式ドキュメント:PVInstance | Documentation - Roblox Creator Hub 8

:BulkMoveTo() 公式ドキュメント: WorldRoot | Documentation - Roblox Creator Hub 26

:GetPartBoundsInRadius() 公式ドキュメント: WorldRoot | Documentation - Roblox Creator Hub 10

CollectionService 公式ドキュメント: CollectionService | Documentation - Roblox Creator Hub 3

執筆者のSNSアカウント:x.com


Appendix B - Streaming Enabledの使用

StreamingEnabledの使用を強く検討するべき理由:
ロード時間とメモリ使用量の削減: StreamingEnabledは、読み込み時間とメモリ使用量を大幅に削減できます。
大きなマップの必要性: 大きなマップや広大なワールドを作成する場合、ほぼ必須です。
プレイヤー周辺の制限: プレイヤーの周りにバブルのような形でワールドを制限し、パフォーマンスを向上させます。
常に改善されている: Roblox側でも常に改良が進められています。

ただし、Cardboard Box SimulatorではStreamingEnabledを使用していません。これは、このマップが狭く、大きな仕切り壁があるため、視界の管理を手動で行うのが適しているからです。Robloxは距離カリングとStreamingEnabledの技術を組み合わせても非常に安定して動作するので、ぜひ試してみてください。


Appendix C - Drawcalls - 一体何なのか、またなぜ重要なのか

Robloxでドローコール(描画命令)を慎重に管理する必要があります。ドローコールはコストが高く、特に最新のスマートフォンでも処理速度が遅く、1フレームあたりに処理できる数に限界があります。

ドローコールの仕組み:
ドローコールはシーンレンダリングの一部として生成されます。エンジンが異なるタイプのオブジェクトを描画するたびに、新しいドローコールが生成され、時間的なコストが発生します。例えば、石のティーポットと木のボールが隣にあると、それぞれでドローコールが発生します。

Robloxのエンジンの最適化:
Robloxのエンジンはドローコールの最適化が非常に得意です。この最適化を理解しておくことで、シーンのパフォーマンス、特にモバイルデバイスでのパフォーマンスに大きな影響を与えることができます。

ドローコールを減らす簡単な方法:
• 同じMeshIdと素材を持つ不透明なメッシュは「ジオメトリインスタンシング」により、スケールや回転、位置、色が異なっていても1つのドローコールとしてバッチ処理されます。

注意が必要な点:
• パーティクルエミッターは、それぞれ1つのドローコールを消費します。40個のパーティクルエミッターが画面に表示されると、それだけで40回のドローコールが発生します。

その他の要因:
• 透過オブジェクト(Part.Transparency)は予想外のドローコールを引き起こすことがあります。
• ユニークなTextureIdやDecalIdは、それぞれの素材としてカウントされ、独自のドローコールを生成します。
• SurfaceGuiやBillboardGuiはドローコールを急速に消費します(内蔵の距離カリングを活用しましょう)。
• ビームやパーティクル効果は、それぞれ1つのドローコールを消費します。


Appendix D - マイクロプロファイラー

MicroProfilerは、Robloxの中でも最も高度な機能かもしれません。エンジンがどのように動作しているのか、各フレームでどれだけの処理時間がかかっているかを詳細に把握することができます。

ありがたいことに豊富な公式ドキュメントがあります:MicroProfiler | Documentation - Roblox Creator Hub
特にtag libraryはそれぞれのラベルについて説明されていて、要確認です:Tag Reference | Documentation - Roblox Creator Hub

マイクロプロファイラーへのアクセス方法:
• デスクトップやStudioでは、CTRL + F6でアクセス可能。
• モバイルでは、コンソールからアクセスし、表示されるIPアドレスをブラウザで開きます。

特に気をつけるべきは、スクリプトや物理演算で期待以上に時間がかかっている部分や、フレームごとにRoblox APIを何度も呼び出しているループなどです。


Appendix E -メモリプロファイラー

メモリプロファイラーを使えば、クライアントメモリをリアルタイムで把握することができます。F9キーでコンソールを開き、クライアントメモリを確認可能です。

またより詳細なメモリプロファイラーはここから確認できます。

注意:これらのプロファイラーは多くのプラグインなどが使用されているStudio環境ではあまり参考になりません。

メモリプロファイラーは何度も更新されており、最新の状況を確認するようにしてください。公式ドキュメント:Memory Usage | Documentation - Roblox Creator Hub 8

tipsとして以下の画像の箇所をクリックし、サイズごとに並び替えすることで管理しやすくできます。


Appendix F - ShadowMap Vs Future-Is-Bright (Future)

このプロジェクトでは、[ShadowMap]ライティング技術を使用しています。

  Future の利点は、より複雑なライティングとシェーディングを可能にしますが、ShadowMapよりも動作が重くなることがあります。特に影をキャストするライトを追加することに注意が必要で、ドローコールや三角形数が増加します。
 FutureShadowMapでは、影をどのように扱うかで大きな違いがあり、Ambient occlusionの見た目が違って見えたり、金属や光沢感はFutureの方がよりそれらしい見た目になります。 

 今回のプロジェクトでは、特にスマートフォンでのパフォーマンスが若干優れているShadowMapを採用しました。ただし、屋内での複雑なゲームでは、Futureが推奨されます。

いいなと思ったら応援しよう!