🎯

roblox-api-patterns

🎯Skill

from taozhuo/game-dev-skills

VibeIndex|
What it does

Provides expert guidance on navigating Roblox-specific API behaviors, performance optimizations, and common coding pitfalls in Lua scripting.

roblox-api-patterns

Installation

Install skill:
npx skills add https://github.com/taozhuo/game-dev-skills --skill roblox-api-patterns
1
AddedJan 27, 2026

Skill Details

SKILL.md

Provides Roblox-specific API patterns, gotchas, best practices, and platform knowledge. Use when you need to understand Roblox-specific behaviors, services, or common pitfalls.

Overview

# Roblox API Patterns & Gotchas

When working with Roblox APIs, be aware of these platform-specific behaviors and patterns.

Common Gotchas

Instance.new() - Set Parent Last

```lua

-- BAD: Setting parent first causes events to fire during setup

local part = Instance.new("Part")

part.Parent = workspace -- Events fire here!

part.Size = Vector3.new(5, 5, 5) -- Property changes after parenting

-- GOOD: Set all properties, then parent

local part = Instance.new("Part")

part.Size = Vector3.new(5, 5, 5)

part.Position = Vector3.new(0, 10, 0)

part.Anchored = true

part.Parent = workspace -- Only fires events once, fully configured

-- BETTER: Use Instance.new with parent argument (deprecated but still works)

-- Actually, the second argument is being phased out. Stick with setting parent last.

```

WaitForChild Timeout

```lua

-- BAD: Can hang indefinitely if child doesn't exist

local child = parent:WaitForChild("MaybeExists")

-- GOOD: Use timeout

local child = parent:WaitForChild("MaybeExists", 5) -- 5 second timeout

if child then

-- Child found

else

warn("Child not found after 5 seconds")

end

```

Touched Events Fire Multiple Times

```lua

-- BAD: Can trigger multiple times per contact

part.Touched:Connect(function(hit)

dealDamage(hit) -- May deal damage multiple times!

end)

-- GOOD: Debounce

local debounce = {}

part.Touched:Connect(function(hit)

local character = hit.Parent

if debounce[character] then return end

debounce[character] = true

dealDamage(hit)

task.delay(0.5, function()

debounce[character] = nil

end)

end)

```

os.clock() vs tick() vs time()

```lua

-- os.clock(): High-resolution timer, best for measuring elapsed time

local start = os.clock()

-- ... code ...

local elapsed = os.clock() - start

-- tick(): DEPRECATED, don't use in new code

-- time(): Returns time since game started, affected by time scale

local gameTime = time()

-- workspace:GetServerTimeNow(): Synchronized server time

local serverTime = workspace:GetServerTimeNow()

-- os.time(): Unix timestamp (seconds since 1970)

local unixTime = os.time()

```

Humanoid States

```lua

-- Humanoid states can be unreliable

-- Don't rely solely on StateChanged

local humanoid = character:WaitForChild("Humanoid")

-- Check state directly

local function isGrounded()

-- FloorMaterial is more reliable than state

return humanoid.FloorMaterial ~= Enum.Material.Air

end

-- Or use raycasts

local function isGroundedRaycast()

local hrp = character:FindFirstChild("HumanoidRootPart")

if not hrp then return false end

local result = workspace:Raycast(

hrp.Position,

Vector3.new(0, -3.1, 0), -- Slightly more than hip height

RaycastParams.new()

)

return result ~= nil

end

```

GetChildren vs GetDescendants

```lua

-- GetChildren: Only immediate children

local children = model:GetChildren()

-- GetDescendants: All descendants (recursive)

local allParts = model:GetDescendants()

-- Both return tables that are snapshots

-- Adding/removing during iteration is safe but won't be reflected

for _, child in ipairs(model:GetChildren()) do

child:Destroy() -- Safe, iterating over snapshot

end

```

Service Patterns

Players Service

```lua

local Players = game:GetService("Players")

-- Get local player (client only)

local localPlayer = Players.LocalPlayer

-- Player events

Players.PlayerAdded:Connect(function(player)

-- Note: Character may not exist yet

player.CharacterAdded:Connect(function(character)

-- Character loaded

end)

-- If character already exists (late connection)

if player.Character then

-- Handle existing character

end

end)

-- PlayerRemoving fires BEFORE player is removed

Players.PlayerRemoving:Connect(function(player)

-- Save data here, player still valid

end)

```

RunService

```lua

local RunService = game:GetService("RunService")

-- Different update events

RunService.PreRender:Connect(function(dt)

-- Before rendering (client only, after input)

end)

RunService.PreAnimation:Connect(function(dt)

-- Before animation update

end)

RunService.PreSimulation:Connect(function(dt)

-- Before physics

end)

RunService.PostSimulation:Connect(function(dt)

-- After physics (same as Stepped)

end)

RunService.Heartbeat:Connect(function(dt)

-- After physics and before rendering

-- Most common for game logic

end)

RunService.RenderStepped:Connect(function(dt)

-- Every frame, client only

-- Use for camera/visual updates

end)

-- Check context

if RunService:IsClient() then

-- Client-side code

end

if RunService:IsServer() then

-- Server-side code

end

if RunService:IsStudio() then

-- Running in Studio

end

```

TweenService

```lua

local TweenService = game:GetService("TweenService")

-- Create tween

local tweenInfo = TweenInfo.new(

1, -- Duration

Enum.EasingStyle.Quad, -- EasingStyle

Enum.EasingDirection.Out, -- EasingDirection

0, -- RepeatCount (0 = no repeat)

false, -- Reverses

0 -- DelayTime

)

local tween = TweenService:Create(part, tweenInfo, {

Position = Vector3.new(0, 10, 0),

Transparency = 0.5

})

tween:Play()

-- Wait for completion

tween.Completed:Wait()

-- or

tween.Completed:Connect(function(playbackState)

if playbackState == Enum.PlaybackState.Completed then

-- Finished normally

elseif playbackState == Enum.PlaybackState.Cancelled then

-- Was cancelled

end

end)

-- Cancel tween

tween:Cancel()

-- Tweening CFrame (doesn't lerp, use custom for smooth rotation)

-- Use CFrame:Lerp() manually for better control

```

DataStoreService Limits

```lua

local DataStoreService = game:GetService("DataStoreService")

-- Rate limits:

-- GetAsync: 60 + numPlayers * 10 per minute

-- SetAsync: 60 + numPlayers * 10 per minute

-- UpdateAsync: 60 + numPlayers * 10 per minute

-- GetSortedAsync: 5 + numPlayers per minute

-- SetSortedAsync: 60 + numPlayers * 10 per minute

-- Data limits:

-- Key length: 50 characters max

-- Value size: 4MB max

-- Key format: Only alphanumeric and underscores

-- Budget system

local budget = DataStoreService:GetRequestBudgetForRequestType(

Enum.DataStoreRequestType.GetAsync

)

print("GetAsync budget:", budget)

```

MarketplaceService

```lua

local MarketplaceService = game:GetService("MarketplaceService")

-- Check ownership

local function ownsGamepass(player, gamepassId)

local success, owns = pcall(function()

return MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassId)

end)

return success and owns

end

-- Developer Products (consumable)

MarketplaceService.ProcessReceipt = function(receiptInfo)

-- MUST return a value

-- PurchaseGranted: Purchase successful, receipt cleared

-- NotProcessedYet: Try again later (player rejoins, etc.)

local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)

if not player then

return Enum.ProductPurchaseDecision.NotProcessedYet

end

-- Grant product...

return Enum.ProductPurchaseDecision.PurchaseGranted

end

-- Prompt purchase

MarketplaceService:PromptProductPurchase(player, productId)

MarketplaceService:PromptGamePassPurchase(player, gamepassId)

```

RemoteEvent/Function Patterns

Type Safety

```lua

-- Client sends data, server receives

-- NEVER trust client data types

RemoteEvent.OnServerEvent:Connect(function(player, data)

-- Validate types

if type(data) ~= "table" then return end

if type(data.action) ~= "string" then return end

if type(data.value) ~= "number" then return end

-- Validate ranges

if data.value < 0 or data.value > 100 then return end

if data.value ~= data.value then return end -- NaN check

-- Now safe to use

end)

```

RemoteFunction Timeout

```lua

-- RemoteFunctions can hang if server/client doesn't respond

-- Wrap in pcall and use manual timeout

local function safeInvoke(remote, ...)

local args = {...}

local result

local finished = false

task.spawn(function()

local success, response = pcall(function()

return remote:InvokeServer(unpack(args))

end)

if success then

result = response

end

finished = true

end)

-- Timeout after 10 seconds

local startTime = os.clock()

while not finished and os.clock() - startTime < 10 do

task.wait()

end

return result

end

```

Physics Patterns

Network Ownership

```lua

-- Parts have network owners that control physics simulation

local part = workspace.MyPart

-- Set owner to specific player

part:SetNetworkOwner(player)

-- Set owner to server (nil)

part:SetNetworkOwner(nil)

-- Get current owner

local owner = part:GetNetworkOwner()

-- Check if can set

if part:CanSetNetworkOwnership() then

part:SetNetworkOwner(player)

end

-- Anchored parts have no network owner

-- Welded parts inherit owner from root part

```

AssemblyLinearVelocity vs Velocity

```lua

-- Velocity is DEPRECATED

-- Use AssemblyLinearVelocity and AssemblyAngularVelocity

local part = workspace.MyPart

-- Get velocity

local linearVel = part.AssemblyLinearVelocity

local angularVel = part.AssemblyAngularVelocity

-- Set velocity (only works if you're network owner)

part.AssemblyLinearVelocity = Vector3.new(0, 50, 0)

-- For applying forces, use constraints instead

local attachment = Instance.new("Attachment", part)

local linearVelocity = Instance.new("LinearVelocity")

linearVelocity.Attachment0 = attachment

linearVelocity.VectorVelocity = Vector3.new(0, 50, 0)

linearVelocity.MaxForce = math.huge

linearVelocity.Parent = part

```

Animation Patterns

Loading Animations

```lua

local humanoid = character:WaitForChild("Humanoid")

local animator = humanoid:WaitForChild("Animator")

-- Load animation

local animation = Instance.new("Animation")

animation.AnimationId = "rbxassetid://123456789"

local track = animator:LoadAnimation(animation)

-- Play with settings

track:Play(

0.1, -- Fade in time

1, -- Weight

1 -- Speed

)

-- Animation priorities

track.Priority = Enum.AnimationPriority.Action -- Highest

-- Core < Idle < Movement < Action

-- Animation events

track:GetMarkerReachedSignal("FootstepMarker"):Connect(function()

playFootstepSound()

end)

-- Stop

track:Stop(0.1) -- Fade out time

```

Lighting Patterns

Time of Day

```lua

local Lighting = game:GetService("Lighting")

-- Set time (0-24 hours)

Lighting.ClockTime = 14.5 -- 2:30 PM

-- Animate day/night cycle

local function updateTimeOfDay()

Lighting.ClockTime = (Lighting.ClockTime + 0.001) % 24

end

-- Geographic time (affects sun angle)

Lighting.GeographicLatitude = 41.9 -- New York latitude

```

Post-Processing

```lua

-- Add to Lighting or Camera

local blur = Instance.new("BlurEffect")

blur.Size = 10

blur.Parent = Lighting

local colorCorrection = Instance.new("ColorCorrectionEffect")

colorCorrection.Brightness = 0.1

colorCorrection.Contrast = 0.2

colorCorrection.Saturation = -0.1

colorCorrection.Parent = Lighting

local bloom = Instance.new("BloomEffect")

bloom.Intensity = 0.5

bloom.Size = 24

bloom.Threshold = 0.95

bloom.Parent = Lighting

```

Common Attribute Patterns

Using Attributes

```lua

-- Set attribute

part:SetAttribute("Health", 100)

part:SetAttribute("Team", "Red")

part:SetAttribute("IsActive", true)

-- Get attribute

local health = part:GetAttribute("Health")

-- Get all attributes

local attributes = part:GetAttributes()

for name, value in pairs(attributes) do

print(name, value)

end

-- Listen for changes

part:GetAttributeChangedSignal("Health"):Connect(function()

local newHealth = part:GetAttribute("Health")

end)

-- Attributes replicate automatically (server -> client)

-- Attributes persist with the instance

```

Debugging Patterns

Print Debugging

```lua

-- Use warn for visibility

warn("Important message")

-- Structured logging

local function log(level, message, data)

local timestamp = os.date("%H:%M:%S")

local output = string.format("[%s] [%s] %s", timestamp, level, message)

if data then

output = output .. " " .. HttpService:JSONEncode(data)

end

print(output)

end

log("INFO", "Player joined", {userId = player.UserId})

```

Error Handling

```lua

-- Always wrap risky operations

local success, result = pcall(function()

return riskyOperation()

end)

if success then

-- Use result

else

warn("Operation failed:", result)

end

-- With xpcall for stack traces

local success, result = xpcall(function()

return riskyOperation()

end, function(err)

warn("Error:", err)

warn(debug.traceback())

end)

```

More from this repository10

🎯
audio-system🎯Skill

Manages audio systems with advanced sound pooling, priority management, and performance optimization for immersive game sound experiences.

🎯
optimization🎯Skill

optimization skill from taozhuo/game-dev-skills

🎯
game-systems🎯Skill

Implements robust game systems like inventory, shops, trading, and progression mechanics for Roblox RPGs with secure, stackable item management.

🎯
animation-system🎯Skill

animation-system skill from taozhuo/game-dev-skills

🎯
vfx-effects🎯Skill

Generates dynamic visual effects like particle systems, camera shakes, and impact animations for enhancing game experiences in Roblox.

🎯
gemini-image-prompting🎯Skill

Generates conceptual game art images using Gemini AI, providing precise style-specific prompts for character, environment, and UI design across various game aesthetics.

🎯
combat-system🎯Skill

Enables comprehensive combat mechanics with advanced hitbox detection, damage calculation, and hit validation for creating dynamic player-vs-player and player-vs-NPC combat interactions.

🎯
security-anticheat🎯Skill

security-anticheat skill from taozhuo/game-dev-skills

🎯
data-persistence🎯Skill

Manages reliable Roblox data persistence by implementing robust data loading, saving, caching, and error handling for player progress and game state.

🎯
procedural-generation🎯Skill

Generates procedurally randomized content like terrain, dungeons, and cities using advanced noise algorithms and algorithmic techniques.