LudumDareGameJam复盘
绪论
第 59 届 Ludum Dare Game Jam 的主题是“信号”。这次我们选择做一个调酒小游戏,背景设定为老板在月球上远程操控机器人管理酒馆。玩家需要提前把材料、扫把、补货动作排进执行槽,点击运行后,机器人会按照槽位顺序自动执行,玩家不能中途修改队列。
这个设定和主题“信号”主要有两层关联。第一层是玩法层面:玩家需要阅读客人的表达,从客人的话语中理解他们真正想要的饮料,本质上是在解析客人发出的需求信号。第二层是叙事层面:月球老板远程操控机器人经营酒馆,本身也对应着地月之间的信号传输。
核心玩法设计
本作的核心玩法不是单纯调酒,而是一个带时间压力的排程系统。每轮开始时,系统生成 1 到 2 名客人及其饮品需求,同时给每个需求设置截止时间;玩家进入编辑状态后,需要把材料、扫把、补货动作拖入 7 个执行槽。点击运行后,队列会从左到右依次执行,材料会进入酒杯,三个材料合成一杯酒;如果成品匹配当前客人的需求,则获得分数,否则会产生失败反馈。扫把用于处理清理需求,货架用于补满材料库存。
这个设计的关键在于,玩家点击运行后就不能再修改队列,因此每次编辑阶段都像是在做一次小型计划:要不要先满足快超时的客人,要不要插入扫把避免清理需求失败,要不要牺牲一格槽位去补货,以及上一轮剩余材料会不会影响下一轮的酒杯顺序。相比实时反应,我更希望玩家感受到的是“远程操控机器人”的延迟感和计划感,这也更贴合本届主题“信号”。

核心循环与早期程序设计
为了让这个玩法能在程序里稳定运行,我最早把它整理成了“一个周期的逻辑”。它更接近一份状态机和结算规则文档:游戏由开始状态、编辑状态、运行状态三个阶段组成,通过 score、totalTime、限制时间数组、材料库存和酒杯列表驱动循环。
基础规则是:score 初始为 10(可调),分数小于等于 0 时游戏结束;totalTime 初始为 0,表示游戏内已经消耗的总时间;限制时间数组记录当前所有未完成需求的截止时间。这里的限制时间不是倒计时,而是绝对时间,即生成需求时在 30-50 之间取一个随机数,再加上当前 totalTime。
开始状态会生成 1 到 2 个客人 NPC,并为每个客人随机目标饮品和限制时间。同时系统会处理上一周期留下来的状态:第一轮时所有材料数量设置为最大值;如果上一周期槽位中还有未消耗完的材料,则按顺序填回槽位,并标记为不能编辑。这样上一轮的规划结果会延续到下一轮,而不是每轮完全重置。
编辑状态是玩家主要操作的阶段。场景中有一个 7 格执行槽、一个包含 10 种材料的材料列表、一个最多容纳 2 个酒杯的酒杯列表,以及扫把和货架两个特殊动作。每个材料都有计数器,计数器为 0 时不可拖动,并降低透明度作为提示。玩家需要把材料、扫把、货架拖入执行槽,决定下一轮的执行顺序。
酒杯列表是这套系统里比较关键的一部分。每个酒杯最多容纳 3 个材料,槽位中的材料会按执行顺序依次进入酒杯,从下往上填充。一个酒杯装满 3 个材料时,就立即结算为一杯完成的饮品;如果还有后续材料,则继续进入下一个酒杯。因为所有饮品都固定由 3 个材料组成,所以“酒杯正好装满”就是一次饮品结算点。
点击运行按钮后,游戏进入运行状态,系统沿 7 个槽位从头到尾依次执行。执行到扫把时,totalTime 增加固定时间,并视为完成一次打扫需求;如果打扫需求在截止时间前没有完成,则会扣分。执行到货架时,totalTime 增加固定时间,并把所有材料计数器恢复到最大值。执行到普通材料时,totalTime 增加固定时间,并把材料加入当前酒杯;如果这个材料正好让酒杯满 3 个,就立刻进行饮品匹配。
饮品匹配时,系统根据当前酒杯中的 3 个材料得到对应饮品,再到当前客人需求中查找匹配项。匹配成功则完成该客人需求,得分 +1;如果多个客人需求相同,则优先满足限制时间更近的那个。没有匹配到需求时,最初设计中视为做错饮品并扣 1 分。
每执行完一个槽位后,系统都会统一检查一次所有未完成需求是否超时。伪代码大致如下:
1 | |
这里有一个很重要的约束:超时需求只能扣一次分,已经完成或已经失败的需求不能重复结算。每次加分或扣分后,都需要检查 score 是否已经小于等于 0,如果是,则立即进入游戏结束流程。
回头看,这套设计的核心其实不是“调酒”,而是把客人需求、材料库存、执行顺序、时间压力和跨周期残留材料组织成一个可结算的循环。它让玩家在编辑阶段做计划,在运行阶段承担计划的结果,这也是本作最贴近“远程信号操控”这个主题的地方。
问题与调整
1. AI 实现方式偏离 Unity 工作流
**问题:**最核心的问题是,我没有明确要求 AI 遵守 Editor First / Prefab First,导致它倾向于用集中控制器和运行时代码生成场景对象。结果是后续修改和扩展都变得困难,也引发了很多 bug。
**解决方法:**手动解耦集中控制器脚本,重新制作和拆分预制体,把部分运行时生成逻辑改回 Unity 场景和 Prefab 配置。
**思考:**优先列出核心程序设计思路是对的,但不能直接把规则文档丢给 AI。更合理的做法是先在 Unity 里搭建出所需物体和预制体,再让 AI 根据明确的组件边界补充脚本。
2. 限制时间生成过于随机
**问题:**设计文档中把限制时间设置为 totalTime + 30~50 的随机值。这会导致多个需求的截止时间堆在一起,出现部分任务理论上无法完成的情况。
**解决方法:**按队列可完成时间来推算截止时间。先估算每个需求本身需要多少执行时间,再按顺序排出预计完成时间,最后在这个时间后面加缓冲时间。
**思考:**带时间限制的任务不能只看“随机范围是否合理”,还要看它和玩家实际可执行路径是否匹配。尤其是这种排程玩法里,玩家一次只能按槽位顺序执行动作,所以截止时间必须围绕执行成本和队列顺序来设计,而不是简单随机。
3. 限制时间数组不足以表达需求状态
**问题:**最早设计中只用“限制时间数组”记录需求截止时间,但实际实现时发现,只记录时间不够。需求还需要知道自己的类型、目标内容、完成状态和失败状态,否则超时结算、扫把需求和客人反馈都会变得混乱。
**解决方法:**后续把需求从单纯的时间数组改成完整的需求对象。每次执行完槽位后,遍历当前活跃需求,只对未完成且未失败的需求做超时判断,超时后立刻标记为失败,避免重复扣分。
**思考:**只要一个数据会跨多个阶段被结算,就不能只用一个基础类型保存。像这种带生命周期的任务,至少要记录身份、目标、状态和截止时间。否则后续功能一多,逻辑会很快失控。
总结
这次 Ludum Dare 最大的收获,是把“调酒”拆成了一个可以结算的排程系统:玩家读懂客人的需求信号,再通过执行槽安排机器人行动。虽然最终实现里留下了集中控制器、规则调整和场景搭建方式上的问题,但这些问题也暴露出了后续需要改进的方向:Unity 项目中应该优先明确场景结构、Prefab 和组件边界,再让 AI 辅助完成具体脚本。
从结果来看,这个项目的核心闭环是成立的。后续如果继续迭代,我会优先处理两件事:一是把规则说明和 UI 反馈做得更清楚,二是继续拆分核心控制器,把需求、执行、配方和表现逻辑分开。