0%

状态模式和职责链模式

故事背景

最近在项目中负责任务系统相关的内容,刚重写了整个任务系统,趁热对过程中的心得做一点总结。

原任务系统——状态模式

原来的任务系统是一个多层状态机,第一层控制不同大类任务之间的切换(如主线任务、分支任务),第二层控制不同种类任务之间的切换(如刷怪任务、剧情任务),第三层控制一个任务下不同行为状态之间的切换(如跑到某地、刷怪、对话等行为)。三层状态机中,中间层使用了状态模式实现,其余两层使用判断进行状态切换。在游戏设计之初,这种设计方法可能可以满足策划的需求,但随着需求的增多,问题就逐渐暴露出来,大致可以总结出几个问题:

  • 代码冗余,可读性差:在不同种类的任务中可能会存在相同的行为,如刷怪任务和NPC对话任务都有跑到指定地点的行为,为了能让代码复用,这些公共的行为就被提出来写在了Manager中供状态调用,但是在不同任务中,执行完该行为后切换的下一个行为又是不同的,因此在行为的方法中又需要对当前行为进行判断。整体上来看,表示不同种类任务的状态类中只有状态的切换,具体的逻辑都在Manager中,使得Manager类十分庞杂,阅读时需要在状态类和Manager之间来回切换,不是非常直观。
  • 灵活性差、无法应对多变的需求:这一点是状态模式本身的特点导致的,状态模式在类的设计中就确定了状态之间的转换,其转换关系是确定的,规范的状态模式客户都不知道状态之间的切换的结构,这样的设计非常稳定,但缺点就是灵活性差,耦合度高。但在游戏制作过程中,任务相关的需求总是多变的,比如策划突然想在前往某地和刷怪之间增加一个播放Timeline的环节,那么使用状态模式设计的任务系统就需要添加一个名为播放Timeline的状态,需要在原前往某地的状态切换中将下一个状态从刷怪改为播放Timeline,这就违反了设计原则中的开闭原则

新任务系统——职责链模式

在设计新任务系统时,考虑到一个任务中的步骤可能会经常变化,因此需要使用更加灵活的设计模式来应对变化。新的系统主体采用职责链模式,定义好各种事件,每个事件中处理进入事件、更新事件、退出事件等方法,在执行一个任务前,根据策划的配置从事件库中取出需要执行的事件包装成事件链,这一事件链就构成了这一个任务的整体流程,接着就只需要按照顺序依次触发事件的执行。这样设计的优点在于:

  • 减少判断,流程更清晰:职责链模式在环境类中就组装好事件序列,在具体的事件类中,不需要知道后续的事件做什么,减少if-else判断,只需要维护Execute() Update() OnFinish()等方法,更加纯粹。
  • 灵活性高,使策划能够随意调整任务流程。

总结

  • 状态模式适用于状态间的切换关系确定且不是很复杂的情况,也适用于一些需要根据用户输入改变状态的情况,其本质相当于if-else。
  • 职责链模式适合事件执行顺序复杂多变,但执行开始之前事件序列已经确定的情况,其本质相当于switch-case。