このページでは、一般的なパフォーマンスの問題とそれを軽減するためのベストプラクティスについて説明します。
スクリプト計算
Luau コード内の高コストな操作は処理に時間がかかり、フレームレートに影響を与える可能性があります。並行して実行されていない限り、Luau コードは同期的に実行され、スレッドがスリープする関数に遭遇するまでメインスレッドをブロックします。
一般的な問題
- テーブル構造に対する集中的な操作 - シリアル化、デシリアル化、深いクローンなどの複雑な操作は、高いパフォーマンスコストを伴い、特に大きなテーブル構造に対してはその傾向が強いです。これらの操作が再帰的であったり、非常に大きなデータ構造を反復処理する場合は特にそうです。
- 高頻度のイベント - RunService のフレームベースのイベントに高コストな操作を結びつけ、頻度を制限しないと、これらの操作は毎フレーム繰り返され、計算時間が不必要に増加することがよくあります。これらのイベントには以下が含まれます:
軽減策
- RunService イベントでのコードの呼び出しは控えめに行い、高頻度の呼び出しが必須な場合(例えば、カメラの更新など)に限定します。ほとんどの他のコードは、他のイベントやループ内であまり頻繁に実行することができます。
- task.wait() を使用して、大きなまたは高コストなタスクを分割し、作業を複数のフレームに分散させます。
- 不必要に高コストな操作を特定して最適化し、データモデルにアクセスする必要のない計算コストの高いタスクには マルチスレッド を使用します。
- 特定のサーバーサイドスクリプトは、スクリプトをバイトコードではなくマシンコードにコンパイルする単純なフラグである ネイティブコード生成 の恩恵を受けることができます。
- 最適化後もサーバーの CPU が不足する場合は、コンピュート用の拡張サービス を使用して、より多くのリソースを予約できます。
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 などです。
適応物理ステッピングを使用する - 適応ステッピングは、物理メカニズムの物理計算のレートを動的に調整し、場合によっては物理の更新をより少なく行うことを可能にします。
メカニズムの複雑さを減らす
- 可能な限り、アセンブリ内の物理制約やジョイントの数を最小限に抑えます。
- ラグドールの手足が互いに衝突しないように、制限や衝突なしの制約を適用することで、メカニズム内の自己衝突を減らします。
メッシュの精密衝突精度の使用を減らす
ユーザーが違いに気づくことがほとんどない小さなオブジェクトや非インタラクティブなオブジェクトには、ボックス精度を使用します。
小〜中サイズのオブジェクトには、形状に応じてボックスまたはハル精度を使用します。
大きく非常に複雑なオブジェクトには、可能な限り目に見えないパーツを使用してカスタム衝突を構築します。
衝突を必要としないオブジェクトには、衝突を無効にし、ボックスまたはハル精度を使用します。衝突ジオメトリはメモリに保存されます。
精密さとパフォーマンス要件のバランスを取る衝突精度オプションの選択方法についての詳細な手順は、物理およびレンダリングパラメータの設定 を参照してください。
MicroProfiler スコープ
| スコープ | 関連する計算 |
| physicsStepped | 全体の物理計算 |
| worldStep | 各フレームで行われる離散的な物理ステップ |
物理メモリ使用量
物理の動きと衝突検出はメモリを消費します。メッシュパーツには、メッシュの衝突境界を評価するために使用されるアプローチを決定する CollisionFidelity プロパティがあります。
一般的な問題
デフォルトおよび精密衝突検出モードは、他の 2 つのモードよりもはるかに多くのメモリを消費します。
PhysicsParts の下で高いメモリ消費が見られる場合は、ゲーム内のオブジェクトの 衝突精度 を減らす必要があるかもしれません。
軽減策
衝突精度のために使用されるメモリを減らすには:
- CollisionFidelity 設定を使用して衝突の精度を減らします。Box は最も低いメモリオーバーヘッドを持ち、Default および Precise は一般的に高コストです。
- 小さな固定パーツの衝突精度を Box に設定するのは一般的に安全です。
- 非常に複雑な大きなメッシュの場合は、小さなオブジェクトでボックス衝突精度を持つ独自の衝突メッシュを構築することを検討してください。
ヒューマノイド
Humanoid は、プレイヤーおよび非プレイヤーキャラクター (NPC) に幅広い機能を提供するクラスです。強力ですが、Humanoid にはかなりの計算コストが伴います。
一般的な問題
- NPC にすべての HumanoidStateTypes を有効にしたままにする - 特定の HumanoidStateTypes を有効にしたままにするとパフォーマンスコストが発生します。NPC に必要のないものは無効にしてください。例えば、NPC が梯子を登る予定がない場合は、Climbing ステートを無効にするのが安全です。
- 必要ない場合にヒューマノイドを使用する - 動かない静的 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 アイテムで確認できます。クライアントで Render Stats を表示するには、ShiftF2 を押します。
シーン内で描画する必要のあるオブジェクトが多いほど、GPU へのドローコールが増えます。ただし、Roblox エンジンは インスタンシング と呼ばれるプロセスを利用して、同じテクスチャ特性を持つ同一のメッシュを単一のドローコールにまとめます。具体的には、同じ MeshContent を持つ複数のメッシュは、次の条件を満たす場合に単一のドローコールで処理されます:
- 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 を使用してシートの一部を表示できます。
読み込み時間
プレイヤーがプレイを開始する前に、クライアントは ReplicatedFirst と ReplicatedStorage のすべて、Workspace の初期コンテンツ、およびスクリプトが事前にロードするアセットを受信する必要があります。この時間にクライアントがダウンロードして処理しなければならないコンテンツが多いほど、プレイヤーの待機時間が長くなります。参加プロセスの各ステップの内訳については、パフォーマンスの問題を特定する を参照してください。
一般的な問題
インスタンスストリーミングが無効 - インスタンスストリーミング が無効になっていると、プレイヤーが入る前に Workspace 全体をクライアントに複製する必要があるため、多くのパーツ、モデル、アセットを持つゲームは相対的に長い読み込み時間を持ちます。
ReplicatedStorage 内のコンテンツが過剰 - ReplicatedStorage 内のすべては、プレイヤーが何かを見たり対話したりする前にすべてのクライアントに複製されます。クライアントがすぐに必要とするかどうかにかかわらず、フリーモデルや継承されたアセットは、静かに大きなボリュームを追加する可能性があります。
重い ReplicatedFirst コンテンツ - ReplicatedFirst は他の何よりも先に複製され、主にカスタム読み込み画面を保持するために存在します。これに追加のアセットやスクリプトがあると、メインの複製フェーズの開始が遅れます。
PreloadAsync() の過剰使用 - 多くのゲームはカスタム読み込み画面を実装し、ContentProvider:PreloadAsync() を使用してアセットを要求し、画像、音声、メッシュがバックグラウンドでダウンロードされるようにします。このアプローチは、ゲームの重要な部分が完全に読み込まれることを保証しますが、一般的な誤りは、実際に必要なアセットよりも多くを事前に読み込むことです。
悪い実践の例は、Workspace 全体を読み込むことです。これにより、テクスチャのポップインを防ぐことができますが、読み込み時間が大幅に増加します。
同様の実践は、すべての要求されたアセットが読み込みを完了したことを確認するために ContentProvider.RequestQueueSize を利用することです。ただし、これは読み込み時間が大幅に増加する同じ問題を引き起こし、その変動する性質のために信頼性の低い方法でもあります。
高コストなスタートアップスクリプト - LocalScripts で実行される重いスクリプト(大きなデータ構造を同期的に読み込むなど)は、複製からプレイ可能な状態への移行を遅らせます。
軽減策
インスタンスストリーミングを有効にする - ストリーミングは、プレイヤーのスポーンポイントの近くにあるワールドの部分のみを初期参加時に送信し、その後、プレイヤーがゲーム内を移動するにつれて残りを段階的にストリーミングします。まだ使用していないゲームにとって、ストリーミングは読み込み時間を短縮する最も効果的な方法の 1 つです。新しい場所ではデフォルトでストリーミングが有効になっています。セットアップ手順、プロパティリファレンス、および移行ガイダンスについては、インスタンスストリーミング を参照してください。
ReplicatedStorage をスリムに保つ - プレイヤーが参加したときに必要なものだけを保存します。例えば、LocalScripts が必要とする共有 ModuleScripts などです。すぐに必要でない大きなモデル、オーディオ、その他のアセットは ServerStorage に移動し、プレイヤーが必要なときにクライアントに移動します。定期的に ReplicatedStorage を監査して、もはや使用しないコンテンツを確認します。
ReplicatedFirst をスリムに保つ - ReplicatedFirst を、カスタム読み込み画面に必要なもの(大きなテクスチャや 3D モデルではなく基本的な UI 要素)に制限し、そこから実行されるスクリプトで重い計算を避けます。カスタム読み込み画面を構築する手順については、読み込み画面 を参照してください。
必要なアセットのみを事前に読み込む - ContentProvider:PreloadAsync() を必要な状況でのみ使用します。これには以下が含まれます:
- 読み込み画面の画像。
- ボタンの背景やアイコンなど、ゲームメニュー内の重要な画像。
- 開始またはスポーンエリア内の重要なアセット。
大量のアセットを読み込む必要がある場合は、スキップ読み込み ボタンを提供することをお勧めします。
非必須のスタートアップ作業を遅延させる - プレイヤーがまだ到達していないコンテンツ(開いていない UI パネルなど)を、最初に必要になったときにのみ読み込みます。早期に発生する必要がある初期化作業は、一度にすべてを実行するのではなく、複数のフレームに分散させます。テクニックについては、スクリプト計算 を参照してください。