重用代码

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

在创建几个脚本后,它们之间的代码创建时间一般不会很长。在位置ModuleScripts 允许您在不同侧面的客户端-服务器边界或同一侧面之间重用代码。

创建模块脚本

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

  1. 在 Roblox Studio 中,将鼠标悬停在 ReplicatedStorageExplorer 窗口上,然后单击 +
  2. 选择 ModuleScript 来添加一个新的模块脚本。
  3. 右击脚本并将其重命名为 PickupManager
  4. 双击脚本以在 脚本编辑器 中打开。

模块脚本的解剖

每个 ModuleScript 以下验证码开始:


local module = {}
return module

此代码创建一个空的 Luau 表,并将其返回任何需要模块脚本的脚本。

返回值可以是任何 数据类型 除了 nil 之外,但大多数模块脚本都会返回一个函数、表或表 of 函数。要生成其返回值,模块脚本可以运行任意验证码、包括需要其他模块脚本。

下面的例子返回了一个包含一个单个函数为 getPickupBonus 的表。 将它粘贴到您的新模块脚本中:


-- 复制存储中的模块脚本
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 更改为 1> Client1>。然后添加以下代码来调用 4> PickupManager.getPickupBonus4> 函数:

复制存储中的客户脚本

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

您可以使用同一个代码要求脚本从 ServerScriptService :

服务器脚本存储中的脚本

local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- 获取 “PickupManager” 模块脚本的返回值
local PickupManager = require(ReplicatedStorage:WaitForChild("PickupManager"))

当您在 require() 上调用时,它将运行一次并作为 ModuleScript 的一个参考返回一个单项。再次调用 require() 将返回完全

如果您需要客户端-服务器边界的两侧都有 ModuleScript,那么 ModuleScript 将为每一侧返回唯一的引用。

模式

模块脚本具有一些常见模式,您可以使用它们来简化代码并避免随着体验规模和复杂性的增长而导致的陷阱。

数据共享

要将数据与各个对象关联,您可以为其分配属性或创建 Configuration 文件夹以值对象(例如 StringValueIntValue)。然而,如果您想要添加或修改十个以上对象或数据值,这两种方法都很麻烦。它们也不存储表或函数。

如果您想为同一对象的多个副本修改相同的数据或为不同对象重用相同的数据,存储数据在 ModuleScripts 中。它是一种更容易的方式为您在其他脚本中重用数据,并且您可以存储表和函数。

ModuleScriptReplicatedStorage 的示例,Class.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 方法直接相关的自定义事件处理程序。

Class.ReplicatedStorage|ReplicatedStorage 中的 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 事件触发时调用。

复制存储中的脚本

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 用自定义 Lua 函数封装 Roblox 对象来简化代验证码。

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

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

跟踪数十个单独的 RemoteEvent 对象来实现游戏中的网络是很困难的。您可以使用 ModuleScript 来封装单个 RemoteEvent 来帮助简化此问题。通过包含独特的 2>id2> 参数,您仍然可以

下面的例子中,名为 NetworkManagerClientRemoteEvent:FireServer() 封装了 1>

ModuleScript 中, следующие ReplicatedFirst 提供了一个封装函数,您可以调用客户端脚本发送网络消信息:

网络模块

-- 复制的第一个名为 NetworkManagerClient 的模块脚本
local NetworkManagerClient = {}
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent")
-- 封装远程对象的 FireServer 函数
function NetworkManagerClient.FireServer(id, ...)
remoteEvent:FireServer(id, ...)
end
return NetworkManagerClient

Class.ServerScriptService1> 中的 ServerScriptService 使用 BindableEvents 为每个脚本连接到特定 ID。当客户端发送网络消信息时,与指定 ID 相关的每个 1>Class.BindableEvent1> 会触发。


-- 名为 NetworkManagerServer 的 ServerScript 服务中的模块脚本
local NetworkManagerServer = {}
local networkSignalList = {}
function NetworkManagerServer.GetServerEventSignal(id)
local bindableEvent = Instance.new("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 参数。


-- 复制的第一个地方的本地脚本
local ReplicatedFirst = game:GetService("ReplicatedFirst")
local NetworkManagerClient = require(ReplicatedFirst:WaitForChild("NetworkManagerClient"))
NetworkManagerClient.FireServer("RequestA", "Hello")

下列 Script 连接到网络消息 ID RequestA 并在收到请求时打印带有任何附加参数的语句。


-- 服务器脚本服务中的脚本
local ServerScriptService = game:GetService("ServerScriptService")
local NetworkManagerServer = require(ServerScriptService:WaitForChild("NetworkManagerServer"))
NetworkManagerServer.GetServerEventSignal("RequestA"):Connect(function(player, ...)
print("Received RequestA from", player, ...)
end)