本文最后更新于13 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com
重构前:一个什么都管的“总导演”
在重构之前,位于 early_battle/ 目录下的 battle_manager.gd 脚本几乎承担了战斗系统的所有职责。我们可以称它为一个“上帝对象” (God Object),因为它什么都想管:
- 角色管理: 它自己维护着
player_characters和enemy_characters两个数组,负责添加和移除角色。 - 回合管理: 它自己维护着
turn_queue回合队列,并通过_build_turn_queue函数来根据速度排序,决定谁先行动。 - 状态管理: 它通过一个庞大的
_on_state_changed函数来控制战斗的每一个阶段(回合开始、玩家行动、敌人行动、回合结束等)。 - 规则判断: 它用
check_battle_end_condition函数来判断战斗的胜负。 - 视觉表现: 它包含了大量的
_play_*_effect函数,直接通过Tween来实现角色抖动、变色等视觉效果。
这样做的问题很明显:
所有逻辑都耦合在一个文件里,当你想修改其中任何一个功能时(比如改变回合排序规则,或者调整一个技能的视觉效果),你都必须在这个几百行的庞大文件中找到对应的代码,并且要非常小心,以免不经意间破坏了其他逻辑。这使得代码难以维护和扩展。
重构后:一个“总导演” + 多个“专业导演”
重构后,新的 battle/ 目录引入了“模块化”思想。原来的 BattleManager 被拆分,其核心职责被分配给了几个新的、高度专注的管理器(Manager)脚本。
下面我们来看看这些新的“专业导演”各自负责什么:
1. BattleCharacterRegistryManager.gd (角色注册管理器)
- 核心职责: 管理所有参与战斗的角色。
- 接管了什么: 它完全接管了旧
BattleManager中对player_characters和enemy_characters数组的管理。 - 如何工作:
- 现在,当一个角色加入或离开战斗时,不再是直接操作
BattleManager的数组,而是调用这个管理器的register_character和unregister_character方法。 - 它成为了战斗中所有角色的“户籍中心”,提供了如
get_player_team,get_enemy_team,is_team_defeated等方便查询的方法。 - 新的
BattleManager不再关心角色具体是怎么存储的,它只需要向这个管理器查询:“把所有活着的敌人给我”或者“判断一下A和B是不是敌人”。
- 现在,当一个角色加入或离开战斗时,不再是直接操作
2. TurnOrderManager.gd (回合顺序管理器)
- 核心职责: 决定谁在什么时候行动。
- 接管了什么: 接管了旧
BattleManager中的turn_queue和_build_turn_queue的逻辑。 - 如何工作:
- 它的唯一任务就是维护回合队列。当一个大回合开始时,
BattleManager会命令它build_queue()。 - 当需要下一个行动者时,
BattleManager会向它调用get_next_character()。 - 所有关于回合顺序的逻辑(比如按速度排序,或者未来可能加入的“插队”机制)都将在这里实现,与战斗的其他部分完全隔离。
- 它的唯一任务就是维护回合队列。当一个大回合开始时,
3. CombatRuleManager.gd (战斗规则管理器)
- 核心职责: 判断战斗的胜负。
- 接管了什么: 接管了旧
BattleManager中的check_battle_end_condition逻辑。 - 如何工作:
- 它的职责就是定义“怎样算赢?”和“怎样算输?”。
- 它通过查询
BattleCharacterRegistryManager来了解队伍的存活情况,然后根据规则(例如,一方全部阵亡)来判断战斗是否结束。 - 如果未来要增加新的胜利或失败条件(比如“超过20回合算失败”),你只需要修改这个文件,而不用动
BattleManager。
4. BattleVisualEffects.gd (战斗视觉效果管理器)
- 核心职责: 处理所有的视觉和听觉表现。
- 接管了什么: 接管了旧
BattleManager中所有以_play_开头的视觉效果函数。 - 如何工作:
- 它将“逻辑”和“表现”彻底分离。例如,
Character脚本在计算后得知自己受到了10点伤害(这是逻辑),然后它会通知BattleManager,BattleManager再命令BattleVisualEffects去“播放一个受击特效”并“显示一个-10的伤害数字”(这是表现)。 - 这意味着你的战斗逻辑可以完全脱离视觉效果运行。你可以轻松地更换、升级或关闭所有视觉效果,而无需改动任何核心战斗代码。
- 它将“逻辑”和“表现”彻底分离。例如,
5. BattleStateManager.gd (状态管理器)
- 核心职责: 驱动战斗流程的状态机。
- 这个模块在重构前后变化不大,因为它本身的设计已经很好了,就是一个纯粹的状态机。它定义了战斗的各个阶段(
IDLE,START,PLAYER_TURN等),并负责在这些状态间切换。
新的 BattleManager.gd:真正的“总导演”
那么,经过重构后,新的 BattleManager.gd 变成了什么样呢?
它不再亲力亲为,而是变成了一个真正的“总导演”。它的代码现在非常简洁,读起来就像是战斗的“剧本大纲”:
- 它持有对所有“专业导演”(各个子管理器)的引用。
- 在它的
_on_state_changed函数中,它不再执行具体操作,而是发出指令:- 当进入
ROUND_START状态,它对TurnOrderManager说:“建立新的回合队列”。 - 当进入
TURN_START状态,它对TurnOrderManager说:“告诉我下一个该谁行动了”。 - 当一个行动结束后,它问
CombatRuleManager:“战斗结束了吗?”。 - 当一个角色受到伤害时,它对
BattleVisualEffects说:“给他播放一个受伤动画”。
- 当进入
总结:重构带来的巨大优势
这次重构将一个大而全的复杂脚本拆分成了多个小而精的独立模块,最终达成了以下目的:
- 高内聚,低耦合: 每个模块只做一件事,并且做得很好(高内聚)。模块之间的依赖关系变得清晰,不再是乱成一团的毛线球(低耦合)。
- 清晰易读: 当你看到
TurnOrderManager这个文件名时,你立刻就知道它的作用。代码的可读性大大增强。 - 易于维护和调试: 如果回合顺序出错了,你只需要检查
TurnOrderManager。如果视觉效果不播放,你只需要检查BattleVisualEffects。问题定位变得非常简单。 - 易于扩展: 想添加新的战斗规则?修改
CombatRuleManager。想支持更酷炫的技能特效?扩展BattleVisualEffects。添加新功能不再是噩梦。
总而言之,这次重构是一次非常成功的软件工程实践,它将一个难以维护的系统,改造成了一个清晰、灵活、可扩展的模块化系统,为你将来在这个战斗系统上添加更多复杂功能打下了坚实的基础。希望这个讲解能帮助你更清晰地了解这个战斗系统的“总导演”脚本!
