Skip to content

10 整理代码并交付小游戏 ​

把上一课已经能玩的函数拆成模块,通过同一套操作检查重构前后行为,再交付完整工程。

这课不加新规则。你把第 09 课的函数整理到模块,让入口更容易读,并核对整理后仍是同一个推球游戏。

开始前检查 ​

先用第 09 课完成一轮“触球→命中→个人得分→结算→点击重开”,记下结果。如果已有错误,先修复再整理。场景、HUD 和可选表现保持搭建指南中的配置。

1. 安装最终六文件工程 ​

文件:下表全部六个 Lua 文件。操作:下载第 10 课完整工程,停止试玩,按同路径整文件替换或新增。两个 main.lua 必须替换,不能在原来的完整逻辑后面再追加模块调用。

文件本课职责相对第 09 课
server/main.lua调用回合模块初始化、开始、关闭清理替换为短入口
server/round.lua原服务端的回合、归属、计分、协议和可选特效新增,把已有函数移入
client/main.lua调用 HUD 模块初始化替换为短入口
client/hud.lua原客户端的快照、显示、按钮和音效新增,把已有函数移入
common/remote_events.lua两端共用通道名保持
data/game_config.lua两端共用配置保持

预期:保存同步后仍使用 20 秒回合、个人计分、结算等待和全场重开。方块仍在起点附近随机选择 Z 轴偏移,允许抽到 0 或重复位置。整理文件不应该改变任何一条规则。

模块化工程依赖全景图 ​

text
client/main.lua ──require──> client/hud.lua        (入口只做组装)
server/main.lua ──require──> server/round.lua      (入口只做组装)

client/hud.lua   ──require──> common/remote_events.lua   (双端通道定义)
client/hud.lua   ──require──> data/game_config.lua       (数值配置)
server/round.lua ──require──> common/remote_events.lua   (双端通道定义)
server/round.lua ──require──> data/game_config.lua       (数值配置)

运行时双端通过 common/remote_events.lua 里的 RemoteEvent 通道交换快照与请求:服务端 Snapshot:FireClient 下发,客户端 Request:FireServer 上报;hud.lua 只管显示与按钮,round.lua 只管回合、积分与球体复位。

2. 读懂服务端入口 ​

文件:server/main.lua。操作:确认完整内容如下。

lua
-- @runtime server
-- ============================================================================
-- 模块职责:服务端主入口(Entry Point)
-- 核心目标:
--   1. require 引入业务管理模块 server.round,保持入口脚本极简
--   2. 调用 Round.Init() 初始化场景与状态机,失败时安全拦截
--   3. 启动对局循环,并在服务器关闭或模块卸载时调用 Round.Destroy() 释放资源
-- ============================================================================

local Round = require("server.round")

-- 初始化场景与状态
if not Round.Init() then
    print("[推球][ERROR] 初始化失败,请核对球体和方块")
    return
end

-- 启动对局流程
Round.Start()

-- 绑定生命周期退出清理
game:BindToClose(function()
    Round.Destroy()
end)

return {
    Destroy = function()
        Round.Destroy()
    end,
}

require 得到 Round 这个模块表;它还没有建立监听。Round.Init() 才查场景、准备状态并绑定事件,找不到球或方块时返回失败。Round.Start() 让第一局正式开始。

服务端关闭时调用 Round.Destroy(),断开碰撞、Heartbeat、请求和玩家离开监听,并让尚未结束的表现回调失效。

3. 读懂客户端入口 ​

文件:client/main.lua。操作:确认完整内容如下。

lua
-- @runtime client
-- ============================================================================
-- 模块职责:客户端主入口(Entry Point)
-- 核心目标:
--   1. require 引入界面业务模块 client.hud
--   2. 执行 Hud.Init() 挂接 HUD 渲染、快照接收与音效播放
--   3. 导出 Destroy 接口以供生命周期管理
-- ============================================================================

local Hud = require("client.hud")
Hud.Init()

return {
    Destroy = function()
        Hud.Destroy()
    end,
}

Hud.Init() 完成原来的“先监听、再请求、等待 UI、显示结果”。UI 暂未就绪仍会重试。本地玩家离开时统一清理;Hud.Destroy() 也可由加载它的上层逻辑主动调用,断开消息、玩家离开、Heartbeat 和按钮连接。入口返回 Destroy 便于主动卸载,它不会因 return 而自动调用。模块不会销毁引擎管理的根节点或编辑器中准备的 UI。

两个模块的 Init() 开头都会先调用自身 Destroy(),避免重复初始化时重复监听。在模块文件里搜索上一课的 onCubeTouched、updateHud,你会看到原来的逻辑只是换了位置。

同一次试玩内重新初始化回合模块,局号和修订号继续递增;同一球体、方块仍使用第一次记录的起点。这样客户端能接受新状态,也不会把已经随机移动的位置当成下一次安装的起点。只有停止试玩、开始新的 Lua 运行环境时,才从第 1 局重新开始。

4. 按同一套动作验收 ​

文件:不改代码,使用最终检查点。操作:按下表逐项完成,不用“没有报错”替代实际操作。

操作必须观察到
新一次试玩只有一局开始,HUD显示当前局状态
人物触球后命中本人积分加 1,球复位;方块的 Z 坐标在起点左右 3 范围内,允许回到起点或留在原位
球没有有效触碰者时命中不给玩家加分,球仍复位
等到结束后再碰撞不再加分,按钮出现
连点再来一局全场只开始下一局一次,积分清零
连续完成三局没有叠加的回调、旧归属或重复开局
缺失可选特效或关闭音效规则与 HUD 继续工作
有真实第二客户端时中途加入收到当前回合和自己的分数,重开后两端同局

多人项需要实际联机环境。若只有单客户端,先完成其余项,并把多人项明确留作待验证,不能用模拟玩家对象宣称通过。

自己改一处 ​

文件:data/game_config.lua。操作:把每局时长改为 10,保持两个 main.lua 不动。

预测:模块化以后是否要改多个地方?提示:回合模块仍读取配置。答案:同步并重新试玩后,时长变为 10;HUD与结算读取同一服务端结果,无需另改客户端时间。最后恢复 20。

交付给下一个人 ​

发送最终工程包以及你实际完成的地图/UI配置。本包对应本系列的 10 课主线;SE UGC Lua 开发系列的第 22 章另有独立工程。

说明包里有 Lua 文件和搭建步骤,没有已保存地图、UI 数据或资源文件。让对方在专用空白世界版地图按随包 SCENE.md 搭建,并按上表独立运行一次。能在新地图重建,才说明安装说明足够清楚。

你已经完成默认主线。接下来按需求选择存档与排行榜、物理与射线、角色系统或更多项目挑战。

API:Game、Connection、RemoteEvent、EUIManager。工程化深读:可复用系统。