mcpField
R
roblox-performance

roblox-performance

github.com/sentinelcore開発29847 回インストール36 週間前に更新
リポジトリ

roblox-performance について

Roblox 開発者向けのパフォーマンス最適化ガイドです。ワールドストリーミング、フレームループの効率化、オブジェクト再利用、描画・物理負荷の調整と MicroProfiler による分析を扱います。

主な機能

  1. 01大規模ワールド向け StreamingEnabled
  2. 02フレームループのキャッシュと間引き
  3. 03オブジェクトプールと LOD
  4. 04描画呼び出しと物理負荷の削減
  5. 05MicroProfiler による調査

活用シーン

  1. 01大規模 Roblox マップの最適化
  2. 02オブジェクトの反復生成の削減
  3. 03フレームレート低下の原因調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/sentinelcore/roblox-skills --skill roblox-performance

インストール後、Claude Code で/roblox-performanceを入力して読み込まれたか確認します。

詳細

Roblox パフォーマンス最適化

クイックリファレンス

テクニック効果使用すべき場面
StreamingEnabled高大規模なオープンワールド
オブジェクトプーリング高頻繁な生成・破棄
ループ外での参照キャッシュ高Heartbeat/RenderStepped
wait() ではなく task.wait()中すべてのスクリプト
Union ではなく MeshPart中多数のユニークな形状
LOD(距離による非表示)中複雑なモデル
静的パーツのアンカー固定中物理演算の負荷を削減
PointLight の制限高ライトが多いシーン全般

StreamingEnabled

大規模なワールドで有効化します — エンジンは付近のパーツだけをクライアントに送信します。

-- Studio: Workspace > StreamingEnabled = true
workspace.StreamingEnabled = true
workspace.StreamingMinRadius = 64
workspace.StreamingTargetRadius = 128
  • 半径外のパーツはクライアント上では nil になります — 必ず if part then でガードしてください。
  • 常に存在する必要があるモデルには Model.LevelOfDetail = Disabled を設定します。
  • カットシーンやテレポートの前にエリアを事前ストリーミングします:
workspace:RequestStreamAroundAsync(targetPosition, 5) -- 5s timeout

ホットパスループの最適化

RunService.Heartbeat と RenderStepped は毎フレーム(約60回/秒)発火します。処理は軽量に保ちましょう。

悪い例 — 毎フレーム階層を検索する
RunService.Heartbeat:Connect(function()
    local char = workspace:FindFirstChild(player.Name)
    local humanoid = char and char:FindFirstChild("Humanoid")
    if humanoid then humanoid.WalkSpeed = 16 end
end)
良い例 — 参照を一度キャッシュし、必要なときだけ処理する
local humanoid = nil

Players.LocalPlayer.CharacterAdded:Connect(function(char)
    humanoid = char:WaitForChild("Humanoid")
end)

RunService.Heartbeat:Connect(function(dt)
    if not humanoid then return end
    humanoid.WalkSpeed = 16  -- cached reference, no search
end)

ルール:

  • game:GetService() とパーツの参照はループの外でキャッシュします。
  • Heartbeat の中で FindFirstChild、GetChildren、GetDescendants を絶対に呼び出さないでください。
  • 毎フレーム必要のない処理はスロットリングします:
local TICK_INTERVAL = 0.5
local elapsed = 0

RunService.Heartbeat:Connect(function(dt)
    elapsed += dt
    if elapsed < TICK_INTERVAL then return end
    elapsed = 0
    -- expensive work here, runs 2×/sec instead of 60×/sec
end)

task ライブラリ vs レガシースケジューラ

常に task を使用してください — wait() と spawn() は負荷時にスロットリングされ、非推奨です。

レガシーモダン
wait(n)task.wait(n)
spawn(fn)task.spawn(fn)
delay(n, fn)task.delay(n, fn)
coroutine.wrap(fn)()task.spawn(fn)

オブジェクトプーリング

毎フレームインスタンスを生成・破棄するのではなく、再利用します。

-- ObjectPool ModuleScript
local ObjectPool = {}
ObjectPool.__index = ObjectPool

function ObjectPool.new(template, initialSize)
    local self = setmetatable({ _template = template, _available = {} }, ObjectPool)
    for i = 1, initialSize do
        local obj = template:Clone()
        obj.Parent = nil
        table.insert(self._available, obj)
    end
    return self
end

function ObjectPool:Get(parent)
    local obj = table.remove(self._available) or self._template:Clone()
    obj.Parent = parent
    return obj
end

function ObjectPool:Return(obj)
    obj.Parent = nil
    table.insert(self._available, obj)
end

return ObjectPool
-- Usage
local pool = ObjectPool.new(ReplicatedStorage.Bullet, 20)

local function fireBullet(origin)
    local bullet = pool:Get(workspace)
    bullet.CFrame = CFrame.new(origin)
    task.delay(3, function() pool:Return(bullet) end)
end

レベルオブディテール(LOD)

ビルトイン: Model.LevelOfDetail = Automatic を設定します — エンジンが遠くのパーツを自動的にインポスター(代替)メッシュに統合します。

手動の距離ベース LOD:

-- LocalScript
local INTERVAL = 0.2
local LOD_DISTANCE = 150
local elapsed = 0

RunService.Heartbeat:Connect(function(dt)
    elapsed += dt
    if elapsed < INTERVAL then return end
    elapsed = 0

    local dist = (workspace.CurrentCamera.CFrame.Position - model.PrimaryPart.Position).Magnitude
    local visible = dist < LOD_DISTANCE
    for _, v in model:GetDescendants() do
        if v:IsA("BasePart") then
            v.LocalTransparencyModifier = visible and 0 or 1
        end
    end
end)

ドローコールの削減

  • 同じマテリアルを共有するパーツを 1 つの MeshPart に統合します(Blender から .fbx としてエクスポート)。
  • MeshPart は CSG Union よりも効率的にバッチ処理されます(Union は実行時に再トライアングル化されます)。
  • マテリアルを再利用します — SmoothPlastic を共有する 10 個のパーツは、10 個のユニークなテクスチャよりもはるかに低コストです。
  • 多数のパーツに Decal を重ねるのではなく、単一の MeshPart で TextureId を使用します。

MicroProfiler によるプロファイリング

  1. ゲーム内で Ctrl+F6 を押して MicroProfiler を開きます。
  2. Ctrl+P を押して一時停止し、単一フレームを検査します。
  3. heartbeatSignal(Lua)、physicsStepped(物理演算)、render(GPU)内の幅の広いバーを探します。
  4. 自分のコードにラベルを付けます:
RunService.Heartbeat:Connect(function()
    debug.profilebegin("MySystem")
    -- your code
    debug.profileend()
end)

よくある FPS 低下の原因

原因修正方法
何千もの個別パーツMeshPart に統合する
アンカー固定されていない静的ジオメトリ決して動かないものには Anchored = true を設定する
多数の PointLight / SpotLight インスタンスエリアごとに動的ライトを約 10〜20 個に制限する
高レートの ParticleEmitterRate と Lifetime を下げる;画面外では無効化する
高負荷時の wait()task.wait() に置き換える
Heartbeat 内の FindFirstChild チェーンロード時にキャッシュする
大規模マップで StreamingEnabled がオフ有効化する
すべての場所で Model.LevelOfDetail = Disabled安全な場所では Automatic を使用する

よくある間違い

間違い修正方法
毎フレームの workspace:FindFirstChildキャラクター/モデルのロード時に参照をキャッシュする
弾やエフェクトの破棄と再生成オブジェクトプールを使用する
タイトなループ内での wait()task.wait()
すべてのパーツにユニークなマテリアル共有マテリアルの少数のセットに標準化する
画面外で有効な ParticleEmitterパーティクルソースが表示されていないときは Enabled を無効化する
装飾用パーツでの物理演算Anchored = true

コメント