《游戏编程模式》各种模式汇总(四)

  1. 对象池模式

使用场景:

1)需要频繁创建和销毁对象时,用来避免内存碎片。

2)每个对象都封装了像数据库或网络连接这样的昂贵资源。

/*---------------------------------对象池模式-----------------------------
*/

// -------------------------------Demo-----------------------------//、
// 粒子对象
class Particle
{
private:
    //
    union
    {
        // 用一个结构体live包含粒子在使用时需要访问的属性
        struct
        {
            double _x, _y;
            double _xVel, _yVel;
        } live;
        // 一个指向下一个粒子的指针
        Particle *next;
    } _state;

    // 用粒子的存活时间来判断是否正在使用
    int _lifetime;

public:
    // 构造粒子时,默认"不使用"
    Particle() : _lifetime(0) {}
    // 初始化,可以激活粒子,使其进入“使用”状态
    void init(double x, double y, double xVel, double yVel, int lifetime)
    {
        _state.live._x = x;
        _state.live._y = y;
        _state.live._xVel = xVel;
        _state.live._yVel = yVel;
        _lifetime = lifetime;
    }
    // 用粒子的存活时间来判断是否正在使用
    bool isUse() const { return _lifetime > 0; }
    // 每帧调用一次animate()
    bool animate()
    {
        // 当粒子“未激活”即空闲时,不执行状态更新,返回false
        if (!isUse())
            return false;
        // lifetime应该减去每帧花费的现实时间
        _lifetime--;
        // 模拟粒子的运动
        _state.live._x += _state.live._xVel;
        _state.live._y += _state.live._yVel;
        // 当粒子生命结束时,即正好空闲,返回true
        return _lifetime == 0;
    }
    // 链表的API,get和set-->*next
    Particle *getNext() const { return _state.next; }
    void setNext(Particle *next) { _state.next = next; }
};

// 对象池
class ParticlePool
{
private:
    static const int POOL_SIZE = 100;
    // 用数组作为容器
    Particle _container[POOL_SIZE];
    Particle *_firstAvailableParticle;

public:
    // 在初始化对象池的时候,设置好链表的连接
    ParticlePool()
    {
        // 设置第一个空闲粒子
        _firstAvailableParticle = &_container[0];
        // 让每个粒子指向下一个,形成链表
        for (int i = 0; i < POOL_SIZE - 1; i++)
            _container[i].setNext(&_container[i + 1]);
        // 设置链表最后一个节点指向null
        _container[POOL_SIZE - 1].setNext(nullptr);
    }
    // create()不能写成传进来一个Particle&,因为这样的话不能控制对象在哪块内存生成
    void create(double x, double y, double xVel, double yVel, int lifetime)
    {
        // 保证池没有满,即还有空闲粒子没被使用
        assert(_firstAvailableParticle != nullptr);
        // 找到一个空闲粒子
        Particle *newParticle = _firstAvailableParticle;
        // 初始化该粒子
        newParticle->init(x, y, xVel, yVel, lifetime);
        // 设置下一个空闲粒子的指针
        _firstAvailableParticle = newParticle->getNext();
    }
    // 池内对象轮流调用一次animate()
    void animate()
    {
        for (auto &&i : _container)
        {
            // 对象调用完一次animate()后,若返回ture,代表其正好到达空闲状态
            if (i.animate())
            {
                // 将处于空闲状态的粒子加入链表的头部
                i.setNext(_firstAvailableParticle);
                // 让头节点指针(_firstAvailableParticle)指向这个新加入的节点
                _firstAvailableParticle = &i;
            }
        }
    }
};
  1. 游戏循环

游戏程序特有的模式。

这里将Unity的游戏循环作为例子。

https://gafferongames.com/post/fix_your_timestep/

http://www.koonsolo.com/news/dewitters-gameloop/

http://docs.unity3d.com/Manual/ExecutionOrder.html

  1. 组件模式

在使用并发编程的现代游戏中,将游戏分割到线程的一个通用的方法就是通过领域划分,比如在

第一个核上运行AI,在第二个播放声音,在第三个上渲染等等。因此需要解耦各个领域。

现在的软件设计趋势是用组件代替继承,不是让两个类继承一个类来分享属性,而是让他们拥有

同一个类的实例。

当有一个涉及控制多个领域的类出现,并且这多个领域相互隔绝时,使用组件模式。

(Unity就使用组件模式,一个GameObject通过添加组件来完善其功能)

/*---------------------------------组件模式-----------------------------
*/

// -------------------------------Demo-----------------------------//
// GameObject容器类
class GameObject
{
private:
    InputComponent *_inputComp;
    PhysicsComponent *_physicsComp;
    GraphicsComponent *_graphicsComp;

public:
    int _velocity;
    int _xPos, _yPos;

    /* 这里可以考虑结合对象池模式,将Component抽象成基类接口,输入组件、图
    像组件等都实现这个接口,然后再GameObject里创建一个Component指针数组,
    把各种接口放进这个对象池里。如何找到特定组件:来个模板方法,类似Unity的
    GetComponent<T>()(可能并不太好?需要遍历数组才能找到)【动态添加组件】

    如果是现在这种硬编码的方式,就需要将可能用到的组件全部先放在Gameobject类
    中,需要用到的给指针,用不到的给null,找到特定组件倒是简单了。【静态添加组
    件】
    */
    // 用组件构造对象,没有这个组件就设为null
    GameObject(InputComponent *inputComp = nullptr,
               PhysicsComponent *physicsComp = nullptr,
               GraphicsComponent *graphicsComp = nullptr)
        : _inputComp(inputComp),
          _physicsComp(physicsComp),
          _graphicsComp(graphicsComp)
    {
    }

    void update()
    {
        // 没有检测空指针,懒得写了
        _inputComp->update();
        _physicsComp->update();
        _graphicsComp->update();
    }
};

// 抽象基类接口
class Component
{
public:
    virtual void update() = 0;
    virtual ~Component() {}
};

// 接下来是三个组件的实现
class InputComponent : public Component
{
public:
    virtual void update() override{
        // 实现
    };
};

class PhysicsComponent : public Component
{
public:
    virtual void update() override{
        // 实现
    };
};

class GraphicsComponent : public Component
{
public:
    virtual void update() override{
        // 实现
    };
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值