optimization

Roblox Performance Optimization

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "optimization" with this command: npx skills add taozhuo/game-dev-skills/taozhuo-game-dev-skills-optimization

Roblox Performance Optimization

When optimizing games, follow these patterns for better performance across all devices.

Rendering Optimization

Part Count Reduction

-- Combine multiple parts into unions or meshes local function combineStaticParts(model) local parts = {} for _, part in ipairs(model:GetDescendants()) do if part:IsA("BasePart") and part.Anchored then table.insert(parts, part) end end

if #parts > 1 then
    local union = parts[1]:UnionAsync(parts, Enum.CollisionFidelity.Box)
    union.Name = model.Name .. "_Combined"
    union.Parent = model.Parent

    for _, part in ipairs(parts) do
        part:Destroy()
    end

    return union
end

end

-- Better: Use MeshPart for complex static geometry -- Import optimized meshes from Blender with proper LODs

Level of Detail (LOD)

local LODManager = {} local LOD_DISTANCES = {50, 100, 200} -- Distance thresholds

function LODManager.setup(model) local lodLevels = { model:FindFirstChild("LOD0"), -- Highest detail model:FindFirstChild("LOD1"), model:FindFirstChild("LOD2"), model:FindFirstChild("LOD3") -- Lowest detail }

local function updateLOD()
    local camera = workspace.CurrentCamera
    local distance = (model.PrimaryPart.Position - camera.CFrame.Position).Magnitude

    local activeLOD = 1
    for i, threshold in ipairs(LOD_DISTANCES) do
        if distance > threshold then
            activeLOD = i + 1
        end
    end

    for i, lod in ipairs(lodLevels) do
        if lod then
            lod.Visible = (i == activeLOD)
        end
    end
end

RunService.RenderStepped:Connect(updateLOD)

end

-- Automatic LOD using Roblox's built-in system local function setupAutomaticLOD(meshPart) -- RenderFidelity controls automatic LOD meshPart.RenderFidelity = Enum.RenderFidelity.Automatic

-- CollisionFidelity affects physics performance
meshPart.CollisionFidelity = Enum.CollisionFidelity.Box  -- Simplest

end

Streaming Enabled

-- Enable instance streaming for large worlds workspace.StreamingEnabled = true workspace.StreamingMinRadius = 64 -- Min loaded radius workspace.StreamingTargetRadius = 256 -- Target loaded radius workspace.StreamingIntegrityMode = Enum.StreamingIntegrityMode.Default

-- For important models that must always be loaded local importantModel = workspace.ImportantModel importantModel.ModelStreamingMode = Enum.ModelStreamingMode.Atomic -- Load together -- or importantModel.ModelStreamingMode = Enum.ModelStreamingMode.Persistent -- Always loaded

Texture Optimization

-- Use appropriate texture sizes -- Mobile: 256x256 or 512x512 -- Desktop: 512x512 or 1024x1024 max

-- Reduce unique materials local function consolidateMaterials(model) local materials = {} for , part in ipairs(model:GetDescendants()) do if part:IsA("BasePart") then local key = tostring(part.Material) .. "" .. tostring(part.Color) materials[key] = (materials[key] or 0) + 1 end end -- Identify and consolidate similar materials end

Script Optimization

Avoid wait() and Use task Library

-- BAD: Uses deprecated wait() wait(1) spawn(function() ... end) delay(1, function() ... end)

-- GOOD: Use task library task.wait(1) task.spawn(function() ... end) task.delay(1, function() ... end)

-- Even better: Use events when possible part.Touched:Wait() -- Yields until event fires

Event Connection Management

-- BAD: Memory leak - connection never disconnected part.Touched:Connect(function() -- This connection persists even after part is destroyed end)

-- GOOD: Store and disconnect connections local connection connection = part.Touched:Connect(function(hit) if someCondition then connection:Disconnect() end end)

-- BEST: Use Maid/Janitor pattern for cleanup local Maid = {} Maid.__index = Maid

function Maid.new() return setmetatable({_tasks = {}}, Maid) end

function Maid:GiveTask(task) table.insert(self._tasks, task) end

function Maid:Cleanup() for _, task in ipairs(self._tasks) do if typeof(task) == "RBXScriptConnection" then task:Disconnect() elseif typeof(task) == "Instance" then task:Destroy() elseif type(task) == "function" then task() end end self._tasks = {} end

Caching and Avoiding Repeated Lookups

-- BAD: Repeated FindFirstChild every frame RunService.Heartbeat:Connect(function() local hrp = player.Character:FindFirstChild("HumanoidRootPart") local humanoid = player.Character:FindFirstChildOfClass("Humanoid") -- ... end)

-- GOOD: Cache references local character, hrp, humanoid

local function cacheCharacter() character = player.Character if character then hrp = character:WaitForChild("HumanoidRootPart") humanoid = character:WaitForChild("Humanoid") end end

player.CharacterAdded:Connect(cacheCharacter) cacheCharacter()

RunService.Heartbeat:Connect(function() if hrp then -- Use cached reference end end)

Table Operations

-- BAD: Creating new tables constantly RunService.Heartbeat:Connect(function() local data = {x = 1, y = 2, z = 3} -- New table every frame end)

-- GOOD: Reuse tables local data = {x = 0, y = 0, z = 0} RunService.Heartbeat:Connect(function() data.x, data.y, data.z = 1, 2, 3 end)

-- Use table.create for known sizes local arr = table.create(1000, 0) -- Pre-allocate 1000 slots

-- Clear table without creating new one local function clearTable(t) for k in pairs(t) do t[k] = nil end end

Local vs Global Variables

-- BAD: Accessing globals is slower for i = 1, 1000000 do local x = math.sin(i) -- Global lookup each time end

-- GOOD: Cache in local variable local sin = math.sin for i = 1, 1000000 do local x = sin(i) -- Local lookup is faster end

Memory Optimization

Instance Destruction

-- Properly destroy instances to free memory local function cleanup(instance) -- Disconnect all connections first for _, connection in ipairs(instance:GetConnections()) do connection:Disconnect() end

-- Clear attributes
for _, attr in ipairs(instance:GetAttributes()) do
    instance:SetAttribute(attr, nil)
end

instance:Destroy()

end

-- Nil references after destroy local part = Instance.new("Part") part:Destroy() part = nil -- Allow garbage collection

Object Pooling

local ObjectPool = {} ObjectPool.__index = ObjectPool

function ObjectPool.new(template, initialSize) local pool = setmetatable({ template = template, available = {}, active = {} }, ObjectPool)

for i = 1, initialSize do
    local obj = template:Clone()
    obj.Parent = nil
    table.insert(pool.available, obj)
end

return pool

end

function ObjectPool:get() local obj = table.remove(self.available) if not obj then obj = self.template:Clone() end table.insert(self.active, obj) return obj end

function ObjectPool:release(obj) local index = table.find(self.active, obj) if index then table.remove(self.active, index) end obj.Parent = nil -- Remove from world -- Reset state... table.insert(self.available, obj) end

Garbage Collection Awareness

-- Avoid creating garbage in hot loops -- BAD: RunService.Heartbeat:Connect(function() local info = { -- Creates garbage every frame position = hrp.Position, velocity = hrp.AssemblyLinearVelocity } end)

-- GOOD: Use primitives or reuse tables local cachedPosition = Vector3.new() local cachedVelocity = Vector3.new()

RunService.Heartbeat:Connect(function() -- Vectors are value types, no garbage created local pos = hrp.Position local vel = hrp.AssemblyLinearVelocity end)

-- Manual GC control (use sparingly) local function forceGC() collectgarbage("collect") end

Physics Optimization

Collision Groups

local PhysicsService = game:GetService("PhysicsService")

-- Create collision groups PhysicsService:RegisterCollisionGroup("Players") PhysicsService:RegisterCollisionGroup("Enemies") PhysicsService:RegisterCollisionGroup("Projectiles") PhysicsService:RegisterCollisionGroup("Debris")

-- Disable unnecessary collisions PhysicsService:CollisionGroupSetCollidable("Players", "Players", false) PhysicsService:CollisionGroupSetCollidable("Projectiles", "Projectiles", false) PhysicsService:CollisionGroupSetCollidable("Debris", "Debris", false)

-- Assign to parts local function setCollisionGroup(part, groupName) part.CollisionGroup = groupName end

Anchored Parts

-- Anchor static parts to remove from physics simulation local function optimizeStaticParts(model) for _, part in ipairs(model:GetDescendants()) do if part:IsA("BasePart") then local isStatic = not part:FindFirstChildOfClass("Motor6D") and not part:FindFirstChildOfClass("Weld") if isStatic then part.Anchored = true end end end end

Simplified Collision

-- Use simpler collision shapes meshPart.CollisionFidelity = Enum.CollisionFidelity.Box -- Fastest meshPart.CollisionFidelity = Enum.CollisionFidelity.Hull -- Medium meshPart.CollisionFidelity = Enum.CollisionFidelity.Default -- Detailed

-- Disable collisions for visual-only parts visualPart.CanCollide = false visualPart.CanQuery = false -- Excludes from raycasts too visualPart.CanTouch = false -- Excludes from Touched events

Network Optimization

Minimize RemoteEvent Traffic

-- BAD: Fire every frame RunService.Heartbeat:Connect(function() PositionRemote:FireServer(hrp.Position) end)

-- GOOD: Throttle updates local lastUpdate = 0 local UPDATE_RATE = 1/20 -- 20 updates per second

RunService.Heartbeat:Connect(function() local now = os.clock() if now - lastUpdate >= UPDATE_RATE then lastUpdate = now PositionRemote:FireServer(hrp.Position) end end)

-- BETTER: Only send when changed significantly local lastSentPosition = Vector3.new() local POSITION_THRESHOLD = 0.5

RunService.Heartbeat:Connect(function() local pos = hrp.Position if (pos - lastSentPosition).Magnitude > POSITION_THRESHOLD then lastSentPosition = pos PositionRemote:FireServer(pos) end end)

Data Compression

-- Quantize positions to reduce data size local function quantizeVector3(v, precision) precision = precision or 0.1 return Vector3.new( math.floor(v.X / precision) * precision, math.floor(v.Y / precision) * precision, math.floor(v.Z / precision) * precision ) end

-- Pack multiple values local function packColor(color) return color.R * 65536 + color.G * 256 + color.B end

local function unpackColor(packed) local r = math.floor(packed / 65536) local g = math.floor((packed % 65536) / 256) local b = packed % 256 return Color3.fromRGB(r, g, b) end

Profiling Tools

MicroProfiler

-- Use debug.profilebegin/end for custom profiling debug.profilebegin("MyExpensiveFunction") -- ... expensive code ... debug.profileend()

-- View in MicroProfiler (Ctrl+F6 in Studio)

Performance Stats

local Stats = game:GetService("Stats")

local function logPerformance() print("Memory:", Stats:GetTotalMemoryUsageMb(), "MB") print("Instances:", Stats.InstanceCount) print("Data Receive:", Stats.DataReceiveKbps, "Kbps") print("Data Send:", Stats.DataSendKbps, "Kbps") print("Physics Step:", Stats.PhysicsStepTimeMs, "ms") end

Frame Rate Monitoring

local frameCount = 0 local lastTime = os.clock()

RunService.RenderStepped:Connect(function() frameCount = frameCount + 1

local now = os.clock()
if now - lastTime >= 1 then
    local fps = frameCount / (now - lastTime)
    print("FPS:", math.floor(fps))
    frameCount = 0
    lastTime = now
end

end)

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

audio-system

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

vfx-effects

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

animation-system

No summary provided by upstream source.

Repository SourceNeeds Review