游戏开发模式一:组件模式(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_;
};
                    
                
                
            
        
浙公网安备 33010602011771号