主题
第 1 章:脚本工程、目录结构与运行入口
建立可运行的 SE Lua 工程,理解 client、server、common 与 data 目录边界。
你会学到什么
- 如何从编辑器生成 Lua 脚本工程。
- 默认生成的
client、server、common三个目录分别负责什么。 - 什么时候会出现
data/、EggyAPI.lua,以及它们和 VS Code 插件“导出数据 / 导出 API”的关系。 official、.codemaker、.vscode这类目录为什么不要随便改。- 如何写最小脚本,确认 client 和 server 都在运行。
- 为什么
main.lua应该只做入口组装。 - 世界编辑器的 Lua 运行环境与原点版有哪些差异(库裁剪、表键、
math库)。
从编辑器生成脚本工程
- 在编辑器左上角选择 LUA - 蛋仔开发助手。
- 在弹窗左下角选择 生成工程。
- 选择本地路径,生成成功后用 VS Code 打开。
- 在 VS Code 里确认默认源码目录只有
client、server、common。
如果你还没有在 VS Code 插件里点过其它按钮,先不要疑惑为什么没有 data/ 或 EggyAPI.lua:
| 动作 | 生成或更新什么 | 用途 |
|---|---|---|
| 生成工程 | client/、server/、common/ | 脚本入口和双端共享模块 |
| 导出数据 | data/,以及 UINodes.lua、Prefab.lua 等导出表 | UI 节点、预设、资源索引等编辑器数据 |
| 导出 API | EggyAPI.lua | 给 VS Code / Lua 语言服务使用的 API 提示与类型辅助 |
不同编辑器版本生成的辅助目录可能略有差异,但第一轮主线代码先围绕 client/server/common 组织;需要 UI、Prefab 或资源导出时,再通过“导出数据”获得 data/。
推荐工程结构
text
LuaSource_<project_name>/
.codemaker/ -- 官方编程助手规则、技能、工具
.vscode/ -- VS Code 工作区配置
EggyAPI.lua -- 点击“导出 API”后生成,供 VS Code 提示使用
data/ -- 点击“导出数据”后生成;也可手动放自己的静态配置
CustomAsset.lua -- 编辑器导出的自定义资产
OfficialAsset.lua -- 编辑器导出的官方资产索引
Prefab.lua -- 编辑器导出的预设资源
UINodes.lua -- UI 编辑器导出的节点表
items.lua -- 你自己写的静态配置
common/
remote_events.lua -- 双端共享事件定义
client/
main.lua -- 客户端入口
server/
main.lua -- 服务端入口
official/ -- 若存在,视为官方脚本目录
log.txt -- 某些版本/导出流程生成的试玩日志;实际位置以当前工具为准目录职责
| 目录 | 运行端 | 能引用谁 | 不能引用谁 | 典型内容 |
|---|---|---|---|---|
data/ | 双端可 require | 必要时引用其它纯 data 模块 | common/client/server | 纯配置、导出表、资源 URI |
common/ | 双端可 require | data | client/server | 工具函数、RemoteEvent 定义、常量 |
client/ | client | data/common/client | server | UI、输入、本地表现、相机 |
server/ | server | data/common/server | client | 计分、校验、存储、回合、跨服 |
official/ | 由官方生成 | 不建议引用或修改 | 不修改 | 官方脚本或辅助内容 |
data 和 common 的区别很重要:
data可能来自“导出数据”,也可以由你手动创建配置表;它只放“数据”:table、数字、字符串、资源 URI,不要在顶层获取 Service、绑定事件或启动逻辑。common可以放双端工具,也可以集中定义 RemoteEvent,但仍不能依赖client或server。
EggyAPI.lua 不属于玩法代码,不要在业务脚本里 require("EggyAPI")。它的作用是让编辑器和 VS Code 更懂当前 SDK。
require 的规则
require 必须带目录前缀,并且只能引用当前目录规则允许的模块。下面四条是分别放在合适运行端的写法,不是让你把它们全部复制到同一个文件:
- 双端都可用:
require("data.items")、require("common.remote_events")。 - 只放客户端:
require("client.hud")。 - 只放服务端:
require("server.round_manager")。
错误写法:
lua
local Config = require("items") -- 找不到 data/items.lua
local Hud = require("server.hud") -- client 不能 require server
local Service = require("GameAPI") -- game、Enums、Data 类型都是全局,不 require全局对象直接使用:game、Enums、Vector3、Color、Quaternion、CFrame、RemoteEvent 等都不需要 require。如果看到旧规则要求 math.Vector3(...),以当前教程和运行时验证为准:正文统一使用全局 Vector3(...)。
Lua 运行环境(与原点版的差异)
世界编辑器的脚本同样运行在安全沙盒中,出于安全考虑裁剪了部分标准库。这部分裁剪与原点版(帧同步)一致;但在语言特性与 math 库上,世界编辑器不做原点版那样的定制。
库的裁剪(与原点版共通)
以下标准库被移除,脚本中不能使用:
ioospackagedebug
全局变量、函数和标准库按白名单管理。下面只列本教程会直接用到的常用入口,不宣称是运行包的完整清单;具体可用范围仍以目标运行包和当前开发助手提示为准。旧 Lua 教程里出现、但目标环境没有提供的库或函数不能直接照搬。
| 全局变量/函数 | 限制 |
|---|---|
_VERSION | 无 |
error / assert | 无 |
ipairs / pairs / next | 无 |
pcall / xpcall | 无 |
tostring / tonumber / type | 字符串转数字失败时 tonumber 返回 nil,使用前要准备默认值 |
select | 无 |
coroutine | 协程标准库;入门异步优先使用第 6 章的 Task 服务 |
require | 仅限加载脚本工程目录下的其他 Lua 模块 |
setmetatable | 不可使用 __mode 和 __gc 域 |
getmetatable | 仅可获取 table 的 metatable |
traceback | 等价于 debug.traceback |
print | 无 |
string / table / utf8 / math | 保留的标准库入口;具体成员仍以目标运行包为准 |
例如第 11 章会用 tonumber(value) or 0 把可能的字符串输入规整成数字;第 6 章则优先用 Task:Spawn / Task:Wait 讲协程,不要求新手直接操作 coroutine。
main.lua 应该做什么
client/main.lua 和 server/main.lua 会自动执行,但它们不应该越来越长。推荐原则:
main.lua负责 require 模块。main.lua负责调用Init()/Start()。- 玩法细节放进 Manager 或模块。
- UI 模块不要在 require 顶层自动绑定事件,应显式调用
Init()。
本章先用下面的 TestCommonUtil 完成最小入口。到第 19 章再拆 Manager,第 22 章会给出包含 RoundManager.Init(...)、Start()、Destroy() 的完整模块,避免你现在复制一个依赖尚未创建文件的半成品入口。
最小示例:确认双端都在运行
common/TestCommonUtil.lua
运行端:common
lua
local TestCommonUtil = {}
function TestCommonUtil.PrintCurrentSide()
local RunService = game:GetService("RunService")
if RunService:IsClient() then
print("[SE Lua Guide] 这里是客户端 client")
elseif RunService:IsServer() then
print("[SE Lua Guide] 这里是服务端 server")
else
print("[SE Lua Guide] 当前不是 client/server 试玩运行域")
end
end
return TestCommonUtilclient/main.lua
运行端:client
lua
local TestCommonUtil = require("common.TestCommonUtil")
print("[SE Lua Guide] Hello, SE Client!")
TestCommonUtil.PrintCurrentSide()server/main.lua
运行端:server
lua
local TestCommonUtil = require("common.TestCommonUtil")
print("[SE Lua Guide] Hello, SE Server!")
TestCommonUtil.PrintCurrentSide()期望结果
运行游戏后,在编辑器输出窗口中应该看到类似日志。部分版本或导出流程也会生成 log.txt;没有这个文件不代表脚本没运行,应以当前编辑器输出和工具实际提供的日志入口为准。
text
[SE Lua Guide] Hello, SE Server!
[SE Lua Guide] 这里是服务端 server
[SE Lua Guide] Hello, SE Client!
[SE Lua Guide] 这里是客户端 client日志顺序可能不同,这是正常的。客户端和服务端不是同一条顺序执行的脚本。
如果只看到其中一端,按这个顺序排查:
- 确认文件名分别是
client/main.lua和server/main.lua,没有放反目录。 - 确认
require("common.TestCommonUtil")的目录、文件名和大小写完全一致。 - 从日志中找到第一条错误;后续错误往往只是第一处加载失败的连带结果。
- 修复后重新试玩,不要只看旧日志判断新代码。
来源优先级
本系列会参考 Meta 契约、.codemaker 规则、published API 文档和真实运行验证。遇到冲突时按下面顺序处理:
- 当前 Meta 契约:
se-lua/sdk/meta/、meta_reference.txt和 meta-spec 派生规则。 - 真实运行验证结果,用于验证行为,不新增未进 Meta 的作者 API。
- published/generated 文档、
.codemaker中仍然有效的工程规则。 - 与当前 API 体系和版本一致的外部正式官方文档,可用来交叉核对 Meta 描述,但不能替代 Meta 建立新能力。
- 旧手册或旧规则中的历史写法,只能帮助理解概念和迁移背景。
判断“正式官方文档”时还要看它属于哪套 API。若页面主要使用 LuaAPI / GameAPI / GlobalAPI,而当前工程使用 game:GetService(...) 和 Service / Unit / Data / Enum,那么两者不是可以逐成员互相覆盖的同一版契约。例如 .codemaker 的工程目录和 require("common.xxx") 规则仍然有效;但旧手册中的 math.Vector3(...) 已被当前 Meta 和运行时验证支持的全局 Vector3(...) 规范替代。
常见错误
错误:把运行逻辑写进 data
data/items.lua 只应该返回配置表。如果在里面 game:GetService()、绑定事件、创建 Unit,client 和 server require 它时都会产生副作用。data/ 即使是你手动创建的,也要遵守同样规则。
错误:client require server 模块
客户端不能直接引用服务端模块。需要跨端通信时,第 8 章会使用 RemoteEvent。
错误:修改 official 目录
如果工程里有 official/,把它当作官方生成内容。项目逻辑写在 client/server/common/data,不要把自定义玩法塞进 official。
错误:把 EggyAPI.lua 当运行时代码
EggyAPI.lua 是点击“导出 API”后生成给编辑器/VS Code 看的辅助文件。它不是玩法模块,不应被 client/main.lua 或 server/main.lua require。
错误:main.lua 堆满所有玩法
第一天能跑,第三天就难改。超过一个小功能后,就把计分、回合、UI、技能等拆成模块。
练习任务
- 如果工程里还没有
data/,先手动创建它,或在插件里点击“导出数据”生成它。 - 创建
data/round_config.lua,返回{ RoundSeconds = 60 }。 - 在 client 和 server 中分别 require 并打印
RoundSeconds。 - 创建
common/side.lua,封装RunService:IsClient()/IsServer()。 - 点击“导出 API”,确认根目录出现
EggyAPI.lua,但不要在运行时代码里 require 它。 - 在确认前五步通过后,临时把一个 require 路径写错,观察第一条加载错误;随后立即恢复正确路径并重新试玩,确保工程回到可运行状态。
本章验收标准
- [ ] 我知道如何从编辑器生成脚本工程。
- [ ] 我知道默认生成工程先只有
client/server/common。 - [ ] 我知道
data/来自“导出数据”或手动创建,EggyAPI.lua来自“导出 API”。 - [ ] 我知道
client/server/common/data的职责和依赖方向。 - [ ] 我知道
official和.codemaker不应作为玩法代码目录。 - [ ] 我知道
require要写目录前缀。 - [ ] 我能通过日志确认 client 和 server 都在运行。
本章产物
本章产物是一个能启动的脚本工程骨架:client/main.lua、server/main.lua 和 common 模块都能被正确加载;如果你需要配置表或 UI 节点映射,还应能通过手动创建或“导出数据”得到 data/,并知道“导出 API”生成的 EggyAPI.lua 只服务于编辑器提示。
本章 API 对照
下一章预告
现在你已经能运行脚本了。下一章我们将学习如何在 Lua 中找到编辑器里摆放的对象,以及为什么位置、颜色、枚举不能用普通 table。
