在創建了幾個腳本之後,您很快就會想要在它們之間重複使用一些代碼。根據位置,ModuleScripts 讓您可以在客戶端與伺服器邊界的不同側,或同一側之間重複使用代碼。
您可以將模組腳本放置在您放置腳本的任何地方,但 ReplicatedStorage 是一個受歡迎的位置;將模組腳本儲存於此使您能夠在伺服器和客戶端之間重複使用代碼。
模組腳本的結構
在 Roblox Studio 中,將一個模組腳本添加到 ReplicatedStorage 並將其重新命名為 PickupManager。每個 ModuleScript 都以以下代碼開始:
local module = {}return module
這段代碼創建了一個空的 Luau 表格,並將其返回給任何需要該模組腳本的腳本。
返回值可以是任何數據類型,除了 nil,但大多數模組腳本返回的是一個函數、一個表格或一個函數的表格。模組腳本當然可以運行任意代碼來生成其返回值,包括要求其他模組腳本。
以下範例返回一個包含名為 getPickupBonus 的單一函數的表格。將其粘貼到新的模組腳本中:
-- ModuleScript in ReplicatedStorage
local PickupManager = {}
local defaultMultiplier = 1.25
local rarityMultipliers = {
common = 10,
uncommon = 20,
rare = 50,
legendary = 100
}
-- 將 getPickupBonus 函數添加到 PickupManager 表格
PickupManager.getPickupBonus = function(rarity)
local bonus = rarityMultipliers[rarity] * defaultMultiplier
return bonus
end
return PickupManager
將函數添加到表格並不是嚴格必要的——您可以直接返回函數本身——但這是一個良好的模式;當您從另一個腳本調用函數時,它會給您一個易於理解的語法,並且可以讓您隨著時間的推移輕鬆地向模組腳本添加更多函數。
要求模組腳本
要加載模組腳本,您需要調用 require() 函數。在 ReplicatedStorage 中,添加一個新腳本,並將其 RunContext 更改為 Client。然後添加以下代碼來調用 PickupManager.getPickupBonus 函數:
Client script in ReplicatedStoragelocal ReplicatedStorage = game:GetService("ReplicatedStorage")-- 獲取模組腳本返回的值local PickupManager = require(ReplicatedStorage:WaitForChild("PickupManager"))-- 調用模組腳本函數local bonus = PickupManager.getPickupBonus("legendary")print(bonus) --> 125
將模組腳本儲存在 ReplicatedStorage 中使您能夠在伺服器和客戶端之間共享代碼,因此您可以使用相同的代碼從 ServerScriptService 中要求該腳本:
Script in ServerScriptServicelocal ReplicatedStorage = game:GetService("ReplicatedStorage")local PickupManager = require(ReplicatedStorage:WaitForChild("PickupManager"))
當您調用 require() 於 ModuleScript 時,它只會運行一次並返回一個引用的單一項目。再次調用 require() 會返回完全相同的引用,這意味著如果您修改返回的表格或 Instance,隨後的 require() 調用將返回該修改過的引用。模組本身不會多次運行。
如果您從客戶端-伺服器邊界的兩側要求 ModuleScript,例如上述範例,ModuleScript 將為每一側返回一個唯一的引用。
模式
模組腳本有一些常見模式,您可以使用這些模式來簡化代碼並避免隨著您的遊戲增長而引發的問題。
數據共享
要將數據與個別物件關聯,您可以為它們分配屬性,或創建帶有值物件(例如 StringValue 或 IntValue)的 Configuration 文件夾。然而,這兩種方法在您想要添加或修改數十個物件或數據值時都是麻煩的。它們還不存儲表格或函數。
如果您希望修改同一數據的多個副本,或重複使用同一數據於不同的物件,則應將數據儲存在 ModuleScripts 中。這樣可以更輕鬆地在其他腳本中重複使用數據,並且您可以儲存表格和函數。
以下的 ModuleScript 在 ReplicatedStorage 中存儲一個通用槍支的配置值:
ModuleScript in ReplicatedStoragelocal GunConfig = {}GunConfig.MagazineSize = 20GunConfig.AmmoCount = 100GunConfig.Firerate = 600GunConfig.Damage = {["Head"] = 50;["Torso"] = 40;["Body"] = 25;}return GunConfig
自定義事件
自定義事件使腳本之間能夠相互通信,但必須跟踪對個別 BindableEvent 對象的引用可能會使您的代碼雜亂。
您可以使用 ModuleScripts 儲存 BindableEvents 並提供直接與 ModuleScript 的方法綁定的自定義事件處理程序。
以下 ModuleScript 在 ReplicatedStorage 中具有一個自定義事件,當開關狀態改變時觸發:
ModuleScript in ReplicatedStorage
local Switch = {}
-- 創建可綁定的,任何腳本都可以監聽開關變更的事件
local bindableEvent = Instance.new("BindableEvent")
Switch.Changed = bindableEvent.Event
local state = false
function Switch.flip()
state = not state
bindableEvent:Fire(state)
end
return Switch
以下在 ReplicatedStorage 中的客戶端腳本連接了當 Switch.Changed 事件觸發時要調用的函數。
Script in ReplicatedStorage
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Switch = require(ReplicatedStorage:WaitForChild("Switch"))
Switch.Changed:Connect(function(newState)
print("Switch state is now", newState)
end)
-- 測試翻轉幾次
task.wait(1)
Switch.flip()
task.wait(1)
Switch.flip()
封裝
封裝是創建對象或腳本邏輯的抽象層以隱藏複雜性的做法。您可以使用 ModuleScripts 封裝 Roblox 對象與自定義 Luau 函數來簡化代碼。
例如,您可以使用封裝來:
- 使用單一 RemoteEvent 對象簡化跨網路通信。
- 在敏感服務(如 DataStoreService)周圍包裝錯誤處理代碼。
- 定義自定義方法以控制或擴展 Roblox 對象的功能。
要在遊戲中實現網絡通信,跟踪數十個單獨的 RemoteEvent 對象是困難的。您可以使用 ModuleScript 封裝一個單一的 RemoteEvent 來幫助簡化這個問題。通過包含一個唯一的 id 參數,您仍然可以在只使用一個 RemoteEvent 的情況下發送不同的網絡消息。
在下面的示例中,名為 NetworkManagerClient 的 ModuleScript 封裝了 RemoteEvent:FireServer() 方法,以包含這個額外的 id 參數。此外,這個 ModuleScript 引用 RemoteEvent 對象本身,這樣您就不必在代碼的其他部分引用它。您只需要引用這個 ModuleScript 來發送網絡消息,並無需在代碼庫的其他部分處理 RemoteEvent 對象。
以下在 ReplicatedFirst 的 ModuleScript 提供了一個封裝的函數,您可以在客戶端腳本上調用以發送網絡消息:
Network Module
-- ModuleScript in ReplicatedFirst named NetworkManagerClient
local NetworkManagerClient = {}
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent")
-- 封裝遠程對象的 FireServer 函數
function NetworkManagerClient.FireServer(id, ...)
remoteEvent:FireServer(id, ...)
end
return NetworkManagerClient
以下在 ServerScriptService 的 ModuleScript 使用 BindableEvents 來讓每個腳本連接到特定的 ID。當客戶端發送網絡消息時,與指定 ID 相關聯的每個 BindableEvent 都會觸發。
-- ModuleScript in ServerScriptService named NetworkManagerServer
local NetworkManagerServer = {}
local networkSignalList = {}
function NetworkManagerServer.GetServerEventSignal(id)
local bindableEvent = Instance.new("BindableEvent")
-- 將新的 BindableEvent 與 id 連結
table.insert(networkSignalList, {
id = id,
bindableEvent = bindableEvent,
})
return bindableEvent.Event
end
-- 連接到
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent")
remoteEvent.OnServerEvent:Connect(function(player, id, ...)
-- 查找所有匹配接收的遠程事件 ID 的綁定事件
for _, signal in networkSignalList do
if signal.id == id then
signal.bindableEvent:Fire(player, ...)
end
end
end)
return NetworkManagerServer
以下在 LocalScript 中發送一個 ID 為 RequestA 的消息,並帶有可選的 Hello 參數。
-- LocalScript in ReplicatedFirstlocal ReplicatedFirst = game:GetService("ReplicatedFirst")local NetworkManagerClient = require(ReplicatedFirst:WaitForChild("NetworkManagerClient"))NetworkManagerClient.FireServer("RequestA", "Hello")
以下在 Script 中連接到網絡消息 ID RequestA,並在接收到請求時打印出任何附加參數的聲明。
-- Script in ServerScriptService
local ServerScriptService = game:GetService("ServerScriptService")
local NetworkManagerServer = require(ServerScriptService:WaitForChild("NetworkManagerServer"))
NetworkManagerServer.GetServerEventSignal("RequestA"):Connect(function(player, ...)
print("Received RequestA from", player, ...)
end)