重用代码

*此内容使用人工智能(Beta)翻译,可能包含错误。若要查看英文页面,请点按 此处

在创建了几个脚本之后,你总是想在它们之间重用一些代码。根据 位置ModuleScripts 允许你在客户端与服务器的边界两侧或边界同一侧的脚本之间重用代码。

你可以将模块脚本放在任何可以放置脚本的地方,但 ReplicatedStorage 是一个常用位置;将模块脚本存储在这里可以让你在服务器和客户端之间重用代码。

模块脚本的结构

在 Roblox Studio 中,将一个模块脚本添加到 ReplicatedStorage 并将其重命名为 PickupManager。每个 ModuleScript 都以以下代码开头:


local module = {}
return module

这段代码创建了一个空的 Luau 并将其返回给任何需要该模块脚本的脚本。

返回值可以是任何 数据类型,但不能是 nil,不过大多数模块脚本返回一个函数、一个表,或一个函数表。为了生成其返回值,模块脚本当然可以运行任意代码,这包括需要其他模块脚本。

以下示例返回一个包含单个函数 getPickupBonus 的表。将其粘贴到新的模块脚本中:


-- 存储在 ReplicatedStorage 中的 ModuleScript
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 函数:

ReplicatedStorage 中的客户端脚本

local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 获取模块脚本返回的值
local PickupManager = require(ReplicatedStorage:WaitForChild("PickupManager"))
-- 调用模块脚本函数
local bonus = PickupManager.getPickupBonus("legendary")
print(bonus) --> 125

将模块脚本存储在 ReplicatedStorage 中可让你在服务器和客户端之间共享代码,因此你可以使用相同的代码从 ServerScriptService 需要该脚本:

ServerScriptService 中的脚本

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PickupManager = require(ReplicatedStorage:WaitForChild("PickupManager"))

当你在 ModuleScript 上调用 require() 时,它只运行一次并返回一个引用。再次调用 require() 将返回完全相同的引用,这意味着如果你修改返回的 Instance,随后的 require() 调用将返回该已修改的引用。模块本身不会多次运行。

如果你在客户端与服务器的边界两侧都需要 ModuleScript,如上述示例,ModuleScript 将为每一侧返回唯一的引用。

模式

模块脚本有一些常见的模式,你可以利用它们来简化代码并避免在游戏扩大到一定规模和复杂性时遇到的问题。

数据共享

要将数据与单个对象关联,可以为它们分配属性,也可以创建带有值对象(如 StringValueIntValue)的 Configuration 文件夹。然而,如果你想添加或修改数十个对象或数据值,这两种方法都非常麻烦,并且它们也不存储表或函数。

如果你希望修改多个相同对象的相同数据,或为不同对象重用相同数据,请将数据存储在 ModuleScripts 中。这是一种更轻松的方式,可以让你在其他脚本中重用数据,你还可以存储表和函数。

以下示例的 ModuleScript 存储在 ReplicatedStorage 中,保存了一个通用枪支的配置值:

ReplicatedStorage 中的 ModuleScript

local GunConfig = {}
GunConfig.MagazineSize = 20
GunConfig.AmmoCount = 100
GunConfig.Firerate = 600
GunConfig.Damage = {
["Head"] = 50;
["Torso"] = 40;
["Body"] = 25;
}
return GunConfig

自定义事件

自定义事件使脚本能够相互通信,但需要跟踪对每个 BindableEvent 对象的引用可能会使你的代码变得混乱。

你可以使用 ModuleScripts 来存储 BindableEvents,并提供与 ModuleScript 方法直接关联的自定义事件处理程序。

以下 ModuleScript 存储在 ReplicatedStorage 中,具有在开关状态变化时触发的自定义事件:

ReplicatedStorage 中的 ModuleScript

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 事件触发时连接一个函数:

ReplicatedStorage 中的脚本

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Switch = require(ReplicatedStorage:WaitForChild("Switch"))
Switch.Changed:Connect(function(newState)
print("开关状态现在是", newState)
end)
-- 测试翻转几次
task.wait(1)
Switch.flip()
task.wait(1)
Switch.flip()

封装

封装是创建一个抽象层围绕对象或脚本逻辑以隐藏复杂度的做法。你可以使用 ModuleScripts 将 Roblox 对象与自定义 Luau 函数封装在一起,以简化代码。

例如,你可以使用封装来:

  • 用单个 RemoteEvent 对象简化跨网络通信。
  • 将错误处理代码包装在敏感服务(如 DataStoreService)周围。
  • 定义自定义方法来控制或扩展 Roblox 对象的功能。

在游戏中实施网络时,跟踪数十个单独的 RemoteEvent 对象是非常困难的。你可以使用 ModuleScript 来封装一个单一的 RemoteEvent,以帮助简化这个问题。通过包含一个唯一的 id 参数,你仍然可以发送不同的网络消息,同时仅使用一个 RemoteEvent

在以下示例中,名为 NetworkManagerClientModuleScript 封装了 RemoteEvent:FireServer() 方法,并包含了这个额外的 id 参数。此外,这个 ModuleScript 还引用了 RemoteEvent 对象本身,因此你不必在代码的其他部分引用它。你只需需要引用这个 ModuleScript 来发送网络消息,不需要在代码库的其余部分处理 RemoteEvent 对象。

以下存储在 ReplicatedFirst 中的 ModuleScript 提供了一个封装函数,你可以在客户端脚本中调用它以发送网络消息:

网络模块

-- 存储在 ReplicatedFirst 中的 ModuleScript,名为 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 都会触发。


-- 存储在 ServerScriptService 中的 ModuleScript,名为 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

以下存储在 ReplicatedFirst 中的 LocalScript 发送一个 ID 为 RequestA 以及可选的 Hello 参数的消息。


-- 存储在 ReplicatedFirst 中的 LocalScript
local ReplicatedFirst = game:GetService("ReplicatedFirst")
local NetworkManagerClient = require(ReplicatedFirst:WaitForChild("NetworkManagerClient"))
NetworkManagerClient.FireServer("RequestA", "Hello")

以下存储在 ServerScriptService 中的 Script 连接到网络消息 ID RequestA 并在收到请求时打印出任何附加参数的声明。


-- 存储在 ServerScriptService 中的脚本
local ServerScriptService = game:GetService("ServerScriptService")
local NetworkManagerServer = require(ServerScriptService:WaitForChild("NetworkManagerServer"))
NetworkManagerServer.GetServerEventSignal("RequestA"):Connect(function(player, ...)
print("收到来自", player, "的 RequestA 请求,参数为", ...)
end)
©2026 Roblox Corporation、Roblox、Roblox 标志及 Powering Imagination 是我们在美国及其他国家或地区的注册与未注册商标。