roblox-luau
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRoblox Luau Scripting
Roblox Luau 脚本编写
Script a Roblox experience in Luau: services, s, events, the
server/client split, and secure cross-boundary communication. Targets the current
Roblox engine and Studio.
Instance使用Luau编写Roblox体验脚本:服务、、事件、服务器/客户端分离,以及安全的跨边界通信。针对当前Roblox引擎与Studio。
InstanceWhen to use
使用场景
- Use when writing Roblox scripts: getting services, creating/parenting instances,
connecting events, deciding server vs client, or wiring /
RemoteEventcommunication.RemoteFunction - Use when the project has /
Script/LocalScriptobjects,ModuleScriptplaces, or a Rojo.rbxl(x), and code calls*.project.json.game:GetService(...)
When not to use: persisting data across sessions → .
Generic Lua questions unrelated to the Roblox API. Engine-agnostic input/save
architecture → / .
roblox-datastoresinput-systemssave-systems- 编写Roblox脚本时使用:获取服务、创建/设置实例父对象、连接事件、判断代码运行在服务器还是客户端,或者配置/
RemoteEvent通信。RemoteFunction - 项目包含/
Script/LocalScript对象、ModuleScript场景文件,或Rojo.rbxl(x),且代码调用*.project.json时使用。game:GetService(...)
**不适用场景:**跨会话持久化数据 → 使用。与Roblox API无关的通用Lua问题。与引擎无关的输入/存储架构 → 使用 / 。
roblox-datastoresinput-systemssave-systemsCore workflow
核心工作流程
- Get services with . Common ones:
game:GetService("Name"),Players,Workspace(shared client+server),ReplicatedStorage(server-only code),ServerScriptService,ServerStorage,RunService(client).UserInputService - Know where code runs. A runs on the server; a
Scriptruns on a client (inLocalScript,StarterPlayerScripts, or the player's character). AStarterGuiis shared code youModuleScript.require - Create instances deliberately. , set its properties, then set
local p = Instance.new("Part")last (parenting triggers replication).p.Parent - React with events. to signals like
:Connect,Players.PlayerAdded, orpart.Touched. Disconnect when done to avoid leaks.RunService.Heartbeat - Cross the client/server boundary with Remotes — and never trust the client.
Clients request via ; the server validates and applies. The server is authoritative for all game state.
RemoteEvent:FireServer(...) - Test in Studio with Play / Play Here / server+client Start; use the Output window and the server/client view toggle to confirm where code ran.
- 使用获取服务。常见服务包括:
game:GetService("Name")、Players、Workspace(客户端与服务器共享)、ReplicatedStorage(仅服务器代码)、ServerScriptService、ServerStorage、RunService(客户端)。UserInputService - 明确代码运行位置。运行在服务器;
Script运行在客户端(位于LocalScript、StarterPlayerScripts或玩家角色中)。StarterGui是可通过ModuleScript调用的共享代码。require - 谨慎创建实例。,设置其属性,最后再设置
local p = Instance.new("Part")(设置父对象会触发复制)。p.Parent - 通过事件响应操作。使用连接
:Connect、Players.PlayerAdded或part.Touched等信号。使用完毕后断开连接以避免内存泄漏。RunService.Heartbeat - 通过Remotes实现跨客户端/服务器通信——绝不信任客户端。客户端通过发起请求;服务器验证后执行操作。服务器对所有游戏状态拥有权威控制权。
RemoteEvent:FireServer(...) - 在Studio中测试:使用Play / Play Here / 服务器+客户端启动;使用输出窗口和服务器/客户端视图切换按钮确认代码运行位置。
Patterns
模式示例
1. Server Script: react to players joining (leaderstats)
1. 服务器脚本:响应玩家加入(排行榜)
lua
-- ServerScriptService/Leaderboard.server.luau (a Script = runs on the server)
local Players = game:GetService("Players")
local function onPlayerAdded(player: Player)
local stats = Instance.new("Folder")
stats.Name = "leaderstats" -- this name makes it show on the leaderboard
local coins = Instance.new("IntValue")
coins.Name = "Coins"
coins.Value = 0
coins.Parent = stats
stats.Parent = player -- parent LAST
end
Players.PlayerAdded:Connect(onPlayerAdded)lua
-- ServerScriptService/Leaderboard.server.luau (Script = 在服务器运行)
local Players = game:GetService("Players")
local function onPlayerAdded(player: Player)
local stats = Instance.new("Folder")
stats.Name = "leaderstats" -- 此名称会让数据显示在排行榜上
local coins = Instance.new("IntValue")
coins.Name = "Coins"
coins.Value = 0
coins.Parent = stats
stats.Parent = player -- 最后设置父对象
end
Players.PlayerAdded:Connect(onPlayerAdded)2. Create and configure an instance
2. 创建并配置实例
lua
local Workspace = game:GetService("Workspace")
local part = Instance.new("Part")
part.Size = Vector3.new(4, 1, 4)
part.Position = Vector3.new(0, 10, 0)
part.Anchored = true -- won't fall under gravity
part.BrickColor = BrickColor.new("Bright blue")
part.Parent = Workspace -- set Parent last so it replicates once, fullylua
local Workspace = game:GetService("Workspace")
local part = Instance.new("Part")
part.Size = Vector3.new(4, 1, 4)
part.Position = Vector3.new(0, 10, 0)
part.Anchored = true -- 不会受重力影响掉落
part.BrickColor = BrickColor.new("Bright blue")
part.Parent = Workspace -- 最后设置Parent,确保实例以完整状态仅复制一次3. Connect an event (and disconnect to avoid leaks)
3. 连接事件(并断开连接避免泄漏)
lua
local debounce = false
local connection
connection = part.Touched:Connect(function(hit: BasePart)
local character = hit.Parent
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if not humanoid or debounce then return end
debounce = true
humanoid.Health -= 10
task.wait(1) -- task.wait, NOT the deprecated wait()
debounce = false
end)
-- Later, when the part is removed or the round ends:
-- connection:Disconnect()lua
local debounce = false
local connection
connection = part.Touched:Connect(function(hit: BasePart)
local character = hit.Parent
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if not humanoid or debounce then return end
debounce = true
humanoid.Health -= 10
task.wait(1) -- 使用task.wait,而非已弃用的wait()
debounce = false
end)
-- 后续当部件被移除或回合结束时:
-- connection:Disconnect()4. Client → server with a RemoteEvent (validate on the server!)
4. 通过RemoteEvent实现客户端→服务器通信(务必在服务器验证!)
lua
-- ReplicatedStorage: create a RemoteEvent named "BuyItem" (in Studio or via code).
-- CLIENT (LocalScript): request a purchase. The client can lie — this is only a request.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyItem = ReplicatedStorage:WaitForChild("BuyItem") -- wait: may not have replicated yet
buyButton.MouseButton1Click:Connect(function()
buyItem:FireServer("sword") -- send the item id only; never the price/result
end)lua
-- SERVER (Script): the ONLY place the transaction is decided.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyItem = ReplicatedStorage:WaitForChild("BuyItem")
local PRICES = { sword = 100, shield = 75 }
buyItem.OnServerEvent:Connect(function(player: Player, itemId)
-- TRUST NOTHING from the client. Validate types and values.
if type(itemId) ~= "string" then return end
local price = PRICES[itemId]
if not price then return end -- unknown item
local coins = player.leaderstats.Coins
if coins.Value < price then return end -- can't afford
coins.Value -= price -- server applies the change
grantItem(player, itemId)
end)lua
-- 在ReplicatedStorage中创建名为"BuyItem"的RemoteEvent(可在Studio中创建或通过代码创建)。
-- 客户端(LocalScript):发起购买请求。客户端可能造假——这只是请求。
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyItem = ReplicatedStorage:WaitForChild("BuyItem") -- 使用WaitForChild:对象可能尚未完成复制
buyButton.MouseButton1Click:Connect(function()
buyItem:FireServer("sword") -- 仅发送物品ID;绝不要发送价格或结果
end)lua
-- 服务器(Script):交易的唯一决策地点。
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyItem = ReplicatedStorage:WaitForChild("BuyItem")
local PRICES = { sword = 100, shield = 75 }
buyItem.OnServerEvent:Connect(function(player: Player, itemId)
-- 绝不信任客户端传来的任何内容。验证参数的类型与值。
if type(itemId) ~= "string" then return end
local price = PRICES[itemId]
if not price then return end -- 未知物品
local coins = player.leaderstats.Coins
if coins.Value < price then return end -- 资金不足
coins.Value -= price -- 服务器执行扣款操作
grantItem(player, itemId)
end)5. A per-frame loop with RunService
5. 使用RunService实现逐帧循环
lua
local RunService = game:GetService("RunService")
-- Heartbeat fires every frame AFTER physics; dt is seconds since the last step.
RunService.Heartbeat:Connect(function(dt)
spinner.CFrame *= CFrame.Angles(0, math.rad(90) * dt, 0) -- 90deg/sec, frame-independent
end)lua
local RunService = game:GetService("RunService")
-- Heartbeat在每帧物理计算后触发;dt为自上一帧以来的秒数。
RunService.Heartbeat:Connect(function(dt)
spinner.CFrame *= CFrame.Angles(0, math.rad(90) * dt, 0) -- 每秒90度,与帧率无关
end)6. Shared code in a ModuleScript
6. ModuleScript中的共享代码
lua
-- ReplicatedStorage/GameConfig (a ModuleScript) — usable by server and client.
local GameConfig = {}
GameConfig.MaxHealth = 100
function GameConfig.damageFor(weapon: string): number
return ({ sword = 25, bow = 15 })[weapon] or 0
end
return GameConfiglua
local GameConfig = require(game:GetService("ReplicatedStorage"):WaitForChild("GameConfig"))
print(GameConfig.MaxHealth)lua
-- ReplicatedStorage/GameConfig(ModuleScript)——服务器与客户端均可使用。
local GameConfig = {}
GameConfig.MaxHealth = 100
function GameConfig.damageFor(weapon: string): number
return ({ sword = 25, bow = 15 })[weapon] or 0
end
return GameConfiglua
local GameConfig = require(game:GetService("ReplicatedStorage"):WaitForChild("GameConfig"))
print(GameConfig.MaxHealth)Pitfalls
常见陷阱
- Trusting the client is an exploit → clients can send any arguments to a
/
RemoteEvent. Validate every argument's type and range on the server and keep the server authoritative over health, currency, and inventory.RemoteFunction - doesn't run where you put it → LocalScripts run in
LocalScript,StarterPlayerScripts,StarterCharacterScripts, or tools — not inStarterGuiorWorkspace. ServerServerScriptServices belong inScript/ServerScriptService.Workspace - Deprecated globals → use /
task.wait/task.spawn, not the oldtask.delay/wait()/spawn()(worse scheduling and throttling).delay() - Parenting first, then setting properties → set properties first and last so the instance replicates once in its final state.
Parent - on the client right after join → objects stream/replicate over time; use
nilinstead of indexing directly on the client.parent:WaitForChild("Name") - Connections never disconnected → long-lived handlers leak and can fire on destroyed objects; store the connection and
:Connect(or use:Disconnect()/Instance:GetAttributeChangedSignalwhere appropriate).:Once - Using a RemoteFunction where a RemoteEvent fits → blocks waiting for a return and a malicious/slow client can stall the server; prefer one-way
RemoteFunctions unless you genuinely need a reply.RemoteEvent
- 信任客户端会导致漏洞 → 客户端可向/
RemoteEvent发送任意参数。务必在服务器验证每个参数的类型与范围,服务器需对生命值、货币和物品拥有权威控制权。RemoteFunction - LocalScript未在预期位置运行 → LocalScript仅在、
StarterPlayerScripts、StarterCharacterScripts或工具中运行——不会在StarterGui或Workspace中运行。服务器ServerScriptService应放在Script/ServerScriptService中。Workspace - 已弃用的全局函数 → 使用/
task.wait/task.spawn,而非旧版task.delay/wait()/spawn()(调度性能更差且存在节流问题)。delay() - 先设置父对象再设置属性 → 应先设置属性,最后设置,确保实例以最终状态仅复制一次。
Parent - 客户端刚加入时对象为nil → 对象会随时间流式传输/复制;在客户端使用而非直接索引。
parent:WaitForChild("Name") - 连接从未断开 → 长期存在的处理程序会导致内存泄漏,且可能在对象销毁后仍触发;需保存连接并调用
:Connect(或在合适场景使用:Disconnect()/Instance:GetAttributeChangedSignal)。:Once - 在适合用RemoteEvent的场景使用RemoteFunction → 会阻塞等待返回结果,恶意或响应缓慢的客户端可能导致服务器卡顿;除非确实需要回复,否则优先使用单向的
RemoteFunction。RemoteEvent
References
参考资料
- For the full client/server model (replication, vs
RemoteFunction,RemoteEventtiming,:WaitForChildfor same-context messaging, attributes,BindableEventtags, andCollectionService/connection cleanup), read:Once.references/client-server.md
- 如需了解完整的客户端/服务器模型(复制机制、与
RemoteFunction对比、RemoteEvent时机、同上下文通信的:WaitForChild、属性、BindableEvent标签,以及CollectionService/连接清理),请阅读:Once。references/client-server.md
Related skills
相关技能
- — persist player data across sessions (server-only).
roblox-datastores - — engine-agnostic persistence concepts.
save-systems - /
game-ai— portable AI and input patterns to implement in Luau.input-systems
- —— 跨会话持久化玩家数据(仅服务器可用)。
roblox-datastores - —— 与引擎无关的持久化概念。
save-systems - /
game-ai—— 可在Luau中实现的通用AI与输入模式。input-systems