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. 05使用 MicroProfiler 检查

适用场景

  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
使用 task.wait() 而非 wait()中所有脚本
使用 MeshPart 而非 Union中需要许多独特形状时
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 库与旧版调度器

始终使用 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)

减少绘制调用

  • 将共享材质的部件合并为一个 MeshPart(从 Blender 导出为 .fbx)。
  • MeshPart 比 CSG Union 更适合批处理(Union 会在运行时重新三角化)。
  • 重复使用材质——10 个共享 SmoothPlastic 的部件,比 10 种独特纹理的开销小得多。
  • 在单个 MeshPart 上使用 TextureId,而不是在许多部件上叠加 Decal。

使用 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 个
高频率 ParticleEmitter降低 Rate 和 Lifetime;移出屏幕时禁用
高负载下使用 wait()替换为 task.wait()
在 Heartbeat 中使用 FindFirstChild 链式调用加载时缓存
大型地图未启用 StreamingEnabled启用它
到处都设置 Model.LevelOfDetail = Disabled在安全情况下使用 Automatic

常见错误

错误修复方法
每帧调用 workspace:FindFirstChild加载角色/模型时缓存引用
销毁并重新创建子弹/特效使用对象池
在紧密循环中使用 wait()使用 task.wait()
所有部件都使用独特材质统一使用少量共享材质
ParticleEmitter 在屏幕外仍处于启用状态粒子源不可见时将 Enabled 设为禁用
装饰性部件启用了物理效果设置 Anchored = true

评论

支持 Markdown