对于许多开发者来说,适应 Roblox 脚本的基本挑战在于文件位置和 Script.RunContext 属性的重要性。根据脚本类型、在 Explorer 中的位置和运行上下文,脚本的行为可能会非常不同。某些方法调用可能会失败,游戏中的对象可能无法访问,或者脚本根本无法运行。
这种复杂性的原因在于 Roblox 游戏默认是多人游戏。脚本需要能够仅在服务器上运行、仅在客户端上运行,或在两者之间共享。随着 Roblox 平台的演变,情况变得更加复杂。
脚本类型
Roblox 有三种类型的脚本:
- Script - 根据其位置和 Script.RunContext 属性,在服务器或客户端上运行的代码。
- LocalScript - 仅在客户端上运行的代码。没有运行上下文。
- ModuleScript - 可以在其他脚本中重用的代码。没有运行上下文。
当你创建一个 Script 时,其默认运行上下文为 Legacy,这意味着它 a) 是一个服务器端脚本,并且 b) 仅在服务器容器中运行,例如 ServerScriptService 或 Workspace。
- 如果你将脚本的运行上下文更改为 Server,它现在也可以在 ReplicatedStorage 中运行,但我们不推荐这样做。该位置的内容会复制到客户端,因此它不是服务器端脚本的良好位置。
- 如果你将脚本的运行上下文更改为 Client,它可以在 ReplicatedStorage 中运行。它还可以在 StarterCharacterScripts 和 StarterPlayerScripts 中运行。然而,启动容器会复制到客户端,因此原始脚本 和 复制都会运行,这并不理想。
要更改脚本的运行上下文,请在 Explorer 中选择它,并在 Properties 窗口中更改值。

推荐
将一个 Script,其 RunContext 为 Client,放入 ReplicatedStorage。
将一个 Script,其 RunContext 为 Server,放入 ServerScriptService。
尽可能使用 ModuleScripts 来处理客户端和服务器代码。要求这些模块在你的客户端脚本和服务器脚本中。
这种方法为你的代码提供了一个在客户端和服务器端的单一入口点,简化了组织,并使隔离或禁用有问题的模块变得容易。为每个 ModuleScript 添加一个 start() 函数,以便在开始执行其代码之前加载所有模块:
示例服务器脚本--!strictlocal ServerScriptService = game:GetService("ServerScriptService")local SampleModule = require(ServerScriptService.SampleModule)local AnotherSampleModule = require(ServerScriptService.AnotherSampleModule)SampleModule.start()AnotherSampleModule.start()示例模块--!strictlocal CollectionService = game:GetService("CollectionService")local NPC_TAG = "npc"local SampleModule = {}local function setUpNpc(npc: Instance)-- 初始化每个 NPCendlocal function cleanUpNpc(npc: Instance)-- 当事件触发时运行endfunction SampleModule.start() -- 将函数添加到表中-- 基于标签的设置示例循环for _, npc in CollectionService:GetTagged(NPC_TAG) dosetUpNpc(npc)end-- 当事件触发时运行函数CollectionService:GetInstanceAddedSignal(NPC_TAG):Connect(setUpNpc)CollectionService:GetInstanceRemovedSignal(NPC_TAG):Connect(cleanUpNpc)endreturn SampleModule要共享代码,请在 ReplicatedStorage 中使用 ModuleScripts,并在你的客户端脚本和服务器脚本中要求它们。
如果你的游戏需要,在 ReplicatedFirst 中重复相同的模式,使用一个客户端脚本和最少数量的 ModuleScripts 来实现加载屏幕。要了解有关 ReplicatedFirst 的更多信息,请参见 复制顺序。
稀疏使用 LocalScripts。如果必须使用它们,请将它们放在 StarterCharacterScripts、StarterPlayerScripts、StarterGui 或 StarterPack 中。
这些容器中的脚本会克隆到玩家容器中,而不是从一个位置运行,这可能会使调试变得复杂。使用 ReplicatedStorage 进行客户端代码可以让你在 Output 窗口中单击行并转到 ReplicatedStorage.YourScript(脚本的稳定位置),而不是 Players.YourName.PlayerScripts.YourLocalScript(脚本在运行时被复制到的临时位置)。
避免将脚本直接附加到 Workspace 中的实例。相反,使用 标记实例 并使用 CollectionService 从单个 ModuleScript 中处理它们。
关键例外是如果你在创作者商店中分发模型或包。在这种情况下,你可能需要在实例层次结构中包含脚本;为每个脚本指定一个 RunContext 以消除其运行方式的模糊性。显式设置此属性使模型和包更有可能在各种位置正常工作。
示例项目结构
Plant 参考项目展示了你如何在大型复杂游戏中组织代码。它将绝大多数代码存储为可重用的 ModuleScripts。
脚本位置
| Location | Description |
|---|---|
| Workspace | 表示游戏的3D世界。可以运行直接附加到对象并控制其行为的服务器脚本。 |
| ReplicatedFirst | 包含在其他任何对象之前复制到客户端的对象。此位置非常适合显示加载屏幕所需的绝对最小对象和客户端脚本集。 |
| ReplicatedStorage | 包含复制到客户端和服务器的对象。此位置非常适合具有 Scripts 和 RunContext 为 Client 的客户端 ModuleScripts,以及您希望在服务器和客户端上使用的 ModuleScripts。 LocalScripts 不会从此位置运行。 |
| ServerScriptService | 包含服务器脚本。此位置非常适合需要访问服务器端功能或对象的脚本,例如游戏逻辑和云存储。 |
| ServerStorage | 包含服务器端对象。此位置非常适合不需要在玩家加入游戏时立即复制到客户端的大型对象。脚本不会从此位置运行,但您可以在此存储服务器端的 ModuleScripts。 |
| StarterPlayer ⟩ StarterCharacterScripts | 包含在角色生成时运行的 LocalScripts。 |
| StarterPlayer ⟩ StarterPlayerScripts | 包含在玩家加入游戏时运行的 LocalScripts。 |
| StarterGui | 包含客户端在加载游戏时显示的GUI元素。 LocalScripts 可以从此位置运行。 |
| StarterPack | 通常只包含 Tools,但也可以包括用于设置玩家背包的 LocalScripts。 |
此图显示了哪些 Explorer 窗口位置可以包含客户端脚本。请记住,ReplicatedFirst 和 ReplicatedStorage 可以包含 Scripts,其 RunContext 为 Client,而 Starter[] 容器应使用 LocalScripts。
