このページでは、一般的なパフォーマンスの問題と、それを軽減するためのベストプラクティスについて説明します。
スクリプト計算
Luauコードにおける高コストな操作は処理に時間がかかり、フレームレートに影響を与える可能性があります。並行実行でない限り、Luauコードは同期的に実行され、スレッドがスレッドを yield する関数に遭遇するまでメインスレッドをブロックします。
一般的な問題
テーブル構造への集中的な操作 - シリアル化、デシリアル化、ディープクローンなどの複雑な操作は、高いパフォーマンスコストがかかり、特に大きなテーブル構造ではその傾向が顕著です。これらの操作が再帰的である場合や非常に大きなデータ構造を反復処理する場合は特にそうです。
高頻度のイベント - RunServiceのフレームベースのイベントに高コストな操作を結びつけ、頻度を制限しないと、これらの操作は毎フレーム繰り返され、不必要な計算時間の増加を招くことがよくあります。これらのイベントには以下が含まれます:
軽減策
- RunServiceイベントでのコードの呼び出しは必要最小限にし、高頻度の呼び出しが必須でない場合(例:カメラの更新)には他のイベントで実行したり、ループ内でより低頻度で実行してください。
- task.wait()を使用して、大きなまたは高コストなタスクを分割し、作業を複数のフレームに分散させます。
- 不必要に高コストな操作を特定して最適化し、データモデルにアクセスする必要がない高コストな計算作業にはmultithreadingを使用します。
- 特定のサーバーサイドスクリプトは、単純なフラグでスクリプトをバイトコードではなく機械語にコンパイルするnative code generationの恩恵を受けることができます。
MicroProfilerスコープ
| スコープ | 関連する計算 |
| RunService.PreRender | PreRenderイベントで実行されるコード |
| RunService.PreSimulation | Steppedイベントで実行されるコード |
| RunService.PostSimulation | Heartbeatイベントで実行されるコード |
| RunService.Heartbeat | Heartbeatイベントで実行されるコード |
スクリプトのデバッグにMicroProfilerを使用する際の詳細については、特定のコードをタグ付けし、さらに特異性を高めるための関数を含むdebugライブラリを参照してください。スクリプトによって呼び出される多くのRoblox APIメソッドにも、役立つ信号を提供する独自のMicroProfilerタグがあります。
スクリプトのメモリ使用量
メモリリークは、使用されなくなったときにガーベジコレクタが正しく解放できないメモリを消費するスクリプトを書いた場合に発生する可能性があります。リークは、サーバー上で特に蔓延しやすく、数日間にわたってオンラインのままとなることができる一方で、クライアントセッションははるかに短いためです。
Developer Consoleにおける以下のメモリ値は、さらなる調査が必要な問題を示す可能性があります:
- LuaHeap - 高いまたは成長する消費は、メモリリークを示唆します。
- InstanceCount - 一貫して増加するインスタンス数は、コード内にある特定のインスタンスへの参照がガーベジコレクトされていないことを示唆します。
- PlaceScriptMemory - スクリプトごとのメモリ使用量の内訳を提供します。
一般的な問題
接続されたコネクションを残すこと - エンジンはインスタンスに接続されたイベントがガーベジコレクトされず、接続されたコールバック内の参照された任意の値も同様です。したがって、イベントと接続されたインスタンス、接続された関数、および参照された値のアクティブコネクションは、イベントが発火した後でもメモリガーベジコレクタのスコープ外になります。
インスタンスが破棄されると、イベントは接続が解除されますが、一般的な誤りはこれがPlayerオブジェクトに適用されると思い込むことです。ユーザーがゲームを離れた後、エンジンは自動的にその代表的なPlayerオブジェクトおよびキャラクターモデルを破棄しないため、Playerオブジェクトおよびキャラクターモデルの下にあるインスタンスへの接続(例:CharacterAdded)は、スクリプトで切断しない限りメモリを消費し続けます。 これにより、数百人のユーザーがゲームに参加して離れるにつれてサーバー上で非常に有意義なメモリリークが発生する可能性があります。
テーブル - オブジェクトをテーブルに挿入したまま、不要になったときに削除しないと、メモリの無駄遣いが発生します。特に、ユーザーデータを追跡するテーブル(ユーザーが参加する時)ではそうです。たとえば、以下のコードサンプルでは、ユーザーが参加するたびに情報を追加するテーブルを作成します:
例local playerInfo = {}Players.PlayerAdded:Connect(function(player)playerInfo[player] = {} -- 情報end)不要になったときにこれらのエントリを削除しないと、テーブルはサイズが増え続け、より多くのユーザーがセッションに参加するにつれてメモリを消費します。このテーブルを反復処理するコードも、テーブルのサイズが増えるにつれて計算でのコストが増えます。
軽減策
メモリリークを防ぐために使用されているすべての値をクリーンアップするには:
すべての接続を切断する - コードベースを確認し、各接続が以下のいずれかの方法でクリーンアップされていることを確認します:
- Disconnect()関数を使用して手動で切断する。
- イベントが属しているインスタンスをDestroy()関数で破棄する。
- 接続が戻ってくるスクリプトオブジェクトを破壊します。
プレイヤーオブジェクトとキャラクターを離れた後に削除する - ユーザーが離れた後に自動的にプレイヤーオブジェクトとキャラクターモデルを破棄するために、Workspace.PlayerCharacterDestroyBehaviorを有効にします。代わりに手動でクリーンアップすることもできます:
例:プレイヤーとキャラクターのクリーンアップlocal Players = game:GetService("Players")Players.PlayerAdded:Connect(function(player)player.CharacterRemoving:Connect(function(character)task.defer(character.Destroy, character)end)end)Players.PlayerRemoving:Connect(function(player)task.defer(player.Destroy, player)end)
物理計算
過度な物理シミュレーションは、サーバーとクライアントの両方で、フレームごとの計算時間の増加の主な原因となる可能性があります。
一般的な問題
過度な物理タイムステップの頻度 - デフォルトでは、ステッピング動作は適応モードであり、物理は60 Hz、120 Hz、または240 Hzで実行され、物理メカニズムの複雑さに応じて変化します。
一定のモードを使用すると、物理の精度が向上し、すべての物理アセンブリが240 Hz(フレームごとに4回)でステップします。これにより、毎フレームの計算量が大幅に増加します。
シミュレーションされたオブジェクトの過度な数や複雑さ - シミュレーションされている3Dアセンブリが多ければ多いほど、毎フレームの物理計算にかかる時間が長くなります。ゲームにはシミュレーションが必要ないオブジェクトが含まれていたり、それほど多くの制約やジョイントが必要ないメカニズムがあることがあります。
過度に精密な衝突検出 - メッシュ部分には、異なるパフォーマンス影響レベルを持つさまざまなモードを提供するCollisionFidelityプロパティがあります。メッシュ部分の精密な衝突検出モードは、最も高価なパフォーマンスコストを持ち、エンジンが計算するのに時間がかかります。
軽減策
シミュレーションを必要としないパーツを固定する - 物理学に駆動される必要がないすべてのパーツ(例えば静的なNPC)を固定します。
適応物理ステッピングを使用する - 適応的ステッピングは、物理メカニズムの計算率を動的に調整し、物理の更新を一部のケースであまり頻繁に行わないようにします。
メカニズムの複雑さを減らす
- 可能な限り、アセンブリ内の物理的制約やジョイントの数を最小限に抑えます。
- 制限を適用したり、ラグドールの四肢が互いに衝突しないようにするためのノーコリジョン制約を適用したりして、メカニズム内での自己衝突を減らします。
メッシュの精密な衝突精度の使用を減らす
小さなものや、インタラクションがほとんどないオブジェクトには、ボックス精度を使用します。
小さから中サイズのオブジェクトには、形状に応じてボックスまたはハル精度を使用します。
大きくて非常に複雑なオブジェクトには、可能であれば目に見えないパーツを使用してカスタムの衝突を構築します。
衝突が不要なオブジェクトには、衝突を無効にしてボックスまたはハル精度を使用します。衝突幾何もメモリに保存されています。
スタジオでデバッグ目的で衝突幾何を描画するには、3Dビューポートの右上隅にあるビジュアリゼーションオプションウィジェットから衝突精度をオンにします。
代わりに、ExplorerにCollisionFidelity=PreciseConvexDecompositionフィルタを適用すると、精密精度を持つすべてのメッシュパーツのカウントが表示され、それらを簡単に選択できます。
精密さとパフォーマンス要求のバランスを取る衝突精度オプションの選択方法に関する詳細な手順は、物理およびレンダリングパラメータの設定を参照してください。
MicroProfilerスコープ
| スコープ | 関連する計算 |
| physicsStepped | 全体の物理計算 |
| worldStep | 各フレームでの離散物理ステップ |
物理メモリ使用量
物理的な動きや衝突検出はメモリを消費します。メッシュパーツには、そのメッシュの衝突境界を評価するために使用されるアプローチを決定するCollisionFidelityプロパティがあります。
一般的な問題
デフォルトおよび精密な衝突検出モードは、他の2つのモードに比べて著しく多くのメモリを消費します。
PhysicsPartsにおいて高いメモリ消費レベルが見られる場合、ゲーム内のオブジェクトの衝突精度を減らす必要があるかもしれません。
軽減策
衝突精度に対するメモリの使用を減らすには:
- CollisionFidelity設定を使用して衝突の精度を減らします。Boxはメモリオーバーヘッドが最も少なく、DefaultおよびPreciseは一般的にコストが高いです。
- 小さな固定パーツの衝突精度をBoxに設定するのは一般的に安全です。
- 非常に複雑な大きなメッシュには、ボックス衝突精度を使用して小さなオブジェクトから独自の衝突メッシュを構築することを検討してください。
ヒューマノイド
Humanoidは、プレイヤーキャラクターと非プレイヤーキャラクター(NPC)に幅広い機能を提供するクラスです。強力ですが、Humanoidはかなりの計算コストがかかります。
一般的な問題
- NPCにすべてのHumanoidStateTypeを有効にしたままにする - 特定のHumanoidStateTypesを有効にしたままにすることにはパフォーマンスコストがあります。NPCに必要ないものは無効にしてください。たとえば、NPCが梯子を登ることがない場合、Climbingステートを無効にしても安全です。
- 必要のない場合にHumanoidを使用する - 静的なNPCは動かないので、一般的にHumanoidクラスを必要としません。
- サーバーから大規模なNPCのアニメーションを再生する - サーバー上で動作するNPCのアニメーションは、サーバーでシミュレーションされ、クライアントに複製される必要があります。これは不必要なオーバーヘッドを引き起こす可能性があります。
- 不必要なサイズやスケールの変更を行う - サイズ/スケールの変更はFastClusterの再構築を引き起こします。これは、FastClusterに関連するパフォーマンスの問題が発生している場合には、ゲームプレイ中にこれを減らすようにしましょう。同様に、他のプロパティの変更もFastClusterの再構築を引き起こす可能性があるため、一般的にこれらの変更は可能な限り減らすべきです。
軽減策
- NPCのアニメーションをクライアントで再生する - 多数のNPCを含むゲームでは、クライアントでAnimatorを作成し、ローカルでアニメーションを実行することを検討してください。これにより、サーバーの負荷と不必要な複製の必要が軽減されます。また、(キャラクターに近いNPCのアニメーションのみを再生するなどの)追加の最適化を実現する可能性もあります。
- ヒューマノイドの代わりにパフォーマンス向上のための代替手段を使用する - NPCモデルにはヒューマノイドオブジェクトが必要なわけではありません。
- 静的なNPCには、動く必要はないため、単純なAnimationControllerを使用します。
- 動きのあるNPCには、自分の移動コントローラーを実装し、NPCの複雑さに応じてアニメーション用にAnimationControllerを使用することを検討してください。
- 未使用のヒューマノイド状態を無効にする - Humanoid:SetStateEnabled()を使用して、各ヒューマノイドに必要な状態のみを有効にします。
- 頻繁に再生成されるNPCモデルをプールする - NPCを完全に破棄するのではなく、不活性NPCのプールに送ります。これにより、新しいNPCを再生成する必要があるときにプールからNPCの1つを再アクティブ化するだけで済みます。このプロセスはプーリングと呼ばれ、キャラクターをインスタンス化する回数を最小限に抑えます。
- ユーザーが近くにいるときだけNPCをスポーンさせる - ユーザーが範囲内にいないときにはNPCをスポーンさせず、ユーザーが範囲を離れたときにはそれをクリンアップします。
- インスタンス化された後にアバター階層に変更を加えないこと - アバター階層に対する特定の変更は、顕著なパフォーマンス影響を与えます。利用可能な最適化には次のものがあります:
- カスタムプロシージャアニメーションの場合、JointInstance.C0およびJointInstance.C1プロパティを更新しないでください。代わりに、Motor6D.Transformプロパティを更新します。
MicroProfilerスコープ
| スコープ | 関連する計算 |
| stepHumanoid | ヒューマノイド制御および物理 |
| stepAnimation | ヒューマノイドおよびアニメーターのアニメーション |
| updateInvalidatedFastClusters | アバターのインスタンス化または変更に関連 |
レンダリング
クライアントが各フレームで過ごす時間の大部分は、現在のフレームでシーンをレンダリングすることに費やされます。サーバーはレンダリングを行わないため、このセクションはクライアント専用です。
描画呼び出し
描画呼び出しとは、エンジンからGPUに何かをレンダリングするための指示のセットです。描画呼び出しには大きなオーバーヘッドがあります。一般に、フレームごとの描画呼び出しが少ないほど、フレームをレンダリングするために費やされる計算時間が少なくなります。
現在の描画呼び出しの数を確認するには、スタジオでRender Stats ⟩ Timingを表示します。クライアントでは、ShiftF2を押すことでRender Statsを表示できます。
シーンで指定されたフレーム内で描画しなければならないオブジェクトの数が多いほど、GPUへの描画呼び出しは増加します。ただし、Robloxエンジンは、一致するテクスチャ特性を持つ同一メッシュを単一の描画呼び出しにまとめるためのプロセスである_instancing_を利用します。具体的には、次の条件を満たす場合、MeshContentが同じ複数のメッシュが1つの描画呼び出しで処理されます:
- SurfaceAppearancesが存在する場合、同一である場合、そうでない場合はTextureContentsが同一である場合。
- SurfaceAppearanceおよびMeshPart.TextureIDが存在しない場合、材料が同一である。
その他の一般的な問題
過度のオブジェクト密度 - 特定の場所に高密度で多数のオブジェクトが集中している場合、そのシーンのレンダリングにはより多くの描画呼び出しが必要になります。特定のマップの一部を見るときにフレームレートが低下する場合、このエリアのオブジェクトの密度が高すぎるという良い信号です。
デカール、テクスチャ、およびパーティクルのようなオブジェクトは、バッチ処理がうまくいかず、追加の描画呼び出しを導入します。このようなオブジェクトタイプには特に注意してください。特に、ParticleEmittersへのプロパティ変更はパフォーマンスに劇的な影響を与える可能性があります。
インスタンス化の機会を見逃す - シーンには同じメッシュが何度も複製されることがよくありますが、各メッシュのメッシュやテクスチャのアセットIDが異なります。これにより、インスタンシングが妨げられ、不必要な描画呼び出しが発生します。
この問題の一般的な原因は、シーン全体を一度にインポートすることです。個々のアセットをRobloxにインポートし、インポート後に重複してシーンを組み立てるのではなく。
このようなシンプルなスクリプトでも、異なるメッシュIDを使用する同じ名前のメッシュパーツを特定するのに役立ちます:
for _,descendant in workspace:GetDescendants() doif descendant:IsA("MeshPart") thenprint(descendant.Name .. ", " .. descendant.MeshId)endend出力(Stack Linesが有効になっている場合)は、次のようになります。繰り返される行は、同じメッシュの再利用を示す良いものです。一意の行は必ずしも悪いわけではありませんが、命名スキームによってはゲーム内に重複したメッシュがあることを示す場合があります:
LargeRock, rbxassetid://106420009602747 (x144) -- goodLargeRock, rbxassetid://120109824668127LargeRock, rbxassetid://134460273008628LargeRock, rbxassetid://139288987285823LargeRock, rbxassetid://71302144984955LargeRock, rbxassetid://90621205713698LargeRock, rbxassetid://113160939160788LargeRock, rbxassetid://135944592365226 -- all possible duplicates過度のオブジェクトの複雑さ - 描画呼び出しの数ほど重要ではありませんが、シーン内の三角形の数はフレームをレンダリングするのにかかる時間に影響を与えます。非常に多くの非常に複雑なメッシュを含むシーンは一般的な問題であり、MeshPart.RenderFidelityプロパティが多くのメッシュでPreciseに設定されているシーンもあります。
過度のシャドウキャスティング - シャドウを処理することは高コストのプロセスであり、シャドウをキャストする光源オブジェクトや、シャドウの影響を受ける小さな部品の数が多いマップではパフォーマンスの問題が発生することがあります。
高い透明度オーバードロー - 部分的に透明なオブジェクトが近接して配置されると、エンジンは重なり合うピクセルを複数回レンダリングする必要があるため、パフォーマンスが低下します。この問題を識別して修正するための詳細については、レイヤー付き透明性を削除を参照してください。
不要なスキンメッシュパーツの移動 - ヒューマノイドなしのモデルの一部であるスキンメッシュパーツは、空間的に整理されたFastClustersを使用してグループ化されます。これらのメッシュパーツが移動すると、これらの空間クラスターに追加したり取り除いたりしなければならず、クラスターの再構築とパフォーマンスの影響を強制します。
- 効果的な回避策は、モデル内にヒューマノイドを埋め込むことです。ヒューマノイドが存在することで、デフォルトの空間クラスタリング動作がオーバーライドされ、モデル全体に単一の統一されたFastClusterの使用が強制されます。結果として、位置の更新はクラスターの再構築を必要としなくなるため、パフォーマンスボトルネックが軽減されます。この技法は、動きが予想されるメッシュパーツのみに留めておくべきです。メモリオーバーヘッドが発生する可能性があり、空間的最適化の利点を無効にすることがあるためです。このような変更を行った後は、常にゲームをプロファイリングすることをお勧めします。追加の情報については、ヒューマノイドのパフォーマンステクニックを参照してください。
Model内のパーツが多すぎる - モデル内にパーツが多すぎると、パーツのプロパティが変更される可能性があるため、再構築が頻繁にある場合があります。FastClusterを使用しているモデル内のパーツの適切なバランスを見つけることが重要です。
軽減策
同一メッシュのインスタンス化とユニークメッシュの数の削減 - 同一のメッシュが同じ基礎アセットIDを持つようにすることで、エンジンはそれらを単一の描画呼び出しで認識してレンダリングできるようになります。同じメッシュをマップ内でアップロードする際に一度だけ行い、その後スタジオで再利用のために複製してください。大規模なマップを一度にインポートすると、同一メッシュが異なるコンテンツIDを持ち、エンジンによってユニークなアセットとして認識される可能性があります。 パッケージはオブジェクトの再利用に役立つメカニズムです。
カリング - カリングは、最終的にレンダリングされるフレームに影響しないオブジェクトの描画呼び出しを排除するプロセスを指します。デフォルトでは、カメラの視界(フラスタムカリング)の外にあるオブジェクトや、他のオブジェクトによって視界から隠された部品、メッシュ、地形に対する描画呼び出しをエンジンはスキップします。屋内環境などの特定のシナリオでは、部屋またはポータルシステムを実装して、オブジェクトを手動でカリングして描画呼び出しや全体の計算負荷をさらに減らすことができるかもしれません。
モデルの詳細レベルを減らす - インスタンス ストリーミングを有効にし、ワールドモデルのLevelOfDetailプロパティをSLIMに設定して、カメラからの距離が増加するにつれて最適化された軽量なSLIMメッシュを画面にレンダリングします。
アバターの詳細レベルを減らす - インスタンスストリーミングを有効にし、Workspace.EnableSLIMAvatarsを設定して、カメラからの距離が増加するにつれて最適化された軽量SLIM表現としてプラットフォームアバターをレンダリングします。
レンダリング精度を減らす - MeshPart.RenderFidelityをAutomaticまたはPerformanceに設定します。これにより、メッシュはより複雑でない代替手段にフォールバックでき、描画する必要があるポリゴンの数を減少させることができます。
適切なパーツや光源オブジェクトでのシャドウキャスティングを無効にする - Robloxエンジンは、クライアントのグラフィック品質レベルが低下すると、影の品質を自動的に低下させ、最終的には品質レベルが4未満になると影を完全に無効にします。ただし、光源オブジェクトやパーツのシャドウキャスティングプロパティを選択的に無効にして、影を有効にしたままパフォーマンスを改善し、影が有効であり続ける可能性を高めます。編集時またはランタイム中に実行できる最適化の例は以下の通りです:
BasePart.CastShadowプロパティを使用して、小さなパーツからシャドウキャスティングを無効にします。この戦略は、ユーザーのカメラから遠く離れたパーツに適用すると特に効果的です。
可能な限り、動的なオブジェクトのシャドウを無効にします。
シャドウが必要でないオブジェクトの光源インスタンスでLight.Shadowsを無効にします。
光源インスタンスの範囲や角度を制限します。
光源インスタンスの数を減らします。
特定の範囲の外にある光を無効にするか、屋内環境の部屋ごとのベースで光を無効にすることを検討します。
MicroProfilerスコープ
| スコープ | 関連する計算 |
| Prepare and Perform | 全体のレンダリング |
| Perform/Scene/computeLightingPerform | 光グリッドとシャドウの更新 |
| LightGridCPU | ボクセル光グリッドの更新 |
| ShadowMapSystem | シャドウマッピング |
| Perform/Scene/UpdateView | レンダリング準備とパーティクルの更新 |
| Perform/Scene/RenderView | レンダリングおよび後処理 |
ネットワークと複製
ネットワークと複製は、サーバーと接続されたクライアント間でデータが送信されるプロセスを指します。各フレームごとにクライアントとサーバーの間で情報が送信されますが、より大きな情報の量は、より多くの計算時間を必要とします。
一般的な問題
過度のリモートトラフィック - 大量のデータをRemoteEventまたはRemoteFunctionオブジェクトを通じて送信したり、非常に頻繁に呼び出したりすることは、各フレームの受信パケット処理に大量のCPU時間が費やされる可能性があります。一般的な誤りには次のことが含まれます:
- 複製の必要がないデータを毎フレーム複製すること。
- 複製の必要がない状態でユーザー入力に応じてデータを複製すること。
- 必要以上のデータを送信すること。例えば、アイテムを購入した際にプレイヤーの全インベントリを送信するのではなく、購入したアイテムの詳細のみを送信することです。
複雑なインスタンスツリーの作成または削除 - サーバー上でデータモデルに変更が加えられると、接続されたクライアントに複製されます。これは、ランタイムでマップのような大規模なインスタンス階層を作成したり破棄したりすることがネットワークに非常に負担をかける可能性があります。
ここでの一般的な原因は、リグ内のAnimation Editorプラグインによって保存された複雑なアニメーションデータです。これらをゲームを公開する前に削除しないと、アニメーションモデルが定期的にクローンされ、不必要に大量のデータが複製されます。
サーバー側のTweenService - TweenServiceをサーバーでオブジェクトを補間するために使用すると、補間されたプロパティは毎フレーム各クライアントに複製されます。これは、クライアントのレイテンシーが変動するために、補間が揺れるだけでなく、不必要なネットワークトラフィックを引き起こします。
軽減策
不必要な複製を減らすために、次の戦略を採用できます:
- リモートイベントを介して大量のデータを一度に送信しない。必要なデータだけを低頻度で送信してください。たとえば、キャラクターの状態を変更されたときに複製し、毎フレームではなくすることができます。
- 複雑なインスタンスツリー(マップなど)をチャンク化し、複製作業を複数のフレームに分散させるために分割して読み込みます。
- アニメーションメタデータをクリーンアップします。特にリグのアニメーションディレクトリをインポート後に行います。
- 不要なインスタンス複製を制限する。特に、サーバーが生成されるインスタンスについて把握する必要がない場合。この場合には:
- 爆発や魔法の発射のような視覚効果。サーバーは結果を判断するための位置のみを知ればよく、クライアントはローカルで視覚エフェクトを生成できます。
- 一人称のアイテムビューモデル。
- クライアント側ではなくサーバー側でオブジェクトを補間します。
MicroProfilerスコープ
| スコープ | 関連する計算 |
| ProcessPackets | イベントの呼び出しやプロパティ変更など、受信ネットワークパケットの処理 |
| Allocate Bandwidth and Run Senders | サーバー上のアウトゴーイングイベント |
アセットメモリ使用量
クライアントメモリ使用量を改善するためのクリエイターにとって最も影響力のあるメカニズムは、インスタンスストリーミングを有効にすることです。
インスタンスストリーミング
インスタンスストリーミングは、必要ないデータモデルの部分を選択的に読み込み、これが読み込み時間を大幅に短縮し、メモリ圧力がかかるとクライアントのクラッシュを防ぐ能力を向上させることができます。
メモリの問題が発生しており、インスタンスストリーミングが無効になっている場合は、特に3D世界が大きい場合は、ゲームを更新してサポートすることを検討してください。インスタンスストリーミングは3D空間内の距離に基づいているため、大きなワールドでは自然により恩恵を受けることができます。
インスタンスストリーミングが有効になっている場合、その積極性を高めることができます。たとえば、次のことを検討してください。
- 可能な限りEnum.ModelStreamingMode.Persistentの使用を減らします。互換性の措置として使用している場合は、スクリプトを更新する必要があるかもしれません。
- Workspace.StreamingMinRadiusおよびWorkspace.StreamingTargetRadiusを減らします。
ストリーミングオプションとその利点に関する詳細については、ストリーミングプロパティを参照してください。
その他の一般的な問題
アセットの重複 - 同じアセットを複数回アップロードして異なるアセットIDを生成することが一般的な誤りです。これにより、同じコンテンツがメモリに複数回ロードされることがあります。
アセットのボリュームが過度に多い - アセットが同一でない場合でも、同じアセットを再利用してメモリを節約する機会を逃すことがあります。
オーディオファイル - オーディオファイルは、すべてをクライアントに一度に読み込まず、ゲームの一部分で必要なものだけを読み込む場合には、メモリ使用量の驚くべき寄与者となる可能性があります。戦略については読み込み時間を参照してください。
高解像度のテクスチャ - テクスチャのグラフィックスメモリ消費は、ディスク上のテクスチャのサイズとは無関係です。テクスチャ内のピクセル数がメモリ使用量を決定します。たとえば、1024x1024ピクセルのテクスチャは、512x512ピクセルのテクスチャの4倍のグラフィックスメモリを消費します。
Robloxにアップロードされた画像は固定フォーマットにトランスコードされるため、ピクセルあたりのバイト数が少ないカラーモデルで画像をアップロードすることによるメモリの利点はありません。同様に、アップロード前に画像を圧縮したり、不要な画像からアルファチャネルを削除したりして、ディスクサイズを減少させることができますが、メモリ使用量の改善にはつながりません。
ゲームが読み込まれると、エンジンは最初に低品質のテクスチャを使用し、その後、利用可能なデバイスメモリ、カメラからの距離、テクスチャが占める画面スペースなどに基づいて品質を上げていきます。それでも、テクスチャのサイズを戦略的に調整することで、ゲームのメモリ使用量を改善できます。
軽減策
アセットを一度だけアップロードする - オブジェクト全体で同じアセットIDを再利用し、特にメッシュや画像が重複してアップロードされないようにします。
重複アセットを見つけて修正する - 同一のメッシュパーツやテクスチャが複数回異なるIDでアップロードされていないか確認します。
- アセットの類似性を自動的に検出するAPIはありませんが、場所内のすべての画像アセットIDを収集し(手動またはスクリプトを使用)、ダウンロードして外部比較ツールを使用して比較できます。
- メッシュパーツの場合、ユニークなメッシュIDを取得し、サイズで整理して手動で重複を特定するのが最良の戦略です。
- 異なる色のために別々のテクスチャを使用するのではなく、単一のテクスチャをアップロードし、SurfaceAppearance.Colorプロパティを使用してさまざまな色合いを適用します。
マップ内のアセットを個別にインポートする - マップ全体を一度にインポートするのではなく、インポートし、個々のアセットを再構築します。インポータはメッシュの重複を行わないため、多数の別々の床タイルを必要とする大規模なマップを一度にインポートした場合、それぞれのタイルは別々のアセットとしてインポートされます(たとえ重複していても)。これはパフォーマンスやメモリの問題を引き起こす可能性があります。
画像のピクセルを必要以上に多く期待しない - 画面上で物理的に多くのスペースを占めていない限り、画像は通常最大でも512x512ピクセルを必要とします。ほとんどの小さな画像は256x256ピクセル未満であるべきです。
トリムシートを使用して 3Dマップでのテクスチャの再利用を最大化します。トリムシートの作成方法に関する手順と例については、トリムシートを作成するを参照してください。
スプライトシートを使用して多くの小さなUI画像を単一の画像として読み込むことを考慮することもできます。その後、ImageLabel.ImageRectOffsetおよびImageLabel.ImageRectSizeを使用してシートの部分を表示できます。
読み込み時間
多くのゲームはカスタム読み込み画面を実装し、ContentProvider:PreloadAsync()メソッドを使用してアセットをリクエストし、画像、音声、メッシュがバックグラウンドでダウンロードされるようにします。
このアプローチの利点は、ゲームの重要な部分がポップインなしに完全に読み込まれることを保証できることです。ただし、一般的な誤りは、このメソッドを過剰に利用して、実際に必要なアセットよりも多くをプリロードすることです。
悪い実践の例は、Workspace全体を読み込むことです。これにより、テクスチャのポップインを防ぐことができるかもしれませんが、読み込み時間が大幅に増加します。
同様の実践としては、ContentProvider.RequestQueueSizeを利用して、すべてのリクエストされたアセットが読み込みが完了することを保証することがあります。ただし、これには大幅に増加した読み込み時間が発生し、その変動性から信頼性のないメソッドです。
必要な状況でのみContentProvider:PreloadAsync()を使用してください。これには以下が含まれます:
- 読み込み画面内の画像。
- ボタンの背景やアイコンなど、ゲームメニュー内の重要な画像。
- 開始エリアやスポーンエリアにピックアップする重要なアセット。
大量のアセットを読み込む必要がある場合、スキップ読み込みボタンを提供することをお勧めします。