多年前,我完全从零构建了一款回合制游戏,其中包含极其灵活的技能和效果系统。在此之前,我在大型科技公司做过5年移动端UI组件和REST接口开发。

初次尝试

我的第一个迭代版本是一个简单带硬编码技能的状态机,因为当时觉得直接硬编码十几个技能比开发一套复杂系统来支持灵活性要更容易。然而在最初几次试玩时这套方案就崩溃了——设计需求让我反反复复重写技能代码,并且必须等代码写完才能测试。

改进后的系统

为了解决这个问题,我将技能拆分为三个基本模块(实际是四个,最后一个"目标"机制先按下不表):

  • 条件: 何时触发?(目标生命值<50%、技能暴击时
  • 数值: 数值如何计算?(攻击力的150%、固定50点生命值
  • 动作: 改变什么状态?(造成伤害、施加眩晕、治疗

这样一来,技能和效果就变成了数据库中的行记录,无需重新部署游戏就能读取和修改。我甚至还写了一个小型网页应用,方便设计师编辑和验证技能。

问题所在

直白的动作和被动效果在这个系统下运行得很好,但遇到古怪的时机和触发条件时就变得一团糟(比如:反击触发被动,被动触发治疗)。当各种触发堆叠在不同检查和条件之上时(角色死亡/复活尤其令人头疼,因为它还包含游戏结束判定),我的嵌套状态机开始出现边界模糊的问题。

折中方案

我尝试用事件总线和发布/订阅模式将系统重写为事件驱动,但效果也不太理想,只好放弃。最终通过不厌其烦地不断扩展核心引擎的检查和逻辑,总算让引擎能处理所有边界情况——但显然换个完全不同的方案会更好。

我写了一篇完整的架构复盘文章,分析为什么灵活性会催生复杂性。如果你正在纠结战斗执行循环的设计,或者在状态机与事件驱动系统之间举棋不定,这里有完整解析:https://louisxu3.substack.com/p/i-made-turn-based-combat-into-a-database

好奇大家是如何处理技能和效果的复杂性增长的。