不打无准备之仗,不做无模型之功能。

写代码之前,应该先在心智里把代码跑一遍。这不是空话。

回顾前两篇文章——道具协同设计、地牢射击游戏骨架——里面所有的架构决策,在写第一行代码之前就已经定好了。这不是巧合。好的游戏功能是想出来的,不是调出来的


心智模型到底是什么

心智模型是你对系统运转方式的内在理解。它不是架构图,不是 UML,不是文档。是你闭上眼睛时,能在脑子里看到那行数据流走的样子。

拿武器系统举例。在打开 Godot 之前,先问自己:

当玩家按下滑鼠左键,从输入到子弹命中敌人,中间经过哪些节点?

如果你只能回答「播放动画,生成子弹」,那你的心智模型还不够细。一个合格的模型应该能预测到:

1
2
3
4
5
6
输入 → WeaponController 判断能否开火 → 查询射速/弹匣/后坐力
→ Weapon 提供子弹模板 → 生成子弹 → 子弹飞行
→ 命中检测 → StatModifierManager 查询命中时的修改器
→ 应用效果(伤害 / 元素 / 分裂 / 连锁)
→ 触发事件(ON_BULLET_HIT)
→ 其他道具响应这个事件

能走到这一步,代码还没写,但你已经知道需要什么模块、它们怎么通话、边界在哪。写代码只是把脑子里的东西翻译成 GDScript。


反面教材:边写边想

这是最常见的陷阱:

  1. 想到一个「很酷的道具」→ 开始写
  2. 写到一半发现需要一个新的修改器类型 → 回去改基类
  3. 改基类发现影响了其他四个道具 → 把它们也改了
  4. 测试时发现子弹和碰撞体打架 → 加 body_entered 判断
  5. 加了判断发现性能崩了 → 重构事件系统
  6. 重构到一半发现思路和初版完全不同 → 删掉重来

你认不认得这个循环?我写过太多这种代码了。

症状

  • 一个函数的参数列表越加越长,因为当初没想好数据从哪来
  • 信号连接越连越乱,因为节点之间的通话关系没设计过
  • 一修 Bug 就修出两个新 Bug,因为系统内部的依赖是运行时随机长出来的,不是设计出来的

根因:代码的组织结构由「灵感的当下状态」决定,不受任何全局理解约束。


正确流程:心智模型先行

第一步:问自己五个问题

在写任何代码之前,先回答这五个问题:

问题 意义
这个功能的核心行为是什么? 一句话说清楚,说不清楚就别写
它需要知道哪些东西? 数据依赖
它会产生什么输出? 对系统其他部分的影响
它和其他已有系统怎么通话? 接口定义
边界情况是什么? 空状态、失败路径、极限值

用最近那篇道具协同设计文章里的 TRIGGER 系统举例:

1
2
3
4
5
核心行为:某个事件发生时,执行道具上挂载的效果
需要知道:事件类型、上下文(谁、在哪、伤害多少)
产生输出:可能修改上下文,可能生成新对象
通话方式:StatModifierManager.process_trigger(event, context)
边界情况:递归深度限制(depth > 2 就停)、触发冷却(0.5s

这五个问题答完,代码结构就已经定了。 process_trigger 用什么签名、加什么防护、搁在哪个类里——全都清楚。

第二步:在心智里跑一遍

把前五个问题的答案串起来,在脑子里跑一次完整的流程:

1
2
3
4
5
6
7
玩家开枪 → 子弹命中敌人 → on_hit 信号发出
→ StatModifierManager.process_trigger(ON_BULLET_HIT, context)
→ 遍历 modifiers,筛选 TRIGGER 类型且匹配 ON_BULLET_HIT
→ 对每个匹配的 modifier,检查 depth、cooldown
→ 执行效果(生成分裂弹、触发爆炸、回复血量)
→ 把修改后的 context 传回给事件源
→ 事件源用修改后的 context 继续后续逻辑(伤害结算、死亡判定)

跑得通吗?哪一步会卡住?如果 depth > 2 了但是爆炸还在链式触发——走哪条路?这些问题在写代码之前想清楚,比在调试器里断一个小时有效率得多。

第三步:识别「不知道」的地方

心智模型一定会有盲区。关键在于提前识别它们

  • 「我不知道子弹生成后怎么拿到 modified_context」→ 意味着需要设计一个数据回传机制
  • 「我不知道多个 TRIGGER 的执行顺序」→ 意味着需要优先级排序
  • 「我不知道 depth 参数由谁维护」→ 意味着 context 需要包含递归元信息

每个「不知道」就是一个设计决策点。 卡出所有决策点之后,才开始写代码。写的时候只做翻译工作,不再做设计决策。


什么时候你的心智模型足够好了

一个简单的检验标准:

你能在一行代码没写的情况下,完整回答另一个开发者对这个功能的任何架构问题。

他问:

  • 「分裂出的子弹算不算一次新的 ON_BULLET_HIT?」——你能立刻回答「算,但 depth + 1,超过 2 不再触发」
  • 「换弹时触发的技能会不会打断换弹动作?」——你能立刻回答「不会,技能和换弹是并行流程,技能只是挂了一个额外的回调」
  • 「如果玩家同时持有分裂和爆炸,执行顺序是什么?」——你能立刻回答「按道具获取顺序,但爆炸的触发结果不会再次触发分裂(depth 保护)」

答得出来,模型够了。答不出来,继续想。


心智模型不仅仅用于功能设计

同样的原则可以用在:

  • 重构:动手之前,先在心智里画出重构后的依赖图。如果图和现状差别太大,说明步子太大了,拆成几步
  • 排查 Bug:不要盲打 print。先在心智里复现你认为的完整路径,推理出「应该走到 A 但走到了 B」,再用 print 验证
  • 性能优化:先想数据流瓶颈可能在哪个节点,再 profiler。不然你就是在数据里捞针

从经验中提炼模型

做一阵子游戏开发后,你会积累一批可复用的心智模型:

模式 适用场景
数据栈 属性修改器、Buff/Debuff 系统
事件总线 道具协同、成就系统、UI 更新
对象池 子弹、粒子、敌人(高频创建销毁)
组件树 Entity-Component 架构
状态机 玩家状态、AI 行为、动画控制
资源注册表 导出后资源加载路径管理

建立心智模型的最佳练习就是:在下一篇博客里写「我为什么这样设计」而不是「我做了什么」。 前者训练模型,后者只是记录。你之前那两篇文章——道具协同和地牢骨架——恰好是前者,这也是它们读起来有说服力的原因。


一句话总结

你的代码不会比你的心智模型更好。你想不清楚,就写不清楚。

写代码只是把脑子里的结构翻译成行号。如果你打开编辑器的时候还不知道 process_trigger 该接收几个参数、该返回什么、由谁调用——那就是还没准备好。先合上电脑,想清楚再打开。

不打无准备之仗。