Roblox 体验默认为多人游戏,因此所有体验都具有内在的通信机制。在最简单的情况下,当玩家移动其角色时,某些 Humanoid 属性,例如状态,会向服务器通信,并向其他连接的客户端通传此信息。
远程事件和回调让您通过 通过 客户端-服务器边界:
- RemoteEvents 启用单向通信(发送请求和 不 产生为回应)。
- UnreliableRemoteEvents 启用一向通信以对数据进行持续或不是游戏状态的交易。这些事件交易顺序和可靠性以提高网络性能。
- RemoteFunctions 启用双向通信(发送请求并在收到回应后返收件人)。
与 可绑定事件 不同,其使用场景和远程事件的函数有更多限制,因此要列出远程事件和函数的使用案例需要太多:
- 游戏玩法 - 基础游戏玩法,例如玩家到达关等级终点,可能需要远程事件。客户端脚本向服务器发送通知,服务器脚本重置玩家的位置。
- 服务器验证 - 如果玩家尝试喝下一瓶药水,他们会 有 那瓶药水吗?为了确保公平,服务器必须是真实的来源。客户脚本可以使用远程体验件向服务器通知玩家正在喝下药水,然后服务器脚本可以决定是否有玩家真的有那瓶药水,并且是否要授予任何
- 用户界面更新 - 当游戏状态改变时,服务器脚本可以使用远程事件向客户端发送通知,以便告知他们关于分数、目标等方面的更改。
- 在体验中购买市场产品 - 对于使用远程函数的示例实现,请参阅提示订阅购买。
快速参考
下表为快速参考使用 RemoteEvents 和 RemoteFunctions 之间的通信。
客户端 → 服务器 > | >|
---|---|
客户 | RemoteEvent:FireServer(args) |
服务器 | RemoteEvent.OnServerEvent:Connect(function(player, args)) |
服务器 → 客户端 | >|
服务器 | RemoteEvent:FireClient(player, args) |
客户 | RemoteEvent.OnClientEvent:Connect(function(args)) |
服务器 | RemoteEvent:FireAllClients(args) |
客户 | RemoteEvent.OnClientEvent:Connect(function(args)) |
远程事件
一个 RemoteEvent 对象可以在客户端-服务器边界之间进行异步一方通信,无需为响应。
要创建一个新的 RemoteEvent 通过 Studio 中的 Explorer 窗口:
- 将鼠标悬停在你想要插入RemoteEvent的容器上。为了确保服务器和客户端都可以1) 使用权 2)通行证 3)访问权限问,它必须位于两个方面都能看到的地方,例如ReplicatedStorage,尽管在一些情况下它的存储在Workspace或1> Class.Workspace1>内也可以。
- 点击容器名称右侧的 ⊕ 按钮,然后插入一个 RemoteEvent 实例。
- 重命名实例以反映其目的。
一旦您创建了一个 RemoteEvent,它可以从客户端到服务器通信,从服务器到客户端通信或从服务器到所有客户端通信。
客户端 → 服务器
您可以使用一个 LocalScript 来在 服务器 上触发事件,调用 Class.RemoteEvent:FireServer()|FireServer() 2>Class.RemoteEvent
客户 | RemoteEvent:FireServer(args) |
服务器 | RemoteEvent.OnServerEvent:Connect(function(player, args)) |
下列 Script 连接一个服务器件处理器到 OnServerEvent ,该创建一个新的 Class.
事件连接 - 脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 获取远程事件实例的引用
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onCreatePart(player, partColor, partPosition)
print(player.Name .. " fired the RemoteEvent")
local newPart = Instance.new("Part")
newPart.Color = partColor
newPart.Position = partPosition
newPart.Parent = workspace
end
-- 连接函数到事件
remoteEvent.OnServerEvent:Connect(onCreatePart)
事件发射 - 本地脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")-- 获取远程事件实例的引用local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")-- 发射远程事件并传递额外参数remoteEvent:FireServer(Color3.fromRGB(255, 0, 0), Vector3.new(0, 25, -20))
服务器 → 客户
您可以使用一个 Script 来在
服务器 | RemoteEvent:FireClient(player, args) |
客户 | RemoteEvent.OnClientEvent:Connect(function(args)) |
下列 LocalScript 连接一个事件处理器到 OnClientEvent 事件。 随后,Script 列出来的玩家来到服务器,并调用 1> Class.RemoteEvent:FireClient()|FireClient()1> 为每个有任意数据的玩家。
事件连接 - 本地脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
-- 获取远程事件实例的引用
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local player = Players.LocalPlayer
local function onNotifyPlayer(maxPlayers, respawnTime)
print("[Client] Event received by player", player.Name)
print(maxPlayers, respawnTime)
end
-- 连接函数到事件
remoteEvent.OnClientEvent:Connect(onNotifyPlayer)
事件发射 - 脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
-- 获取远程事件实例的引用
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 监听进入的玩家并将远程事件发送到每个
local function onPlayerAdded(player)
print("[Server] Firing event to player", player.Name)
remoteEvent:FireClient(player, Players.MaxPlayers, Players.RespawnTime)
end
Players.PlayerAdded:Connect(onPlayerAdded)
服务器 → 所有客户
您可以使用一个 Script 来触发所有客户端上的事件,调用 FireAllClients() 方法在 Class.RemoteEvent
服务器 | RemoteEvent:FireAllClients(args) |
客户 | RemoteEvent.OnClientEvent:Connect(function(args)) |
下列 LocalScript 连接一个事件处理器到 OnClientEvent 事件,该事件输出剩余的倒计时时间。随后的 Script 调用每秒钟 1> Class.RemoteEvent:FireAllClients()|FireAllClients()1>
事件连接 - 本地脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 获取远程事件实例的引用
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onTimerUpdate(seconds)
print(seconds)
end
-- 连接函数到事件
remoteEvent.OnClientEvent:Connect(onTimerUpdate)
事件发射 - 脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")-- 获取远程事件实例的引用local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")local countdown = 5-- 在倒计时结束之前,每秒钟发射一个远程事件for timeRemaining = -1, countdown doremoteEvent:FireAllClients(countdown - timeRemaining)task.wait(1)end
远程调用
一个 RemoteFunction 对象可以在客户端-服务器边界上进行同步的两向通信。发送远程函数的发件人会直到收到从收件人收人那里获得的回应。
要创建一个新的 RemoteFunction 通过 Studio 中的 Explorer 窗口:
- 将鼠标悬停在你想要插入RemoteFunction的容器上。为了确保工作间务器和客户端都可以1) 使用权 2)通行证 3)访问权限问,它必须位于两个方面都能看到的地方,例如ReplicatedStorage,尽管在一些情况下它的存储在Workspace或1> Class.Workspace1>内也可以。
- 点击容器名称右侧的 ⊕ 按钮,然后插入一个 RemoteFunction 实例。
- 重命名实例以反映其目的。
当您创建了一个 RemoteFunction 时,它可以在 客户端和服务器 或 服务器和客户端 之间的两向通信中提供帮助。
客户端 → 服务器 → 客户端
您可以使用一个 LocalScript 来调用服务器上的函数,通过在 Class.RemoteFunction:ExecuteServer()|ExecuteServer() 方法上调用 Class.RemoteFunction:
客户 | RemoteFunction:InvokeServer(args) |
服务器 | RemoteFunction.OnServerInvoke = function(player, args) |
下列 Script 定义了回调函数通过 OnServerInvoke 调用,并且通过其 Part 值返回要求的 2>Class.Part2> 。 跟随的 5>Class.LocalScript5> 然后调用 8>Class
回调连接 - 脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 获取远程函数实例的引用
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
-- 回调函数
local function createPart(player, partColor, partPosition)
print(player.Name .. " requested a new part")
local newPart = Instance.new("Part")
newPart.Color = partColor
newPart.Position = partPosition
newPart.Parent = workspace
return newPart
end
-- 将函数设置为远程函数的回调
remoteFunction.OnServerInvoke = createPart
事件邀请 - 本地脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")-- 获取远程函数实例的引用local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")-- 调用回调时通过颜色和位置local newPart = remoteFunction:InvokeServer(Color3.fromRGB(255, 0, 0), Vector3.new(0, 25, -20))-- 输出返回的部分引用print("The server created the requested part:", newPart)
服务器 → 客户 → 服务器
您可以使用一个 Script 来调用客户端上的函数,调用 InvokeClient() 方法在 RemoteFunction 上,但它有以下严重风险:
- 如果客户端抛出错误,服务器也会抛出错误。
- 如果客户端在被调用时断开,InvokeClient() 将发生错误。
- 如果客户端没有返回值,服务器将永久生成。
对于不需要两向通信的操作,例如更新图形GUI界面,请使用 RemoteEvent 和从 服务器到客户端 通信。
参数限制
当您发射一个 RemoteEvent 或使用一个 RemoteFunction 时,它将向您传递通过事件或调用函数传递的任何参数。 任何 Roblox 对象,例如一个 Enum 、一个 1> Class.Instance1> 或其他类型的对象
非字符索引
如果传入的表中的任何 索引 为非字符串类型,例如Instance、userdata或1>函数1>,Roblox 将自动将这些索引转换为字符串。
事件连接 - 本地脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onEventFire(passedTable)
for k, v in passedTable do
print(typeof(k)) --> 字符串
end
end
-- 连接函数到事件
remoteEvent.OnClientEvent:Connect(onEventFire)
事件发射 - 脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 监听进入的玩家并将远程事件发送到每个
local function onPlayerAdded(player)
remoteEvent:FireClient(player,
{
[workspace.Baseplate] = true
}
)
end
Players.PlayerAdded:Connect(onPlayerAdded)
已通过的函数
包含在 RemoteEvent 或 RemoteFunction 作为参数的函数将不会在客户端-服务器边界上复制,因此无法远程传递函数。相反,在接收端的参数将为 8>nil8> 。
事件连接 - 本地脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onClientEvent(func)
print(func) --> 零
end
remoteEvent.OnClientEvent:Connect(onClientEvent)
事件发射 - 脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function testFunction()
print("Hello world!")
end
-- 作为参数使用远程事件
remoteEvent:FireAllClients(testFunction)
桌子索引
如果您通过数据表,通过一个混合表的数字和字符串键,不要通过一个包含 完整值 的键值对 (字典) 或 完整值 的数字索引。
事件连接 - 脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onEventFire(player, passedTable)
for k, v in passedTable do
print(k .. " = " .. v)
--> 1 = 剑
--> 2 = 弓
--> CharName = Diva 拖动器
--> CharClass = 叛逆
end
end
-- 连接函数到事件
remoteEvent.OnServerEvent:Connect(onEventFire)
事件发射 - 本地脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")-- 数量化索引表local inventoryData = {"Sword", "Bow"}-- 字典表local characterData = {CharName = "Diva Dragonslayer",CharClass = "Rogue"}remoteEvent:FireServer(inventoryData)remoteEvent:FireServer(characterData)
桌子身份
通过将参数传递给远程事件/回调是复制的,这意味着它们不会与在事件发生时或调用回调时提供的相同。 也不会返回到调用器的表与在提供时相同。 您可以通过在 RemoteFunction 上运行以下脚本来示例这一点,并观察表在调用时或在调用回调时与提供时的不同。
回调连接 - 脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
-- 回调函数
local function returnTable(player, passedTable)
-- 在调用时输出表身份
print(tostring(passedTable)) --> 桌子:0x48eb7aead27563d9
return passedTable
end
-- 将函数设置为远程函数的回调
remoteFunction.OnServerInvoke = returnTable
事件邀请 - 本地脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")local inventoryData = {"Sword", "Bow"}-- 输出原始表身份print(tostring(inventoryData)) --> 表:0x059bcdbb2b576549local invokeReturn = remoteFunction:InvokeServer(inventoryData)-- 输出表达式 I回传print(tostring(invokeReturn)) --> table: 0x9fcae7919563a0e9
金属表
如果表有一个 metatable,所有的 metatable 信息都会在转换中丢失。在以下代码示例中, NumWheels 属性是 Car 的 metatable 的一部分。当服务器收到下表时,truck 表有 1> Name1> 属性,但
事件连接 - 脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onEvent(player, param)
print(param) --> { ["Name"] = "MyTruck"
end
-- 连接函数到事件
remoteEvent.OnServerEvent:Connect(onEvent)
事件发射 - 本地脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")local Car = {}Car.NumWheels = 4Car.__index = Carlocal truck = {}truck.Name = "MyTruck"setmetatable(truck, Car)-- 带有 metatable 的火焰事件remoteEvent:FireServer(truck)
非复制实例
如果 RemoteEvent 或 RemoteFunction 通过一个只对发件人可见的值,Roblox 不会在客户端-服务器边界上复制它,而是将 nil 代替值
事件发射 - 脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 因为客户端无法访问服务器存储,会被收为“nil”
local storedPart = Instance.new("Part")
storedPart.Parent = ServerStorage
local function onPlayerAdded(player)
remoteEvent:FireClient(player, storedPart)
end
Players.PlayerAdded:Connect(onPlayerAdded)
同样,如果您在 LocalScript 中创建了一个部分,然后尝试将其传递到 Script ,服务器会看到 nil ,因为部分不适用于服务器。
事件发射 - 本地脚本
local ReplicatedStorage = game:GetService("ReplicatedStorage")local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")-- 因为服务器不知道这部分的存在local clientPart = Instance.new("Part")clientPart.Parent = workspaceremoteEvent:FireServer(clientPart)