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 物件促進了在客戶端與伺服器之間的非同步單向通信,而不需要等待回應。
要通過 Studio 中的 資源管理器 視窗創建新的 RemoteEvent:
- 將滑鼠懸停在你想要插入 RemoteEvent 的容器上。為了確保伺服器和客戶端都能訪問,它必須位於雙方都能看到的地方,例如 ReplicatedStorage,雖然在某些情況下,將其存儲在 Workspace 或 Tool 中也是合適的。
- 點擊出現在容器名稱右側的 ⊕ 按鈕,並插入一個 RemoteEvent 實例。
- 將實例重新命名以描述其用途。
一旦你創建了 RemoteEvent,它可以促進從 客戶端到伺服器、從 伺服器到客戶端 或從 伺服器到所有客戶端 的單向通信。



客戶端 → 伺服器
你可以使用 LocalScript 通過在 RemoteEvent 上調用 FireServer() 方法來觸發伺服器上的事件。如果你將參數傳遞給 FireServer(),它們將以某些 限制 的形式傳遞到伺服器上的事件處理程序。請注意,伺服器上事件處理程序的第一個參數始終是調用它的客戶端的 Player 物件,後續參數隨之而來。
| 客戶端 | RemoteEvent:FireServer(args) |
| 伺服器 | RemoteEvent.OnServerEvent:Connect(function(player, args)) |
以下 Script 將事件處理程序連接到 OnServerEvent,該處理程序在伺服器上創建一個新的 Part。隨附的 LocalScript 然後在 RemoteEvent 實例上調用 FireServer(),並傳遞所需的 Color 和 Position 參數。
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
-- 獲取遠端事件實例的引用
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onCreatePart(player, partColor, partPosition)
print(player.Name .. " 觸發了 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() 方法來觸發客戶端上的事件。FireClient() 的第一個參數是你想要響應事件的客戶端的 Player 物件,額外的參數將以某些 限制 的形式傳遞給客戶端。請注意,事件處理程序不需要將 Player 物件作為第一個參數,因為你可以使用 Players.LocalPlayer 在客戶端確定玩家。
| 伺服器 | RemoteEvent:FireClient(player, args) |
| 客戶端 | RemoteEvent.OnClientEvent:Connect(function(args)) |
以下 LocalScript 將事件處理程序連接到 OnClientEvent 事件。隨附的 Script 然後監聽進入伺服器的玩家,並對每個玩家調用 FireClient(),傳遞任意數據。
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("[客戶端] 玩家接收到事件", 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("[伺服器] 向玩家觸發事件", player.Name)
remoteEvent:FireClient(player, Players.MaxPlayers, Players.RespawnTime)
end
Players.PlayerAdded:Connect(onPlayerAdded)伺服器 → 所有客戶端
你可以使用 Script 通過在 RemoteEvent 上調用 FireAllClients() 方法來觸發所有客戶端上的事件。與 FireClient() 不同,FireAllClients() 方法不需要 Player 物件,因為它將 RemoteEvent 發送到所有客戶端。
| 伺服器 | RemoteEvent:FireAllClients(args) |
| 客戶端 | RemoteEvent.OnClientEvent:Connect(function(args)) |
以下 LocalScript 將事件處理程序連接到 OnClientEvent 事件,該事件輸出剩餘的倒計時時間。隨附的 Script 然後每秒在循環中調用 FireAllClients(),以便為所有客戶端觸發 RemoteEvent。
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
-- 每秒觸發 RemoteEvent,直到時間到期
for timeRemaining = -1, countdown do
remoteEvent:FireAllClients(countdown - timeRemaining)
task.wait(1)
end遠端回呼
RemoteFunction 物件促進了在客戶端與伺服器之間的同步雙向通信。遠端函數的發送者將等待直到收到接收者的回應。
要通過 Studio 中的 資源管理器 視窗創建新的 RemoteFunction:
- 將滑鼠懸停在你想要插入 RemoteFunction 的容器上。為了確保伺服器和客戶端都能訪問,它必須位於雙方都能看到的地方,例如 ReplicatedStorage,雖然在某些情況下,將其存儲在 Workspace 或 Tool 中也是合適的。
- 點擊出現在容器名稱右側的 ⊕ 按鈕,並插入一個 RemoteFunction 實例。
- 將實例重新命名以描述其用途。
一旦你創建了 RemoteFunction,它可以促進 客戶端與伺服器 或 伺服器與客戶端 之間的雙向通信。


客戶端 → 伺服器 → 客戶端
你可以使用 LocalScript 通過在 RemoteFunction 上調用 InvokeServer() 方法來調用伺服器上的函數。與 遠端事件 不同,調用 RemoteFunction 的 LocalScript 會等待直到回呼返回。你傳遞給 InvokeServer() 的參數將以某些 限制 的形式傳遞到 OnServerInvoke 回呼中。請注意,如果你為同一 RemoteFunction 定義多個回呼,只有最後一個定義會執行。
| 客戶端 | RemoteFunction:InvokeServer(args) |
| 伺服器 | RemoteFunction.OnServerInvoke = function(player, args) |
以下 Script 通過 OnServerInvoke 定義回呼函數,並通過其 return 值返回請求的 Part。隨附的 LocalScript 然後調用 InvokeServer(),並傳遞額外參數以定義請求的部件顏色和位置。
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
-- 獲取遠端函數實例的引用
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
-- 回呼函數
local function createPart(player, partColor, partPosition)
print(player.Name .. " 請求一個新部件")
local newPart = Instance.new("Part")
newPart.Color = partColor
newPart.Position = partPosition
newPart.Parent = Workspace
return newPart
end
-- 將函數設置為遠端函數的回呼
remoteFunction.OnServerInvoke = createPartlocal ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 獲取遠端函數實例的引用
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
-- 在調用回呼時傳遞顏色和位置
local newPart = remoteFunction:InvokeServer(Color3.fromRGB(255, 0, 0), Vector3.new(0, 25, -20))
-- 輸出返回的部件引用
print("伺服器創建了請求的部件:", newPart)伺服器 → 客戶端 → 伺服器
你可以使用 Script 通過在 RemoteFunction 上調用 InvokeClient() 方法來調用客戶端上的函數,但這樣做有嚴重的風險,如下所示:
- 如果客戶端拋出錯誤,伺服器也會拋出錯誤。
- 如果客戶端在被調用時斷開連接,InvokeClient() 會拋出錯誤。
- 如果客戶端不返回值,伺服器將永遠等待。
對於不需要雙向通信的操作,例如更新 GUI,請使用 RemoteEvent 並從 伺服器到客戶端 進行通信。
傳遞保證
每種遠端類型對於消息是否到達以及以何種順序到達做出不同的承諾。這些承諾僅適用於單個連接的單個方向,意味著從伺服器到特定客戶端,或從特定客戶端到伺服器。
RemoteEvents
RemoteEvents 是可靠且有序的:
- 如果消息在傳輸中丟失,Roblox 會重新傳輸它,直到它到達,只要接收者保持連接。
- 消息以你觸發它們的順序到達,包括跨不同的 RemoteEvent 實例。例如,如果伺服器先觸發事件 A,然後是 B,再然後是 A,那麼該客戶端將接收到 A、B、A。這適用於無論你使用 FireClient() 還是 FireAllClients()。
- 如果客戶端觸發事件的速度超過 節流 限制,伺服器會稍後處理多餘的事件,而不是丟棄它們,並且它們保持其順序。
可靠、有序的消息仍然可能在以下情況下無法到達你的代碼:
- 沒有處理程序連接。 如果在消息到達時,OnServerEvent 或 OnClientEvent 沒有連接任何東西,Roblox 會將其排隊並在連接處理程序後按順序交付。隊列在數量和內存上都有所限制。一旦滿了,Roblox 會丟棄進一步的消息並在 輸出 視窗中記錄 Remote event invocation 錯誤。
- 接收者斷開連接。 當客戶端離開時,尚未交付的消息將丟失。
排序適用於每個處理程序 開始 的時間,而不是完成的時間。如果處理程序等待,例如通過調用 task.wait() 或 WaitForChild(),Roblox 可以在早期處理程序恢復之前開始處理下一條消息。
RemoteFunctions
RemoteFunctions 可靠地發送調用和回應,因此只要雙方保持連接,Roblox 會重新傳輸丟失的消息。由於 InvokeServer() 和 InvokeClient() 會等待直到回應到達,因此單個線程不能有多於一個正在進行的調用。不同的線程可以同時調用,但如果回呼等待,回應可能會以不同於發送的調用的順序返回。
如果沒有回呼分配給 OnServerInvoke 或 OnClientInvoke,Roblox 會將調用排隊,直到有一個為止。這個隊列有與 RemoteEvent 隊列相同的限制。
UnreliableRemoteEvents
UnreliableRemoteEvents 不保證交付或順序。它們與 RemoteEvents 或 RemoteFunctions 之間也沒有排序關係。Roblox 可能在以下任何情況下丟棄消息,而不會重新發送:
- 消息在傳輸中丟失。
- 網路擁擠,消息等待發送的時間過長。
- 負載超過 1,000 字節。
- 客戶端觸發的速度超過 節流 限制。與 RemoteEvent 不同,伺服器會丟棄多餘的消息,而不是延遲它們。
Roblox 也不會等待早期消息到達後再處理後續消息,因此消息可能會無序到達。如果順序很重要,請在每條消息中包含時間戳或序列號,並忽略比你處理的最新消息更舊的消息。
節流與大小限制
客戶端到伺服器的消息受到速率限制,每種遠端類型在其所有實例之間共享。有關當前限制,請參見 RemoteEvent 的 節流 部分和 UnreliableRemoteEvent 中的有效載荷限制。
參數限制
當你觸發 RemoteEvent 或調用 RemoteFunction 時,它會轉發你與事件或回呼函數一起傳遞的任何參數。任何類型的 Roblox 物件,例如 Enum、Instance 或其他,都可以傳遞,以及 Luau 類型,例如數字、字符串和布林值,儘管你應該仔細探索以下限制。
非字符串索引
如果傳遞的表中的任何 索引 是非字符串類型,例如 Instance、userdata 或 function,Roblox 會自動將這些索引轉換為字符串。
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onEventFire(passedTable)
for k, v in passedTable do
print(typeof(k)) --> string
end
end
-- 將函數連接到事件
remoteEvent.OnClientEvent:Connect(onEventFire)local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 監聽進入的玩家並向每個玩家發送遠端事件
local function onPlayerAdded(player)
remoteEvent:FireClient(player,
{
[Workspace.Baseplate] = true
}
)
end
Players.PlayerAdded:Connect(onPlayerAdded)傳遞的函數
作為 RemoteEvent 或 RemoteFunction 的參數包含的函數將 不 複製到 客戶端-伺服器 邊界,這使得無法遠端傳遞函數。相反,接收方的結果參數將為 nil。
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
local function onClientEvent(func)
print(func) --> nil
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 Dragonslayer
--> CharClass = Rogue
end
end
-- 將函數連接到事件
remoteEvent.OnServerEvent:Connect(onEventFire)local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 數字索引表
local inventoryData = {
"劍", "弓"
}
-- 字典表
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)) --> table: 0x48eb7aead27563d9
return passedTable
end
-- 將函數設置為遠端函數的回呼
remoteFunction.OnServerInvoke = returnTablelocal ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:FindFirstChildOfClass("RemoteFunction")
local inventoryData = {
"劍", "弓"
}
-- 輸出原始表身份
print(tostring(inventoryData)) --> table: 0x059bcdbb2b576549
local invokeReturn = remoteFunction:InvokeServer(inventoryData)
-- 輸出返回的表身份
print(tostring(invokeReturn)) --> table: 0x9fcae7919563a0e9元表
如果一個表有元表,所有的元表信息在傳輸中會丟失。在以下代碼示例中,NumWheels 屬性是 Car 元表的一部分。當伺服器接收到以下表時,truck 表具有 Name 屬性,但 不 具有 NumWheels 屬性。
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 = 4
Car.__index = Car
local truck = {}
truck.Name = "MyTruck"
setmetatable(truck, Car)
-- 觸發事件,並傳遞包含元表的表
remoteEvent:FireServer(truck)非複製的實例
如果 RemoteEvent 或 RemoteFunction 傳遞一個僅對發送者可見的值,Roblox 不會在客戶端與伺服器邊界上複製它,並傳遞 nil 代替該值。例如,如果 Script 傳遞 ServerStorage 的子項,則監聽事件的客戶端將接收到 nil 值,因為該物件對客戶端不可複製。
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 將被接收為 "nil",因為客戶端無法訪問 ServerStorage
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 Workspace = game:GetService("Workspace")
local remoteEvent = ReplicatedStorage:FindFirstChildOfClass("RemoteEvent")
-- 將被接收為 "nil",因為伺服器不知道這個部件
local clientPart = Instance.new("Part")
clientPart.Parent = Workspace
remoteEvent:FireServer(clientPart)