《游戏编程模式》--行为模式--学习

文章探讨了在游戏设计中如何使用行为模式,如字节码和解释器模式来定义和执行游戏行为。字节码允许将行为存储在数据库中,提高灵活性和安全性,而解释器模式用于执行这些行为。子类沙箱模式则是一种在基类中提供操作,由子类实现具体行为的方法,减少耦合。类型对象则用于创建灵活的逻辑类型,共享行为和数据。
摘要由CSDN通过智能技术生成

在线阅读地址:

行为模式 · 游戏设计模式 (tkchu.me)

参考文章:

GameDesignPattern_U3D_Version/Assets/008BehavioralPatterns at master · TYJia/GameDesignPattern_U3D_Version · GitHub

行为模式

类型对象定义行为的类别而无需完成真正的类。 子类沙盒定义各种行为的安全原语。 最先进的是字节码,将行为从代码中分离,放入数据文件中。

  • 字节码
    • 享元模式、原型模式中,不同的属性被存储在数据库中,而字节码是将行为存在数据库中
    • 可用于实现可视化脚本编辑工具
  • 子类沙箱
    • 子类使用基类方法,或在基类方法上扩展
  • 类型对象
    • 其实是享元模式、原型模式的一种应用,以不同数据(而不是不同类)区分对象类型

字节码

将行为编码为虚拟机器上的指令,赋予其数据的灵活性

如何能在分离的数据文件中定义行为,游戏引擎还能加载并执行它们?

解释器模式:  太慢了

玩家电脑在运行游戏时并不会遍历一堆C++语法结构树。 我们提前将其编译成了机器码,CPU基于机器码运行

虚拟的机器码:

  • 密集。 它是一块坚实连续的二进制数据块,没有一位被浪费。

  • 线性。 指令被打成包,一条接一条地执行。不会在内存里到处乱跳(除非你的控制流代码真真这么干了)。

  • 底层。 每条指令都做一件小事,有趣的行为从组合中诞生。

  • 速度快。 综合所有这些条件(当然,也包括它直接由硬件实现这一事实),机器码跑得跟风一样快。

模式:

指令集 定义了可执行的底层操作。 一系列的指令被编码为字节序列。 虚拟机 使用 中间值栈 依次执行这些指令。 通过组合指令,可以定义复杂的高层行为。

何时使用:

这个模式应当用在你有许多行为需要定义,而游戏实现语言因为如下原因不适用时:

  • 过于底层,繁琐易错。
  • 编译慢或者其他工具因素导致迭代缓慢。
  • 安全性依赖编程者。如果想保证行为不会破坏游戏,你需要将其与代码的其他部分隔开。

当然,该列表描述了一堆特性。谁不希望有更快的迭代循环和更多的安全性? 然而,世上没有免费的午餐。字节码比本地代码慢,所以不适合引擎的性能攸关的部分。

示例代码:

为了将法术编码进数据,储存了一数组enum值

enum Instruction
{
  INST_SET_HEALTH      = 0x00,
  INST_SET_WISDOM      = 0x01,
  INST_SET_AGILITY     = 0x02,
  INST_PLAY_SOUND      = 0x03,
  INST_SPAWN_PARTICLES = 0x04
};
switch (instruction)
{
  case INST_SET_HEALTH:
    setHealth(0, 100);
    break;

  case INST_SET_WISDOM:
    setWisdom(0, 100);
    break;

  case INST_SET_AGILITY:
    setAgility(0, 100);
    break;

  case INST_PLAY_SOUND:
    playSound(SOUND_BANG);
    break;

  case INST_SPAWN_PARTICLES:
    spawnParticles(PARTICLE_FLAME);
    break;
}

用这种方式,解释器建立了沟通代码世界和数据世界的桥梁。我们可以像这样将其放进执行法术的虚拟机:

class VM
{
public:
  void interpret(char bytecode[], int size)
  {
    for (int i = 0; i < size; i++)
    {
      char instruction = bytecode[i];
      switch (instruction)
      {
        // 每条指令的跳转分支……
      }
    }
  }
};

这样使用并不灵活,我们需要引入参数

栈式机器

要执行复杂的嵌套表达式,得先从最里面的子表达式开始。 计算完里面的,将结果作为参数向外流向包含它们的表达式, 直到得出最终结果,整个表达式就算完了。

switch (instruction)
{
  case INST_SET_HEALTH:
  {
    int amount = pop();
    int wizard = pop();
    setHealth(wizard, amount);
    break;
  }

  case INST_SET_WISDOM:
  case INST_SET_AGILITY:
    // 像上面一样……

  case INST_PLAY_SOUND:
    playSound(pop());
    break;

  case INST_SPAWN_PARTICLES:
    spawnParticles(pop());
    break;
}

设计师希望法术能表达规则,而不仅仅是数值

行为==组合 

字节码 · Behavioral Patterns · 游戏设计模式 (tkchu.me)

 还挺简单的,但是不好做笔记,就直接看原文吧

子类沙箱

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

 模式

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

何时使用

子类沙箱模式是潜伏在代码库中简单常用的模式,哪怕是在游戏之外的地方亦有应用。 如果你有一个非虚的protected方法,你可能已经在用类似的东西了。 沙箱方法在以下情况适用:

  • 你有一个能推导很多子类的基类。

  • 基类可以提供子类需要的所有操作。

  • 在子类中有行为重复,你想要更容易地在它们间分享代码。

  • 你想要最小化子类和程序的其他部分的耦合。

在基类中实现子类可能会有的操作函数,然后通过继承传递给子类,将子类与外界的耦合转移到基类上面。但是基类可能会变得很庞大,这时候可以将操作分流出去,作为一个类,然后将这个类暴露给基类

 如果Superpower已经很庞杂了,我们也许想要避免这样。 取而代之的是创建SoundPlayer类暴露该函数:

class SoundPlayer
{
  void playSound(SoundId sound, double volume)
  {
    // 实现代码……
  }

  void stopSound(SoundId sound)
  {
    // 实现代码……
  }

  void setVolume(SoundId sound)
  {
    // 实现代码……
  }
};

Superpower提供了对其的接触:

class Superpower
{
protected:
  SoundPlayer& getSoundPlayer()
  {
    return soundPlayer_;
  }

  // 沙箱方法和其他操作……

private:
  SoundPlayer soundPlayer_;
};

将提供的操作分流到辅助类可以为你做一些事情:

  • 减少了基类中的方法。 在这里的例子中,将三个方法变成了一个简单的获取函数。

  • 在辅助类中的代码通常更好管理。 像Superpower的核心基类,不管意图如何好,它被太多的类依赖而很难改变。 通过将函数移到耦合较少的次要类,代码变得更容易被使用而不破坏任何东西。

  • 减少了基类和其他系统的耦合度。 当playSound()方法直接在Superpower时,基类与SoundId以及其他涉及的音频代码直接绑定。 将它移动到SoundPlayer中,减少了SuperpowerSoundPlayer类的耦合,这就封装了它其他的依赖。

 参见

  • 当你使用更新模式时,你的更新函数通常也是沙箱方法。

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

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

 类型对象

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

模式

定义类型对象类和有类型的对象类。每个类型对象实例代表一种不同的逻辑类型。 每种有类型的对象保存对描述它类型的类型对象的引用

 实例相关的数据被存储在有类型对象的实例中,被同种类分享的数据或者行为存储在类型对象中。 引用同一类型对象的对象将会像同一类型一样运作。 这让我们在一组相同的对象间分享行为和数据,就像子类让我们做的那样,但没有固定的硬编码子类集合。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值