• 游戏开发模式一:组件模式(Component)


    软件设计模式告诉我们,程序中不同的领域要保持隔离,也就是解耦。所以,我们不希望AI,物理引擎,渲染引擎,声音引擎,还有其他的事情影响到彼此,不能把他们放到同一个类里。

    下面是一个反例:

    if (collidingWithFloor() && (getRenderState() != INVISIBLE))
    {
        playSound(HIT_FLOOR);
    }

    如果有人要修改这段代码,那么他就需要查看物理,绘图,和声音的代码以保证不会出错。更糟糕的情况是,你可能需要修改其他部分的代码!

    解决的办法:

    我们可以吧不同的领域分割城不同的组件,谈后需要的时候持有这些组件的实例,例如 InputComponent。

    再看一个难一点的例子:

    class Bjorn
    {
    public:
      void update(World& world, Graphics& graphics)
      {
        // Apply user input to hero's velocity.
        switch (Controller::getJoystickDirection())
        {
          case DIR_LEFT:  velocity_ -= WALK_ACCELERATION; break;
          case DIR_RIGHT: velocity_ += WALK_ACCELERATION; break;
        }
    
        // Modify position by velocity.
        x_ += velocity_;
        world.resolveCollision(volume_, x_, y_, velocity_);
    
        // Draw the appropriate sprite.
        Sprite* sprite = &spriteStand_;
        if (velocity_ < 0) sprite = &spriteWalkLeft_;
        else if (velocity_ > 0) sprite = &spriteWalkRight_;
    
        graphics.draw(*sprite, x_, y_);
      }
    
    private:
      static const int WALK_ACCELERATION = 1;
    
      int velocity_;
      int x_, y_;
    
      Volume volume_;
    
      Sprite spriteStand_;
      Sprite spriteWalkLeft_;
      Sprite spriteWalkRight_;
    };

    Bjorn 有一个 update()方法每一帧被调用一次。它获取determine来决定方向,谈后他用物理引擎来处理位置,最后,它把Bjørn绘制到屏幕上。可以看到这里其实只做了很少的事情,但是却显得很复杂。

    分割不同的领域:

    首先让我们把input分离:

    class InputComponent
    {
    public:
      void update(Bjorn& bjorn)
      {
        switch (Controller::getJoystickDirection())
        {
          case DIR_LEFT:  bjorn.velocity -= WALK_ACCELERATION; break;
          case DIR_RIGHT: bjorn.velocity += WALK_ACCELERATION; break;
        }
      }
    
    private:
      static const int WALK_ACCELERATION = 1;
    };

    Bjorn的变化:

    class Bjorn
    {
    public:
      int velocity;
      int x, y;
    
      virtual void update(World& world, Graphics& graphics)
      {
        input_.update(*this);
    
        // Modify position by velocity.
        x += velocity;
        world.resolveCollision(volume_, x, y, velocity);
    
        // Draw the appropriate sprite.
        Sprite* sprite = &spriteStand_;
        if (velocity < 0) sprite = &spriteWalkLeft_;
        else if (velocity > 0) sprite = &spriteWalkRight_;
    
        graphics.draw(*sprite, x, y);
      }
    
    private:
      InputComponent input_;
    
      Volume volume_;
    
      Sprite spriteStand_;
      Sprite spriteWalkLeft_;
      Sprite spriteWalkRight_;
    };
    

      然后我们把其他的组件都分离:

    class PhysicsComponent
    {
    public:
      void update(Bjorn& bjorn, World& world)
      {
        bjorn.x += bjorn.velocity;
        world.resolveCollision(volume_, bjorn.x, bjorn.y, bjorn.velocity);
      }
    
    private:
      Volume volume_;
    };
    

      

    class GraphicsComponent
    {
    public:
      void update(Bjorn& bjorn, Graphics& graphics)
      {
        Sprite* sprite = &spriteStand_;
        if (bjorn.velocity < 0) sprite = &spriteWalkLeft_;
        else if (bjorn.velocity > 0) sprite = &spriteWalkRight_;
    
        graphics.draw(*sprite, bjorn.x, bjorn.y);
      }
    
    private:
      Sprite spriteStand_;
      Sprite spriteWalkLeft_;
      Sprite spriteWalkRight_;
    };
    

      现在Bjorn变得很简洁:

    class Bjorn
    {
    public:
      int velocity;
      int x, y;
    
      virtual void update(World& world, Graphics& graphics)
      {
        input_.update(*this);
        physics_.update(*this, world);
        graphics_.update(*this, graphics);
      }
    
    private:
      InputComponent    input_;
      PhysicsComponent  physics_;
      GraphicsComponent graphics_;
    };
    

      

    现在我们已经把不同的组件都分开了,但是Bjorn依然知道这些行为的具体实现。我们将把我们的组件隐藏在借口背后,这样就需要把InputComponent变成一个抽象类:

    class InputComponent
    {
    public:
      virtual void update(Bjorn& bjorn) = 0;
    };
    

      然后实现它:

    class PlayerInputComponent : public InputComponent
    {
    public:
      virtual void update(Bjorn& bjorn)
      {
        switch (Controller::getJoystickDirection())
        {
          case DIR_LEFT:  bjorn.velocity -= WALK_ACCELERATION; break;
          case DIR_RIGHT: bjorn.velocity += WALK_ACCELERATION; break;
        }
      }
    
    private:
      static const int WALK_ACCELERATION = 1;
    };

    我们将持有一个InputComponent的指针,

    class Bjorn
    {
    public:
      int velocity;
      int x, y;
    
      Bjorn(InputComponent* input)
      : input_(input)
      {}
    
      virtual void update(World& world, Graphics& graphics)
      {
        input_->update(*this);
        physics_.update(*this, world);
        graphics_.update(*this, graphics);
      }
    
    private:
      InputComponent*   input_;
      PhysicsComponent  physics_;
      GraphicsComponent graphics_;
    };
    

      现在我们可以传入一个InputComponent来实例化Bjorn:

    Bjorn* bjorn = new Bjorn(new PlayerInputComponent());
    

      来看看InputComponent的另一个实现:

    class DemoInputComponent : public InputComponent
    {
    public:
      virtual void update(Bjorn& bjorn)
      {
        // AI to automatically control Bjorn...
      }
    };
    

      

    好了,最后让我们看看最简介的一般实现:

    我们有两个component:

    class PhysicsComponent
    {
    public:
      virtual void update(GameObject& obj, World& world) = 0;
    };
    
    class GraphicsComponent
    {
    public:
      virtual void update(GameObject& obj, Graphics& graphics) = 0;
    };
    

      一个GameObject:

    class GameObject
    {
    public:
      int velocity;
      int x, y;
    
      GameObject(InputComponent* input,
                 PhysicsComponent* physics,
                 GraphicsComponent* graphics)
      : input_(input),
        physics_(physics),
        graphics_(graphics)
      {}
    
      virtual void update(World& world, Graphics& graphics)
      {
        input_->update(*this);
        physics_->update(*this, world);
        graphics_->update(*this, graphics);
      }
    
    private:
      InputComponent*    input_;
      PhysicsComponent*  physics_;
      GraphicsComponent* graphics_;
    };
    

      

  • 相关阅读:
    JavaScript 类型转换 Type Convertion
    JavaScript 对象 Object
    Git失误操作导致文件冲突、路径错误
    Swift 3.0统计缓存并清理-----(斯威夫特3.0 坑爹之获取文件大小)
    iOS ——屏幕截图
    iOS 控件设置虚线
    Swift_单例的打开方式
    第三方接入小记
    图片自定义圆角
    自定义搜索框-输入框光标不贴边
  • 原文地址:https://www.cnblogs.com/shangdahao/p/2995051.html
Copyright © 2020-2023  润新知