zoukankan      html  css  js  c++  java
  • 游戏编程模式之字节码模式

    通过将行为编码成虚拟机指令,而使其具备数据的灵活性。
    (摘自《游戏编程模式》)

      游戏是一个庞大的工程,因此,在开发的过程中我们会选用高稳定性和高效率的重型语言,例如C++。C++的每一次代码更新都需要编译,然而庞大的代码量使得编译时间变得很长。因此,我们需要想办法将可自定义性高、变动可能性高的部分从代码主干中剥离出来(而游戏代码中恰好有很多部分符合这个要求),这样的代码极大的减少每一次更改后需要重新编译的次数。这就是字节码模式最根本的目的。

      至于其实现原理,很简单,将可变动内容(数值、执行指令、简单逻辑)从游戏核心代码转移到独立的文件中,游戏主干需要实现从这些文件中读取、判断、执行的功能即可

    解释器模式

      GoF的解释器模式其实就是实现字节码模式的一种途径。下面我们将插叙,简单介绍一下GoF的解释器模式。程序读取到一个字符串,并将字符串转化为语法树,解释器需要根据语法树准确的实现执行。那么如何执行语法树呢?以一个简单的运算 (1+2)×(3-4) 为例,解析后的抽象语法树如下图所示:

       "(1+2)×(3-4)"这一字符串解析的元素将存储在一个前序遍历的树中。从元素来看,将被分为值表达式和运算符表达式两种。下面则是示例代码:

    //表达式基类
    class Expression
    {
        public:
            virtual ~Expression(){}
            virtual double evaluate()=0;
    }
    
    //数值表达式
    class NumberExpression : public Expression
    {
        public:
            NumberExpression(double _value) : value(_value){}
            virtual double evaluate(){return value;}
            
        private:
            double value;
    }
    
    //加法表达式
    class AdditionExpression : Expression
    {
        public:
            AdditionExpression(Expression* _left,Expression* _right) : left(_left),right(_right){}
            virtual double evaluate()
            {
                double _left=left->evaluate();
                double _right=right->evaluate();
                return _left+_right;
            }
            
            
        private:
            Expression* left;
            Expression* right;
    }
    

      我们来分析以下解释器模式的缺点:

    • 每一个表达式都意味着一个实例,除此之外,对于运算符表达式来说,还要维护两个数值表达式的指针。这样的编写方式占用了很大的内存来处理一个简答的表达式运算。
    • 基于虚函数实现,维护虚函数表对于这一个简单的运算来说也是大材小用。
    • 表达式语法树的遍历也消耗大量的数据缓存。

      总的来说,就是用了复杂的方式来实现了一个不起眼的小功能,性价比低太低。

    字节码模式

      我们都知道,编译式语言需要在运行前将代码编译成机器码,机器码的优点如下。这些特点使得机器码执行效率极高。

    • 高密度。字节码是连续的二进制数据块,不会浪费任何一个字节。

    • 线性执行程度高。除了控制流跳转,其他的指令都是顺序执行的。

    • 底层。其执行指令是不可分割的,最简单的一个执行单元。

      然而,我们将指令、数据分割开的部分不可能用人工去编写机器码。因此,我们可以自己定义虚拟的机器码,并自行完成执行步骤。事实上,这就是自建一个简单的虚拟机(开发游戏引擎的脚本系统就是运用了字节模式)。

      使用字节码模式编写游戏中独立于核心代码的部分是一个大工程。因此,这里不可能给出一个比较完整的运用示例,不过我们可以见微知著,从一个最简单的案例来体会其字节码模式。

    示例

    • 游戏核心部分:

      //示例类
      class Character
      {
          public:
              void SetAttack(unsigned int type);
              void SetDefend(unsigned int type);
              void SetWalk();
              void AddHealth(double addition);
      }
      
      enum Ops
      {
          OPS_SET_ATTACK =0x00,
          OPS_SET_DEFEND =0x01,
          OPS_SET_WALK   =0x02,
          OPS_ADD_HEALTH =0x03
      }
      
      //自建虚拟机
      class VM
      {
          public:
              vm() : stackSize(0) {}
          
              void interpret(char bytecode[],int size)
              {
                  for(int i=0;i<size;i++)
                  {
                      char instruction=bytecode[i];
                      switch(instruction)
                      {
                          case Ops.OPS_SET_ATTACK:
                              int type=(int)pop();
                              SetAttack(type);
                              break;
                          case Ops.OPS_SET_DEFEND:
                              int type=(int)pop();
                              SetDefend(type);
                              break;
                          case Ops.OPS_SET_WALK:
                              SetWalk();
                              break;
                          case Ops.OPS_ADD_HEALTH:
                              double val=(double)pop();
                              AddHealth(val);
                              break;
                      }
                  }
              }
              
              void push(int value)
              {
                  assert(stackSize<MAX_STACK);
                  stack[stackSize++]=value;
              }
              
              void pop()
              {
                  assert(stackSize>0);
                  return stack[--stackSize];
              }
            
          private:
              static const int MAX_STACK=128;
              int stackSize;
              int stack[MAX_STACK];
              
      }
      
  • 相关阅读:
    hdu2438 三分
    hdu3786 找出直系亲属 水题
    hdu3786 找出直系亲属 水题
    hdu4561 连续最大积
    hdu4561 连续最大积
    hdu4604 不错的子序列问题
    hdu4604 不错的子序列问题
    hdu4450 不错的贪心
    hdu1722 切蛋糕
    hdu3768 spfa+全排列
  • 原文地址:https://www.cnblogs.com/ZhuSenlin/p/15459223.html
Copyright © 2011-2022 走看看