游戏设计模式 #6 行为型模式提炼总结

作者:浅墨
2017-02-18
4 6 0

引言

紧接着命令模式与序列型模式,本文继续整理行为型模式。

行为型模式 Behavioral Patterns

本章的模式可以帮助我们快速定义和完善多种多样的行为:

类型对象定义行为的类别而无需完成真正的类。
子类沙盒定义各种行为的安全原语。
字节码,将行为从代码中拖出,放入数据。

字节码模式 Bytecode

字节码模式,将行为编码为虚拟机器上的指令,来赋予其数据的灵活性。从而让数据易于修改,易于加载,并与其他可执行部分相隔离。

要点

  • 字节码模式:指令集定义了可执行的底层操作。一系列的指令被编码为字节序列。 虚拟机使用中间值堆栈 依次执行这些指令。 通过组合指令,可以定义复杂的高层行为。
  • 可以理解为项目中的转表工具,将excel中的数据转为二进制数据,并读取到工程中,如在项目中使用googleprotobuf或json。
  • 字节码类似GOF的解释器模式,这两种方式都能让我们将数据与行为相组合。其实很多时候都是两者一起使用。用来构造字节码的工具会有内部的对象树,而为了编译到字节码,我们需要递归回溯整棵树,就像用解释器模式去解释它一样。唯一的不同在于,并不是立即执行一段行为,而是生成整个字节码再执行。

使用场合

这是GPP一书中最复杂的模式,不能轻易的加入到游戏中。 当我们需要定义很多行为,而游戏实现语言因为以下原因不能很好地完成任务时,就可以使用字节码模式:

  • 这些行为过于底层,繁琐易错。
  • 这些行为遍历起来很缓慢,导致编译时间长。
  • 这些行为太受依赖。如果想保证行为不会破坏游戏,你需要将其与代码的其他部分隔开。

如果是上述的这些情况,就比较适合使用字节码模式。

但需要注意,字节码比本地代码慢,所以最好不要用于引擎对性能敏感的部分。

引申与参考

  • Lua的内部实现就是一个非常紧凑的,基于寄存器的字节码虚拟机。
  • Kismet是个可视化脚本编辑工具,应用于Unreal引擎的编辑器UnrealEd。
  • 本节内容相关的英文原文
  • 本节内容相关的中文翻译

子类沙箱模式 Subclass Sandbox

用一系列由基类提供的操作定义子类中的行为。

要点

子类沙箱模式:基类定义抽象的沙箱方法和几个提供操作的实现方法,将他们设为protected,表明它们只为子类所使用。每个推导出的沙箱子类用提供的操作实现了沙箱方法。

使用场合

子类沙箱模式是潜伏在编程日常中简单常用的模式,哪怕是在游戏之外的地方。 如果有一个非虚的protected方法,你可能早已在用类似的技术了。

沙箱方法在以下情况适用:

  • 你有一个能推导很多子类的基类。
  • 基类可以提供子类需要的所有操作。
  • 在子类中有行为重复,你想要更容易的在它们间分享代码。
  • 你想要最小化子类和程序的其他部分的耦合。

引申与参考

当你使用上文中介绍到的更新模式时,你的更新函数通常也是沙箱方法。

这个模式与GOF模板方法正好相反。两种模式中,都使用了一系列受限操作实现方法。使用子类沙箱时,方法在推导类中,受限操作在基类中。使用模板方法时,基类有方法,而受限操作在推导类中。

你也可以认为这个模式是GOF外观模式的变形。 外观模式将一系列不同系统藏在简化的API后。使用子类沙箱,基类起到了在子类前隐藏整个游戏引擎的作用。

类型对象模式 Type Object

创造一个类A来允许灵活的创造新的类,而类A的每个实例都代表了不同类型的对象。

要点

类型对象模式:定义类型对象类与有类型的对象类。每个类型对象实例代表一种不同的逻辑类型。每种有类型的对象保存描述它类型的对类型对象的引用。
类型对象的基本思想就是给基类一个品种类(breed类),而不是用一些子类继承自这个基类。所以我们在做种类区分的时候就可以只有两个类,怪物类monster和品种类breed,而不是monster,dragon,troll等一堆类。所以在此种情况下,游戏中的每个怪物都是怪物类的一个实例,而实例中的breed类包含了所有同种类型怪物共享的信息。

使用场合

这个模式在任何你需要定义不同“种”事物,使用不当会让你的系统过于僵硬。而下面两者之一成立时,就非常适合使用:

  • 不知道后续还需什么新类型。(举个例子,如果你的游戏需要支持增量更新,让用户下载后续新包含进来的怪物品种)
  • 想要不改变代码或不重新编译就能修改或添加新类型。

引申与参考

这个模式引出的进阶问题是如何在不同对象之间共享数据。以不同的方式解决同一个问题的是GOF设计模式中的原型模式(prototype pattern)。

类型对象是GOF设计模式中享元模式的亲兄弟。两者都让你在实例间分享代码。使用享元,意图是节约内存,而分享的数据也许不代表任何概念上对象的“类型”。而使用类型对象模式,焦点在组织性和灵活性。

这个模式和GOF设计模式中状态模式有很多相似之处,两者都是委托了对象的部分定义给另外一个对象。