宪章《雷霆战机》飞行射击手游开发–子弹、跟踪导弹以及激光

转载请注明:http://www.cnblogs.com/thorqq/p/6563904.html
试玩版下载:https://pan.baidu.com/s/1mhLBNVa (Windows版)

转载请注明:http://www.cnblogs.com/thorqq/p/6323156.html

机的兵项目众多,大致可分为子弹、跟踪导弹和激光。子弹是直线飞行之;导弹会自动跟踪目标,可曲线飞行;而激光是一律鸣光束,可连地比射到的敌机产生伤害。子弹根据一次性发射的数,可分为单发和多发,根据子弹发射的趋势而分为:自动瞄准、平行、散射。本文将成代码讲述关于飞机武器的次设计。

 

次设计

先押一下本游戏中关于武器的接近的计划
美高梅4688.com 1
其中,BulletGroup唯独知道为弹匣,Bullet就是内部同样颗一颗的枪弹,每粒子弹还起同一的特性,包括外观、伤害值以及宇航性能。而延续自Bullet的五单子类分别吗:

  • ScatterBullet 散弹。含单发和多发,平行和散射。
  • AimScatterBullet
    自动瞄准子弹。继承自ScatterBullet,但是初始发射角度对距离最近之敌机(飞行轨道是直线,发射后未会见改变方向)。
  • CustomBullet 可打定义每粒子弹初始的样子、速度。
  • Missile
    跟踪导弹。顾名思义,在航空过程遭到会自行改变方向,始终本着敌机,但给抑制飞行速度、角速度相当参数,也说不定无法射中目标。
  • Laser 激光。一志光束,伤害值依赖让点的时刻。

本游戏正式放源代码啦~~

参数

下面是Bullet的有参数,包含在结构体TBulletData中:

std::string name; //名称
std::string type; //类型:散弹、导弹、激光

std::vector<std::string> styleArray; //帧动画图片资源
float aniDura;//帧动画时间间隔

std::string armatureName; //骨骼动画名称

int topOfAircraft;//出现在飞机的上层还是下层
int musicId;//音效

int attack; //攻击值
int speed;  //飞行速度。单位:像素/秒

int count;//每次发射的子弹的颗数

int max_count;//最多发射多少颗子弹。当达到最大值时,停止发射或自动降档
float timeLimit; //时间限制。当达到这个时间时,停止发射或自动降档

float angle_of_center;//中心法线的角度。
float angle_interval;//多颗子弹同时发射时,这个角度就是相邻子弹间的夹角

float x_interval;//多颗子弹同时发射时,两两间的横向间距

float delay;//发射第一颗子弹时的延迟时间

//假设发射的顺序为:1.1.1...1.1.1...1.1.1... 其中“1”表示发射子弹,“.”表示间隔时间
//我们称“1.1.1...”是一个大周期,“1.1.1”是三个小周期
float interval;//大周期之间的时间间隔,这里是“...”代表的时间
float interval_2;//小周期之间的时间间隔,这里是“.”代表的时间
int interval_2_cnt;//小周期里子弹数量,这里是3

float rotate_angle; //连续发射时,每次发射偏转的角度
float rotate_max_angle;//最大累计偏转角度
int rotate_flag;//累计偏转角度达到最大值时的处理方法,0:逐渐减小偏转角度;1:立刻还原到零

float bodyCenterX;//碰撞体相对子弹中心点的坐标偏移
float bodyCenterY;
float bodySizeW;//碰撞体的大小
float bodySizeH;

//发射原点相对飞机中心点的坐标偏移
float origin_offset_x;
float origin_offset_y;

std::vector<std::string> fireFameNameArray; //尾部火焰动画,图片列表
float fireAniDura; //动画帧时长
int   fireOnTop; //尾部火焰显示在子弹的上层还是下层
float fireOffsetX;//火焰中心点相对于飞机底部中心点的偏移。如果等于0,则只有一个火焰;否则是两个火焰
float fireOffsetY;

std::vector<std::string> blastStyleArray;//子弹打到目标后产生的爆炸的帧动画资源列表

代码库(可于附件中生载Windows下的试玩程序):https://git.oschina.net/thorqq/RaidenFree

基类设计

下面我们继承分析这款打。

Bullet

Bullet看似非常简单,仅仅用于保障子弹的外观,所以最主要之只有bool Bullet::init(BulletGroup* pBulletGroup)就一个计。详见如下代码:

class Bullet : public GameObject
{
public:
    friend class BulletGroup;

    static Bullet* create(BulletGroup* pBulletGroup);

    //根据pBulletGroup中的子弹相关属性创建子弹
    virtual bool init(BulletGroup* pBulletGroup);
    virtual void reset();

protected:
    Bullet();
    virtual ~Bullet();

    //添加尾部的左右两个火焰动画
    bool addFire(float offsetX, float offsetY, bool isFlipped);

protected:
    cocostudio::Armature* m_pArmature;
    BulletGroup* m_pBulletGroup;
    bool m_bBlast;
};

Bullet::Bullet()
{
    reset();
}

Bullet::~Bullet()
{
}

void Bullet::reset()
{
    GameObject::reset();

    m_pArmature = nullptr;
    m_pBulletGroup = NULL;
    m_bBlast = false;
}

Bullet* Bullet::create(BulletGroup* pBulletGroup)
{
    Bullet *pRet = new(std::nothrow) Bullet();
    if (pRet && pRet->init(pBulletGroup))
    {
        pRet->autorelease();
        return pRet;
    }
    else
    {
        delete pRet;
        pRet = NULL;
        return NULL;
    }
}

//根据pBulletGroup中的子弹相关属性创建子弹
bool Bullet::init(BulletGroup* pBulletGroup)
{
    m_pBulletGroup = pBulletGroup;
    bool bRet = false;
    do
    {
        CC_BREAK_IF(!GameObject::init());

        if (m_pBulletGroup->m_data.topOfAircraft)
        {
            m_pBulletGroup->getParent()->addChild(this, CONSTANT::ZORDER_BULLET_ONTOP);
        }
        else
        {
            if (m_pBulletGroup->getPlane()->getAircraftType() == EAircraftType::Type_Enemy)
            {
                m_pBulletGroup->getParent()->addChild(this, CONSTANT::ZORDER_BULLET_ENEMY);
            }
            else
            {
                m_pBulletGroup->getParent()->addChild(this, CONSTANT::ZORDER_BULLET_PLAYER);
            }
        }


        setBodyCenter(m_pBulletGroup->getBodyCenter());
        setBodySize(m_pBulletGroup->getBodySize());

        GameObject::initSpriteWithFileList(m_pBulletGroup->m_data.styleArray, m_pBulletGroup->m_data.aniDura);

        if (m_pBulletGroup->m_data.armatureName.length() > 0)
        {
            m_pArmature = cocostudio::Armature::create(m_pBulletGroup->m_data.armatureName);
            m_pArmature->setPosition(getContentSize() / 2);
            m_pArmature->getAnimation()->play(GlobalData::getInstance()->getArmatureData(m_pBulletGroup->m_data.armatureName)->defaultAction);

            addChild(m_pArmature);
        }
        else
        {
            if (m_pBulletGroup->m_data.fireFameNameArray.size() > 0)
            {
                m_pBulletGroup->m_data.fireOffsetX = abs(m_pBulletGroup->m_data.fireOffsetX);

                if (m_pBulletGroup->m_data.fireOffsetX > 0.01)
                {
                    addFire(+m_pBulletGroup->m_data.fireOffsetX, m_pBulletGroup->m_data.fireOffsetY, false);
                    addFire(-m_pBulletGroup->m_data.fireOffsetX, m_pBulletGroup->m_data.fireOffsetY, true);
                }
                else
                {
                    addFire(0, m_pBulletGroup->m_data.fireOffsetY, false);
                }
            }
        }

        bRet = true;
    } while (0);

    return bRet;
}

bool Bullet::addFire(float offsetX, float offsetY, bool isFlipped)
{
    Sprite* fire = GameObject::createSpriteWithFileList(
        m_pBulletGroup->m_data.fireFameNameArray, m_pBulletGroup->m_data.fireAniDura);

    //子弹的飞行速度比较快,所以火焰不需要动画
    //if (m_pBulletGroup->m_data.fireFameNameArray.size() == 1)
    //{
    //  ScaleTo* pScale1 = ScaleTo::create(m_pBulletGroup->m_data.fireAniDura, 1.0f, 0.9f);
    //  ScaleTo* pScale2 = ScaleTo::create(m_pBulletGroup->m_data.fireAniDura, 1.0f, 1.0f);
    //  Sequence* sequence = Sequence::create(pScale1, pScale2, NULL);
    //  Repeat* repeat = Repeat::create(sequence, CC_REPEAT_FOREVER);
    //  fire->runAction(repeat);
    //}

    //添加
    if (m_pBulletGroup->m_data.fireOnTop)
    {
        addChild(fire);
    }
    else
    {
        addChild(fire, CONSTANT::ZORDER_PLAYERPLANE_FIRE);
    }

    //镜像翻转
    if (isFlipped)
    {
        fire->setFlippedX(true);
    }
    //位置
    fire->setPosition(Vec2(getContentSize().width / 2 + offsetX, offsetY));

    return true;
}

飞机

这是一模一样慢慢悠悠打飞机玩,所以主角当然是飞机。游戏里,飞机包括以下几种:

  • 玩家飞机:玩家操纵的主飞机
  • 僚机:在玩家飞机左右之粗飞机,相对于玩家飞机的职位是定位不更换的。
  • 日常敌机:不会见变形的敌机
  • 变形敌机:飞至指定位置后变形,然后起放。被击落时,屏幕会感动
  • BOSS:从屏幕上方飞下来,飞至指定位置后变形,然后开左右运动,同时启幕放。boss可以生多次变形,当血量低于一定值经常会沾变形,同时攻击力增强。Boss被击落后,飞机达会见生出多次放炮,屏幕会伴随着激动。
  • 毫无疑问杀僚机:必杀技。当玩家点击必杀按钮时,会由屏幕下方出现一个硕大之飞机,发射超级激光

BulletGroup

BulletGroup大凡比关键之好像,包含了子弹的基本参数(结构体TBulletData,用于”复制“出一致发一发的枪弹)、发射动作、与飞机及敌机间的干。详细代码如下:

//子弹的基类。内部维护了一个子弹池
class BulletGroup : public GameObjectContainer
{
public:
    friend class Bullet;
    friend class Missile;
    friend class Laser;

    BulletGroup();
    virtual ~BulletGroup();

    virtual bool init(Node* pParent, Aircraft* pPlane, const TBulletData* pData);
    virtual void reset();
    virtual void destory();

    //开始射击
    virtual void startShoot();
    //发射指定数量的子弹
    virtual void startShoot(int cnt);
    //停止射击
    virtual void stopShoot();
    //是否正在射击
    virtual bool isShooting();

    //从子弹池中删除一颗子弹。超出屏幕范围,或者击中目标
    virtual void RemoveBullet(Bullet* pBullet);
    //爆炸
    virtual void blast(Bullet* pBullet);

    //当对方飞机增加或者减少时,通知导弹
    virtual void nodifyTargetRemove(Aircraft* pAircraft) {}

    //注册子弹用完监听器
    void regBulletUseUpListener(IBulletUseUpListener* l);
    void notifyBulletUseUp();
    inline bool isUseUp() { return m_bIsUseUp; }

public:
    //子弹飞出屏幕后的完成动作(内部函数)
    virtual void bulletMoveFinished(Node* pSender);

    inline int getAttack()
    {
        return m_data.attack;
    }

    inline void setAttack(int a)
    {
        m_data.attack = a;
    }

    inline void setPlane(Aircraft* plane)
    {
        m_plane = plane;
    }

    inline Aircraft* getPlane()
    {
        return m_plane;
    }

    inline void setOtherSidePlane(Vector<Aircraft*>* const planes)
    {
        m_otherSideArray = planes;
    }

    virtual void update(float dt) override;

protected:

    //发射一次子弹(可能会包含多颗子弹)
    virtual void AddBullet(float dt) {};

    //从子弹池中获取一颗子弹
    virtual Bullet* getOneBullet();

    float getPara(const char* const param);

    bool isSameGroup() { return m_bSameGroup; }
protected:

    TBulletData m_data;

    int m_iCount;
    int m_iAvailableBullet;
    bool m_bIsShooting;
    Aircraft* m_plane;
    Vector<Aircraft*> * m_otherSideArray;

    int m_iIntervalCnt; //小间隔子弹发射次数
    float m_iTimeCum;   //时间累计
    float m_iTimeCumThisGrade;   //本等级下的时间累计
    bool m_bFirstShoot; //

    bool m_bSameGroup;  //是否在同一个大间隔内

    IBulletUseUpListener* m_pBulletUseUpListener;
    bool m_bIsUseUp;
    float m_timeLimitAdd;
};


BulletGroup::BulletGroup()
{
    this->reset();
}

BulletGroup::~BulletGroup()
{
}

void BulletGroup::reset()
{
    m_data.reset();

    m_iCount = 0;
    m_bIsShooting = false;
    m_plane = NULL;
    m_otherSideArray = NULL;
    m_iAvailableBullet = 0;

    m_iIntervalCnt = 0;
    m_iTimeCum = 0;
    m_iTimeCumThisGrade = 0;
    m_bFirstShoot = false;

    m_pBulletUseUpListener = NULL;
    m_bIsUseUp = false;
    m_timeLimitAdd = 0;
}

bool BulletGroup::init(Node* pParent, Aircraft* pPlane, const TBulletData* pData)
{
    bool bRet = false;
    do
    {
        CC_BREAK_IF(!GameObjectContainer::init());

        setPlane(pPlane);
        m_data.clone(*pData);

        Node::setName(m_data.type);

        if (m_data.topOfAircraft)
        {
            pParent->addChild(this, CONSTANT::ZORDER_BULLET_ONTOP);
        }
        else
        {
            if (pPlane->getAircraftType() == EAircraftType::Type_Enemy)
            {
                pParent->addChild(this, CONSTANT::ZORDER_BULLET_ENEMY);
            }
            else
            {
                pParent->addChild(this, CONSTANT::ZORDER_BULLET_PLAYER);
            }
        }

        //body
        Vec2 center(m_data.bodyCenterX, m_data.bodyCenterY);
        setBodyCenter(center);

        //如果没有设置刚体大小,则默认是图片尺寸的60%
        Size size(m_data.bodySizeW, m_data.bodySizeH);
        if (fabs(size.width) < 0.01 || fabs(size.height) < 0.01)
        {
            size.width = this->getContentSize().width * 0.6f;
            size.height = this->getContentSize().height * 0.6f;
        }
        setBodySize(size);

        //获取对方飞机列表
        m_otherSideArray = pPlane->getOtherSidePlane();

        if (pPlane->getAircraftType() == EAircraftType::Type_Enemy)
        {
            m_timeLimitAdd = GameData::getInstance()->getValueToFloat(GAMEDATA::REINFORCE_VALUE_RAMPAGE_DURA);
        }

        bRet = true;
    } while (0);

    return bRet;
}

void BulletGroup::destory()
{
    if (this->m_plane == NULL)
    {
        int i = 0;

        for (i = 0; i < getAllObject()->count(); i++)
        {
            Bullet* p = dynamic_cast<Bullet*>(getAllObject()->getObjectAtIndex(i));

            if (p != NULL && p->isVisible())
            {
                //无需释放
                return;
            }
        }

        if (getAllObject()->count() == i)
        {
            for (i = 0; i < getAllObject()->count(); i++)
            {
                Bullet* p = dynamic_cast<Bullet*>(getAllObject()->getObjectAtIndex(i));

                if (p != NULL)
                {
                    p->destory();
                }
            }

            getAllObject()->removeAllObjects();

            removeFromParent();
        }
    }
}

void BulletGroup::startShoot()
{
    m_bIsShooting = true;

    this->scheduleUpdate();
}

void BulletGroup::startShoot(int cnt)
{
    m_bIsShooting = false;
    m_iCount = 0;
    m_iIntervalCnt = 0;
    m_iTimeCum = 0;
    m_iTimeCumThisGrade = 0;
    m_bFirstShoot = 0;
    m_bSameGroup = false;
    m_bIsUseUp = false;

    m_data.max_count = cnt;
    m_bIsShooting = true;

    this->scheduleUpdate();
}

void BulletGroup::stopShoot()
{
    unscheduleUpdate();
    unscheduleAllCallbacks();

    m_bIsShooting = false;
}

bool BulletGroup::isShooting()
{
    return m_bIsShooting;
}

void BulletGroup::update(float dt)
{
    m_iTimeCum += dt;
    m_iTimeCumThisGrade += dt;

    if (m_data.timeLimit > 0 && m_iTimeCumThisGrade > m_data.timeLimit + m_timeLimitAdd)
    {
        m_bIsUseUp = true;
        m_iTimeCumThisGrade = 0;
        this->notifyBulletUseUp();
        this->stopShoot();
        return;
    }

    if (!m_bFirstShoot && m_iTimeCum < m_data.delay && m_data.delay >= 0.00001) //还没进行第一次发射,等待延迟
    {
        return;
    }
    else if (!m_bFirstShoot && m_iTimeCum >= m_data.delay && m_data.delay >= 0.00001) //第一次发射,超时了
    {
        m_iTimeCum -= m_data.delay;
        m_bFirstShoot = true;

        m_iIntervalCnt++;
        if (Sound::isBulletSound())
        {
            Sound::playSound(m_data.musicId);
        }
        m_bSameGroup = true;
        AddBullet(dt);
        return;
    }

    //小间隔内
    if (m_iIntervalCnt < m_data.interval_2_cnt)
    {
        if (m_iTimeCum >= m_iIntervalCnt * m_data.interval_2)
        {
            m_iIntervalCnt++;
            if (Sound::isBulletSound())
            {
                Sound::playSound(m_data.musicId);
            }

            m_bSameGroup = true;
            AddBullet(dt);
            return;
        }

    }
    else //大间隔
    {
        if (m_iTimeCum >= m_data.interval_2_cnt * m_data.interval_2 + m_data.interval)
        {
            m_iIntervalCnt = 1;
            m_iTimeCum = 0;
            if (Sound::isBulletSound())
            {
                Sound::playSound(m_data.musicId);
            }

            m_bSameGroup = false;
            AddBullet(dt);
            return;
        }
    }
}

Bullet* BulletGroup::getOneBullet()
{
    if (!m_bIsShooting)
    {
        return NULL;
    }

    if (m_data.max_count > 0 && m_iCount >= m_data.max_count)
    {
        m_bIsUseUp = true;
        this->notifyBulletUseUp();

        return NULL;
    }

    int i = 0;
    int size = getAllObject()->count();
    for (; i < size; i++)
    {
        Bullet* p = dynamic_cast<Bullet*>(m_pAllObject->getObjectAtIndex(i));

        if (p != NULL && p->isVisible() == false)
        {
            p->setVisible(true);
            p->startAnimate();
            m_iAvailableBullet++;
            m_iCount++;
            return p;
        }
    }

    if (m_pAllObject->count() == i)
    {
        Bullet* pBullet = Bullet::create(this);
        m_pAllObject->addObject(pBullet);
        m_iAvailableBullet++;
        m_iCount++;
        //CCLOG("Add bullet[%s], size = %d", Node::getName().c_str(), m_pAllObject->count());
        return pBullet;
    }

    return NULL;
}

//击中目标后爆炸并删掉
void BulletGroup::blast(Bullet* pBullet)
{
    //在父层中生成爆炸动画
    PlaneLayer* pLayer = dynamic_cast<PlaneLayer*>(getParent());
    if (NULL != pLayer)
    {
        Rect rect = pBullet->getBodyBox();
        Vec2 pos(rect.getMidX(), rect.getMidY());

        //击中的爆炸动画
        pLayer->addBlast(this->getLocalZOrder(), pos, m_data.blastStyleArray, m_data.blastAniDura);

        //粒子效果。TODO 后续要使用particle表里读取粒子配置
        ParticleSystemQuad *emitter1 = ParticleSystemQuad::create("img/blast/hitEnemy.plist");
        emitter1->setPosition(pos);    // 设置发射粒子的位置  
        emitter1->setAutoRemoveOnFinish(true);                          // 完成后制动移除  
        emitter1->setDuration(0.3f);                                      // 设置粒子系统的持续时间秒  

        pLayer->addChild(emitter1, getLocalZOrder() + 1);
    }

    RemoveBullet(pBullet);
}

//子弹飞出屏幕后删掉
void BulletGroup::bulletMoveFinished(Node* pSender)
{
    RemoveBullet((Bullet*)pSender);
}

void BulletGroup::RemoveBullet(Bullet* pBullet)
{
    if (pBullet != NULL)
    {
        pBullet->stopAllActions();
        pBullet->setVisible(false);
        Node* parent = getParent();

        m_iAvailableBullet--;

        int size = getAllObject()->count();
        int i = 0;
        for (; i < size; i++)
        {
            Bullet* p = dynamic_cast<Bullet*>(m_pAllObject->getObjectAtIndex(i));

            if (p != NULL && p->isVisible() == true && m_iAvailableBullet <= 0)
            {
                return;
            }
        }

    }

    //清除本对象
    if (m_plane == NULL && m_iAvailableBullet <= 0 && getParent() != NULL && !this->isShooting())
    {
        int size = getAllObject()->count();
        int i = 0;
        for (; i < size; i++)
        {
            Bullet* p = dynamic_cast<Bullet*>(m_pAllObject->getObjectAtIndex(i));
            p->m_pBulletGroup = NULL;
            p->getParent()->removeChild(p);
        }

        PlaneLayer* layer = dynamic_cast<PlaneLayer*>(getParent());
        getParent()->removeChild(this);
        if (layer)
        {
            layer->removeBullet(this);
        }
    }

}

void BulletGroup::regBulletUseUpListener(IBulletUseUpListener* l)
{
    m_pBulletUseUpListener = l;
}

void BulletGroup::notifyBulletUseUp()
{
    if (m_pBulletUseUpListener != NULL)
    {
        m_pBulletUseUpListener->bulletUseUp();
    }
}

float BulletGroup::getPara(const char* const param)
{
    auto it = m_data.paramMap.find(param);
    if (it != m_data.paramMap.end())
    {
        return it->second;
    }
    else
    {
        return 0;
    }
}

子弹的每次放的时光点控制以update计被,其中使用到了延时delay、大小周期intervalinterval_2interval_2_cnt齐相关参数。当确定好每次放的时间点后,会经AddBullet来上加相同发子弹,放在某个位置及,并给其发出出来,具体的放位置以及放的角度速度相当参数留给子类来落实,这里就定义一个拖欠方法:virtual void AddBullet(float dt) {};。从方法RemoveBullet好望,当子弹飞出屏幕或击中目标时,我们销毁子弹的道就是住该动画效果,并安装为不可见,这样可减掉频繁创建/销毁子弹带来的属性损失。当子弹用了经常,会由此艺术notifyBulletUseUp来打招呼飞机,这样,飞机就会见指向子弹进行降档,例如从暴走状态回升至日常状态。

脚我们看看散弹、跟踪导弹、激光等逐个子类的现实性实现。

机的着力性

  • 名字
  • 类型
  • 骨骼动画(飞行、左右侧飞、变形、暴走、尾部火焰)
  • 生命值
  • 攻击值:当机同飞机相撞时,对对方飞机有的伤害值
  • 碰撞体(刚体)的基本坐标和分寸(只有当子弹的碰撞体与飞机的碰撞体发生叠时,才会生出伤害)
  • 缩放比例
  • 血槽的体制与职务
  • 飞机的初步位置(对于玩家飞机来说,一般是打屏幕下方飞入;对于敌机来说,一般是于高达半单屏幕的某地方竟然出来)
  • 子弹(弹夹BulletGroup+子弹Bullet)
  • 僚机
  • 飞机爆炸帧动画以及音效
  • 壮大属性(可扩大,用map来实现)

俺们因此结构体TAircraftData来表示:

struct TAircraftData
{
    int id;
    std::string name;//名字
    std::string type;//类型,值为AircraftType
     //飞机动画即支持帧动画,又支持骨骼动画
    std::vector<std::string> styleArray;  //飞机动画
    float aniDura; //动画帧时长
    std::string armatureName; //骨骼动画名称

    int hp;           //当前生命值
    int maxHp;        //最大生命值
    float defence;    //防御
    int attack;       //攻击

    float bodyCenterX; //刚体中心坐标
    float bodyCenterY;
    float bodySizeW;   //刚体尺寸
    float bodySizeH;
    float scale;       //缩放比例

    std::string hpBgStyle; //血槽背景样式
    std::string hpStyle;   //血槽样式
    float hpPosX;          //血槽的位置
    float hpPosY;          //血槽的位置

    std::vector<std::vector<int>*> bulletIdArray;  //多种子弹

    std::vector<std::string> fireFameNameArray; //尾部火焰动画,图片列表
    float fireAniDura; //动画帧时长
    int   fireOnTop;
    float fireOffsetX;//火焰中心点相对于飞机底部中心点的偏移。如果等于0,则只有一个火焰;否则是两个火焰
    float fireOffsetY;

    float startPosX;  //飞机的起始位置
    float startPosY;

    std::vector<int> wingmanArray; //僚机

    std::vector<std::string> blastStyleArray; //飞机爆炸的动画
    float blastAniDura;
    int blastMusicId; //飞机爆炸的音效id

    std::map<std::string, std::string> paramMap; //飞机参数
}

散弹

无限简易的散弹就是历次就放一颗子弹,并且有着子弹还朝着一个请勿转移的势头飞,看起就是比如相同长直线。当每次放两粒或多发子弹时,看起就是是零星修或多修直线,这基本上条直线或平行,或紧邻之间在一个恒定的夹角。在代码中凡是透过angle_interval参数来决定的。

再有同种植偏转角度随日别之散弹,即各级发射一发子弹,其航空角度都见面为某个方向偏转一个永恒的角度,当上一个极度酷角度值时,又见面渐渐衰弱多少偏转角度,看起便像只“之”字形。如果尽可怜偏转角度不存时时,子弹轨迹看起便是个螺旋形状了。在代码中凡是经过rotate_anglerotate_max_angle来控制的。
下面我们看下实际代码。

//可自定义子弹的个数、夹角、偏移
class ScatterBullet : public BulletGroup
{
public:
    ScatterBullet();

    virtual bool init(Node* pParent, Aircraft* pPlane, const TBulletData* pData);
    virtual void reset();

protected:
    //发射一次子弹
    virtual void AddBullet(float dt) override;
    //计算发射中心线的角度
    virtual float calculateCenterLineDegree(Bullet* pBullet, Vec2& src);
    //发射一颗子弹
    virtual bool AddBulletOne(Vec2& srcOffset, float degree);

protected:
    float m_flyDistance;
    long m_shootCnt;
    int m_shootAddFlag;
};


ScatterBullet::ScatterBullet()
    : m_flyDistance(0)
    , m_shootCnt(0)
    , m_shootAddFlag(0)
{

}

void ScatterBullet::reset()
{
    BulletGroup::reset();

    m_flyDistance = 0;
    m_shootCnt = 0;
    m_shootAddFlag = 0;
}

bool ScatterBullet::init(Node* pParent, Aircraft* pPlane, const TBulletData* pData)
{
    bool bRet = false;
    do
    {
        CC_BREAK_IF(!BulletGroup::init(pParent, pPlane, pData));

        m_flyDistance = CONSTANT::DESIGN_RES_DIAGONAL;
        m_shootAddFlag = 1;

        if (m_data.count < 0)
        {
            m_data.count = 1;
        }

        bRet = true;
    } while (0);

    return bRet;
}

void ScatterBullet::AddBullet(float dt)
{
    m_shootCnt += m_shootAddFlag;

    float angleLeft = 0;
    float offsetXLeft = 0;
    if (m_data.count % 2 == 0)//偶数
    {
        //计算最左边的子弹的夹角、偏移
        angleLeft = m_data.angle_interval / 2 + (m_data.count / 2 - 1) * m_data.angle_interval;
        offsetXLeft = -1 * (m_data.x_interval / 2 + (m_data.count / 2 - 1)*m_data.x_interval);
    }
    else //奇数
    {
        angleLeft = (m_data.count - 1) / 2 * m_data.angle_interval;
        offsetXLeft = -1 * (m_data.count - 1) / 2 * m_data.x_interval;
    }

    float offsetAngle = 0;
    if (m_data.angle_of_center > 0 && m_data.angle_of_center < 180)
    {
        offsetAngle = m_data.angle_of_center - 90;
    }
    else
    {
        offsetAngle = m_data.angle_of_center - 270;
    }

    for (int i = 0; i < m_data.count; i++)
    {
        float a = angleLeft - m_data.angle_interval * i;

        float l = offsetXLeft + m_data.x_interval * i;
        float x = l * cos(CC_DEGREES_TO_RADIANS(offsetAngle));
        float y = -l * sin(CC_DEGREES_TO_RADIANS(offsetAngle));
        Vec2 v(m_data.origin_offset_x + x, m_data.origin_offset_y + y);     
        if (!AddBulletOne(v, a))
        {
            break;
        }
    }
}

float ScatterBullet::calculateCenterLineDegree(Bullet* pBullet, Vec2& src)
{
    float angle = (m_shootCnt - 1) * m_data.rotate_angle;
    if (fabs(angle) > m_data.rotate_max_angle && m_data.rotate_max_angle >= 1)
    {
        if (m_data.rotate_flag == 0)
        {
            //m_shootAddFlag = -m_shootAddFlag;
            if (angle * m_data.rotate_angle > 0)
            {
                m_shootAddFlag = -1;
            }
            else
            {
                m_shootAddFlag = 1;
            }
        }
        else
        {
            m_shootCnt = 0;
            m_shootAddFlag = 1;
        }
    }

    return m_data.angle_of_center + angle;
}

//degree 角度偏移。正数表示逆时针,负数表示顺时针
bool ScatterBullet::AddBulletOne(Vec2& srcOffset, float degree)
{
    //添加一个子弹精灵
    Bullet* pBullet = BulletGroup::getOneBullet();
    if (!pBullet || !getPlane())
    {
        return false;
    }

    //子弹的位置
    Vec2 src;
    if (getPlane()->getAircraftType() == EAircraftType::Type_Wingman) //僚机
    {
        Aircraft* pMainPlane = dynamic_cast<Aircraft*>(getPlane()->getParent());

        src = pMainPlane->getPosition() + getPlane()->getPosition() - pMainPlane->getContentSize() / 2;
    }
    else //非僚机
    {
        src = getPlane()->getPosition();
    }

    src.y += srcOffset.y;

    if (m_data.count == 1)
    {
        if (m_data.angle_of_center > 0 && m_data.angle_of_center < 180)
        {
            src.x += srcOffset.x;
        }
        else
        {
            src.x += -srcOffset.x;
        }
    }

    //这一句必须要放在src.x += srcOffset.x之前
    float centerLineDegree = calculateCenterLineDegree(pBullet, src)/* + offsetDegree*/;

    if (m_data.count > 1)
    {
        if (m_data.angle_of_center > 0 && m_data.angle_of_center < 180)
        {
            src.x += srcOffset.x;
            src.y += pBullet->getContentSize().height / 2;
        }
        else
        {
            src.x += -srcOffset.x;
            src.y -= pBullet->getContentSize().height / 2;
        }
    }
    pBullet->setPosition(src);


    if (sinf(CC_DEGREES_TO_RADIANS(m_data.angle_of_center)) > 0)
    {
        pBullet->setRotation(90 - (centerLineDegree + degree));
    }
    else
    {
        pBullet->setRotation(90 - (centerLineDegree + degree) + 180);
    }

    if (degree > 0)
    {
        pBullet->setFlippedX(true);
    }

    //body
    Vec2& pos = m_bodyCenter;
    Vec2 pos2;
    pos2.x = sqrt(pow(pos.x, 2) + pow(pos.y, 2)) * cos(CC_DEGREES_TO_RADIANS(centerLineDegree + degree));
    pos2.y = sqrt(pow(pos.x, 2) + pow(pos.y, 2)) * sin(CC_DEGREES_TO_RADIANS(centerLineDegree + degree));
    pBullet->setBodyCenter(pos2);
    pBullet->setBodySize(getBodySize());

    //子弹飞出屏幕所需的距离、飞行速度、飞行时间
    float realMoveDuration = m_flyDistance / m_data.speed;

    //在realMoveDuration时间内,飞到指定位置
    float deltaX = m_flyDistance * cos(CC_DEGREES_TO_RADIANS(centerLineDegree + degree));
    float deltaY = m_flyDistance * sin(CC_DEGREES_TO_RADIANS(centerLineDegree + degree));

    Vec2 dest = Vec2(src.x + deltaX, src.y + deltaY);
    FiniteTimeAction* actionMove = CCMoveTo::create(realMoveDuration, dest);
    FiniteTimeAction* actionDone = CallFuncN::create(CC_CALLBACK_0(BulletGroup::bulletMoveFinished, this, pBullet));

    //开始执行动作
    Sequence* sequenceL = Sequence::create(actionMove, actionDone, NULL);
    pBullet->runAction(sequenceL);

    return true;
}

核心方法

  • 初始化、复位、销毁
  • 命之抽或多、判断是否生在
  • 初步打、停止射击、是否正在开
  • 飞行
  • 爆炸

跟踪导弹

跟导弹是同样种特别的枪弹,当放出导弹时,它首先会见自动招来并锁定距最近敌机,然后于一个初速度开始,加速奇怪为目标(普通子弹是匀速飞行)。由于目标是活动的,所以导弹还有着自动调整飞行角度的力。但是,导弹还存在转弯半径这个范围,即导弹连无克轻易转弯,它是角速度参数,每个单位时外无限可怜不得不偏转一个原则性的角度。上述所有的逻辑都以Missile类的update措施被,详见下的代码:

void Missile::update(float dt)
{
    if (!this->isVisible() || m_pBulletGroup == NULL)
    {
        return;
    }

    //飞出屏幕
    const Rect& rect = this->getBoundingBox();
    const Size& windowSize = Director::getInstance()->getWinSize();
    if (rect.getMinX() > windowSize.width || rect.getMaxX() < 0
        || rect.getMinY() > windowSize.height || rect.getMaxY() < 0)
    {
        m_pBulletGroup->bulletMoveFinished(this);
        return;
    }

    //寻找距离最近,且夹角小于70度的敌机
    if ((!m_pEnemy || !m_pEnemy->isAlive()) && m_pBulletGroup && m_pBulletGroup->m_otherSideArray)
    {
        ((MissileGroup*)m_pBulletGroup)->searchEnemy(this);
    }

    float f = 0;
    if (m_pEnemy && m_pEnemy->isAlive())
    {
        //转向目标
        float curRot = getRotation();
        float angle = -CC_RADIANS_TO_DEGREES((getPosition() - m_pEnemy->getPosition()).getAngle());
        float tmpAngle = angle;
        if (angle - 90 - curRot < -90 && angle - 90 - curRot + 360 < 90)
        {
            angle += 360;
        }
        else if (angle - 90 - curRot > 90 && angle - 90 - curRot - 360 > -90)
        {
            angle -= 360;
        }
        else
        {
            if (fabsf(m_fLastAngle - angle) > 180)
            {
                if (m_fLastAngle > 0)
                {
                    angle += 360;
                }
                else if (m_fLastAngle < 0)
                {
                    angle -= 360;
                }
            }
        }

        m_fLastAngle = angle;

        //最大偏转角度
        float angleDif = std::min(std::max((angle - 90) - curRot, -m_fTurnRate*dt), m_fTurnRate*dt);
        f = curRot + angleDif;
        //DEBUG_LOG("Missile[%p,%.0f,%.0f] aimed emeny[%p,%.0f,%.0f], "
        //  "angle[%.2f, %.2f],max[%.2f], curRot[%.2f],angleDif[%.2f],f[%.2f]",
        //  this, getPosition().x, getPosition().y,
        //  m_pEnemy, m_pEnemy->getPosition().x, m_pEnemy->getPosition().y,
        //  tmpAngle, angle, m_fTurnRate*dt, curRot, angleDif, f);

    }

    if (!m_pEnemy || !m_pEnemy->isAlive())
    {
        f = getRotation();
        //DEBUG_LOG("Missile[%p,%.0f,%.0f] aimed emeny[NULL], angle[%f]", 
        //  this, getPosition().x, getPosition().y, f);
    }

    setRotation(f);
    setPosition(getPosition() + 
        Vec2(sinf(CC_DEGREES_TO_RADIANS(f))*m_fVelocity, cosf(CC_DEGREES_TO_RADIANS(f))*m_fVelocity)
        * Director::getInstance()->getScheduler()->getTimeScale());

    Vec2 pos2;
    float dd = sqrt(pow(m_bodyCenter.x, 2) + pow(m_bodyCenter.y, 2));
    pos2.x = dd * cos(CC_DEGREES_TO_RADIANS(90 - f));
    pos2.y = dd * sin(CC_DEGREES_TO_RADIANS(90 - f));
    this->setBodyCenter(pos2);

    //当子弹旋转超过45°时,宽高值交换
    if (fabsf(f) > 45 && fabsf(f) < 135)
    {
        Size size = getOrignBodySize();
        float tmp = size.width;
        size.width = size.height;
        size.height = tmp;
        this->setBodySize(size);
    }

    m_fVelocity += m_fAccel*dt;
}

高级道

  • 起活动射击、停止活动开、自动改变子弹级别
  • 子弹升一个级
  • 子弹升到最高阶段
  • 子弹降一个美高梅4688.com流
  • 是不是是最高级别子弹
  • 是否是最低级别子弹
  • 取得子弹等
  • 重置子弹等
  • 子弹用完发生的通告
  • 更新配备
  • 守作用的增多、去除

 详细代码如下:

//飞机的基类
class Aircraft : public GameObject, public IBulletUseUpListener
{
public:
    Aircraft();
    virtual ~Aircraft();

    virtual bool init(Node* parent, const TAircraftData* pData, const TAircraftLevelData* pLevelData = nullptr);
    virtual void reset();
    virtual void destory();

    virtual void reduceHp(int hp);
    virtual void recoverHp(int hp);
    virtual bool isAlive();

    virtual void startShoot();
    virtual void stopShoot();
    virtual bool isShooting();

    virtual void autoShoot();
    virtual void stopAutoShoot();
    virtual void autoChangeBulletGrade();

    //自动飞行
    virtual void fly(){};

    //爆炸
    virtual void blast();

    //更新装备
    bool updateEquip();

    //子弹升级
    virtual bool upgradeBullet();
    virtual bool upgradeMaxBullet();
    //子弹降级
    virtual bool downgradeBullet();
    //是否暴走
    virtual bool isMaxLevelBullet();
    //是否最低级子弹
    virtual bool isMinLevelBullet();
    //获取子弹等级
    virtual int getBulletLevel();
    //重置子弹级别
    virtual bool resetBulletLevel();

    //子弹用完通知
    virtual void bulletUseUp() override;

    //量子护盾
    void addShield();
    void removeShield();
    //防御效果结束
    virtual void defenceDone(float dt);

    //碰撞检测
    virtual bool isCollsion(const Rect& rect, Rect* pCollsionRect = nullptr);

    //获取攻击、防御、导弹、僚机、综合性能
    inline int getAttrAttack(){ return m_iAttrAttack; }
    inline int getAttrArmor() { return m_iAttrArmor; }
    inline int getAttrMissile(){ return m_iAttrMissile; }
    inline int getAttrWingman() { return m_iAttrWingman; }
    inline int getAttrTotal() { return m_iAttrTotal; }

    void calculateAttr();

    inline int getId()
    {
        return m_data.id;
    }

    inline int getLevelId()
    {
        if (m_data.pAircraftLevelData)
        {
            return m_data.pAircraftLevelData->id;
        }
        else
        {
            return -1;
        }
    }

    inline int getHp()
    {
        return m_data.hp;
    }

    inline void setHp(int hp)
    {
        m_data.hp = hp;
    }

    inline int getMaxHp()
    {
        return m_data.maxHp;
    }

    inline void setMaxHp(int max)
    {
        m_data.maxHp = max;
    }

    inline int getAttack()
    {
        //两机相撞时,对对方产生的伤害就是自己的血量
        return getHp();
    }

    inline void setAttack(int a)
    {
        m_data.attack = a;
    }

    void setNoDie(bool b);

    inline bool isNoDie()
    {
        return m_bNoDie;
    }

    inline Vector<BulletGroup*>* getBulletGroupArray()
    {
        return &m_bulletGroupArray;
    }

    inline EAircraftType getAircraftType()
    {
        return m_eAircraftType;
    }

    inline const std::string& getAttr(const std::string& key)
    {
        static std::string empty = "";
        auto it = m_data.paramMap.find(key);
        if (it != m_data.paramMap.end())
        {
            return it->second;
        }
        else
        {
            return empty;
        }
    }

    inline int getAircraftLevelId()
    {
        if (m_data.pAircraftLevelData)
        {
            return m_data.pAircraftLevelData->id;
        }
        else
        {
            return -1;
        }
    }

    Vector<Aircraft*>* getOtherSidePlane() const;
    void setOtherSidePlane(Vector<Aircraft*>* const planes);

    //回收
    virtual void recycle();
    //重用
    virtual void reuse();
    //是否已回收(是否可用)
    bool isRecycled();

protected:
    virtual void setBulletGrade(unsigned grade){ m_iBulletGrade = grade; }
    virtual void setAttackAdjust(float adjust){ m_fAttackAdjust = adjust; }
    virtual void setMissileAdjust(float adjust){ m_fMissileAdjust = adjust; }
    virtual void setBulletSpeedAdjust(float adjust){ m_fBulletSpeedAdjust = adjust; }

    virtual bool initBody(Node* parent, const TAircraftData* pData);
    virtual bool initPosition(Node* parent, const TAircraftData* pData);
    virtual bool initFire(Node* parent, const TAircraftData* pData);
    virtual bool initHpBar(Node* parent, const TAircraftData* pData);
    virtual bool initBullet(Node* parent, const TAircraftData* pData);
    virtual bool initWingman(Node* parent, const TAircraftData* pData);

    //添加尾部的左右两个火焰动画
    bool addFire(float offsetX, float offsetY, bool isFlipped);

protected:
    EAircraftType m_eAircraftType;
    TAircraftData m_data;
    const TAircraftData* m_pDataCopy;

    //战机所有的装备
    const TEquipmentData* m_pEquipAircraft;
    const TEquipmentData* m_pEquipArmature;
    const TEquipmentData* m_pEquipMissile;
    const TEquipmentData* m_pEquipWingman;

    int m_iBulletGrade;
    float m_fAttackAdjust;
    float m_fMissileAdjust;
    float m_fBulletSpeedAdjust;

    float m_fVipRelifeAttrAdjust;

    float m_fDefence;           //防御系数
    float m_fDefenceDura;       //防御持续时间
    int m_iCurBulletGrade; //当前子弹等级
    bool m_bNoDie;              //无敌
    cocostudio::Armature* m_pDefenceBall;     //护盾球
    HpBar* m_pHpBar;            //血槽精灵
    Vector<BulletGroup*> m_bulletGroupArray;  //多种子弹
    Vector<Aircraft*>    m_wingmanArray;      //僚机精灵
    Vector<Aircraft*>*   m_otherSideArray;    //对方飞机。对于玩家来说就是敌机,对于敌机来说就是玩家

    int m_iAttrAttack;
    int m_iAttrArmor;
    int m_iAttrMissile;
    int m_iAttrWingman;
    int m_iAttrTotal;

    bool m_bRecycled;

    bool m_bAutoShoot;
    int m_orignBulletGrade;
};

  为了构建不同之飞机对象,我们多一个机工厂。工厂的create函数中TAircraftData参数是起配置(sqlite数据库)中得到的。

template<typename T>
class PlaneCreator
{
public:
    static T* create(Node* parent, const TAircraftData* data)
    {
        T *pRet = new(std::nothrow) T();
        if (pRet && pRet->init(parent, data))
        {
            pRet->autorelease();
            return pRet;
        }
        else
        {
            delete pRet;
            pRet = NULL;
            return NULL;
        }
    }

};

激光

激光看似与普通子弹或导弹完全两样,但是仔细分析下,也堪看作是同样粒特殊之子弹:

  1. 激光始终都不过是一律粒子弹(并没多粒子弹)
  2. 激光外形是一个尺寸可变的矩形。最要命尺寸是屏幕的万丈,当遇敌机时,激光的长度是起飞机及敌机之间的离开
  3. 激光对目标发出的加害依赖让其接触目标的光阴,并负有线性关系。
    经上述剖析,可以博得激光最独特的地方在于那外观的可变性,下面是切实可行的代码:

    bool Laser::init(BulletGroup* pBulletGroup)
    {

    if (!GameObject::init())
    {
        return false;
    }
    
    m_pBulletGroup = pBulletGroup;
    //int planeZOrder = m_pBulletGroup->m_plane->getLocalZOrder();
    if (m_pBulletGroup->getPlane()->getAircraftType() == EAircraftType::Type_Killer)
    {
        m_pBulletGroup->getParent()->addChild(this, CONSTANT::ZORDER_BULLET_KILLER);
    }
    else if (m_pBulletGroup->getPlane()->getAircraftType() == EAircraftType::Type_Player)
    {
        m_pBulletGroup->getParent()->addChild(this, CONSTANT::ZORDER_BULLET_PLAYER);
    }
    else
    {
        int planeZOrder = CONSTANT::ZORDER_BULLET_PLAYER;
        m_pBulletGroup->getParent()->addChild(this, planeZOrder - 1);
    }
    
    //这里不能用createWithSpriteFrameName,否则激光会变成一段一段的(中间有黑色间隙)
    Sprite* pLaserNode = Sprite::create(m_pBulletGroup->m_data.styleArray.at(0));
    
    m_fWidth = pLaserNode->getContentSize().width;//宽度
    m_fHeight = CONSTANT::DESIGN_RES_HEIGHT + 50;//初始长度,理论上是无限高,这里只需要大于屏幕的高度即可
    
    //设置刚体的大小
    setBodySize(Size(pBulletGroup->getBodySize().width, m_fHeight)); 
    setBodyCenter(pBulletGroup->getBodyCenter());
    
    setContentSize(Size(pBulletGroup->getBodySize().width, m_fHeight));
    
    //计算纹理的个数
    int cnt = (int)(m_fHeight / pLaserNode->getContentSize().height + 0.9999);
    //实际高度
    float h = cnt * pLaserNode->getContentSize().height;
    
    //高度调整为2的n次方
    h = toPOT(h);
    cnt = (int)(h / pLaserNode->getContentSize().height + 0.9999);
    
    // 1: Create new CCRenderTexture
    int potW = toPOT(m_fWidth);
    RenderTexture *rt = RenderTexture::create(potW, h);
    
    // 2: Call CCRenderTexture:begin
    rt->begin();
    
    //开始贴图
    pLaserNode->setFlippedY(true);
    pLaserNode->setPosition(Vec2(m_fWidth / 2, pLaserNode->getContentSize().height / 2));
    pLaserNode->visit();
    
    for (int i = 1; i < cnt; i++)
    {
        //这里不能用createWithSpriteFrameName,否则激光会变成一段一段的(中间有黑色间隙)
        Sprite* pLaserNode2 = Sprite::create(m_pBulletGroup->m_data.styleArray.at(0));
        pLaserNode2->setFlippedY(true);
        pLaserNode2->setPosition(Vec2(m_fWidth / 2, 
            pLaserNode->getContentSize().height / 2 + pLaserNode->getContentSize().height * i));
        pLaserNode2->visit();
    }
    
    // 4: Call CCRenderTexture:end
    rt->end();
    
    // 5: Create a new Sprite from the texture
    m_pLaser = Sprite::createWithTexture(rt->getSprite()->getTexture());
    
    m_pLaser->setTextureRect(Rect(0, 0, m_fWidth, m_fHeight));
    
    Texture2D::TexParams tp = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT };
    m_pLaser->getTexture()->setTexParameters(tp);
    
    m_pLaser->setAnchorPoint(Vec2(0.5, 0));
    m_pLaser->setPosition(Vec2(getContentSize().width / 2, 0));
    addChild(m_pLaser);
    
    //添加爆炸点
    m_pBlast = GameObject::createSpriteWithFileList(m_pBulletGroup->m_data.blastStyleArray, m_pBulletGroup->m_data.blastAniDura);
    if (m_pBlast)
    {
        m_pBlast->setPosition(Vec2(getBodySize().width / 2, getBodySize().height));
        addChild(m_pBlast);
    }
    
    scheduleUpdate();
    
    return true;
    

    }

    void Laser::rejustHeight(float fMinY)
    {

    if (m_pBulletGroup && m_pBulletGroup->m_plane)
    {
        //根据敌机的位置计算激光的高度
        m_fHeight = fMinY - m_pBulletGroup->m_plane->getPosition().y;
        if (m_fHeight < 0)
        {
            m_fHeight = CONSTANT::DESIGN_RES_HEIGHT + 200;
        }
        setBodySize(Size(m_pBulletGroup->getBodySize().width, m_fHeight));
        if (m_pBlast)
        {
            m_pBlast->setPosition(Vec2(getBodySize().width / 2, getBodySize().height));
        }
    }
    

    }

    void Laser::update(float dt)
    {

    if (!this->isVisible() || m_pBulletGroup == NULL)
    {
        return;
    }
    
    //计算当前位置
    setBodySize(Size(getBodySize().width, CONSTANT::DESIGN_RES_HEIGHT));
    
    m_fTmpOffset += m_pBulletGroup->m_data.speed * dt;
    
    const Size& textureSize = m_pLaser->getTextureRect().size;
    m_pLaser->setTextureRect(Rect(0, m_fTmpOffset, textureSize.width, m_fHeight));
    

    }

其中,init用以创造激光外观,这里通过{ GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT }来创造一个重复的纹理,然后以update面临为纹路理无停止的上扬滚动。在rejustHeight遭受,我们根据目标的Y轴坐标来测算激光的莫大,并在激光接触目标的职位多一个中效果的贴图。

转载请注明:http://www.cnblogs.com/thorqq/p/6563904.html
正文系源码:http://files.cnblogs.com/files/thorqq/Bullet.rar

飞机池

   
当界面上之飞行器于多,并且反复出现、被击落的时候,系统会无鸣金收兵的开创、销毁飞机对象,这样见面严重影响游戏之帧率,所以,我们增加了一个略的飞机池:当飞机被击落时,飞机对象并没有被灭绝掉,而只是停止射击、停止所有动画并藏起来。当得创造新的飞机时,会起池子中搜索出没有发出对应之已回收的飞行器,如果找到,则针对斯目标还开展初始化。代码如下:

class AircraftPool
{
public:
    static AircraftPool* getInstance();

    //获取一架飞机
    template<typename T>
    T* get(Node* parent, const TAircraftData* pAircraftData, const TAircraftLevelData* pAircraftLevelData)
    {
        //AircraftPool 只在急速模式下使用。其他两种模式反而会增加内存
        if (GameData::getInstance()->getValueToInt(GAMEDATA::MODE) != ModeBase::ModeRapid)
        {
            return PlaneCreator<T>::create(parent, pAircraftData, pAircraftLevelData);
        }

        auto it = m_aircraftMap.find(pAircraftLevelData->id);
        std::vector<Aircraft*>* pArray = nullptr;
        if (it != m_aircraftMap.end())
        {
            pArray = it->second;
        }
        else
        {
            pArray = new std::vector<Aircraft*>;
            m_aircraftMap.insert(std::map<int, std::vector<Aircraft*>*>::value_type(pAircraftLevelData->id, pArray));
        }

        //查找可用的飞机
        for (Aircraft* p : *pArray)
        {
            if (p && p->isRecycled())
            {
                p->reuse();
                T* ret = dynamic_cast<T*>(p);
                if (ret)
                {
                    parent->addChild(ret);
                    return ret;
                }
                else
                {
                    DEBUG_LOG("fuck error type of aircraft");
                }
            }
        }

        //没找到,新建一个
        auto p = PlaneCreator<T>::create(parent, pAircraftData, pAircraftLevelData);
        p->retain();
        for (unsigned i = 0; i < pArray->size(); i++)
        {
            if ((*pArray)[i] == nullptr)
            {
                (*pArray)[i] = p;
                return p;
            }
        }

        pArray->push_back(p);
        return p;
    }

    //回收一架飞机
    void recycle(Aircraft* pAircraft);

    //清空pool
    void release();

protected:
    AircraftPool();

private:
    static AircraftPool* m_pInstance;

    std::map<int, std::vector<Aircraft*>*> m_aircraftMap;
};

AircraftPool* AircraftPool::m_pInstance = nullptr;

AircraftPool* AircraftPool::getInstance()
{
    if (!m_pInstance)
    {
        m_pInstance = new AircraftPool();
    }

    return m_pInstance;
}

AircraftPool::AircraftPool()
{
}

//回收一架飞机
void AircraftPool::recycle(Aircraft* pAircraft)
{
    //如果在池中,则回收;否则直接销毁
    auto it = m_aircraftMap.find(pAircraft->getAircraftLevelId());
    std::vector<Aircraft*>* pArray = nullptr;
    if (it != m_aircraftMap.end())
    {
        pArray = it->second;
        for (Aircraft* p : *pArray)
        {
            if (p == pAircraft)
            {
                p->recycle();
                return;
            }
        }
    }

    pAircraft->destory();
}

//清空pool
void AircraftPool::release()
{
    for (auto it : m_aircraftMap)
    {
        bool bInUse = false;
        std::vector<Aircraft*>* pArray = it.second;
        for (Aircraft* p : *pArray)
        {
            if (p && p->isRecycled())
            {
                p->destory();
                p->release();
            }
            else if (p)
            {
                bInUse = true;
                DEBUG_LOG("Aircraft[%d] can't release", p->getId());
                //CCASSERT(false, "Release error aircraft");
            }
        }

        if (!bInUse)
        {
            pArray->clear();
            delete pArray;
        }
    }

    m_aircraftMap.clear();
}

//////////////////////////////////////////////////////////
//下面是Aircraft对象中的回收、重用等方法
//回收
void Aircraft::recycle()
{
    if (m_bRecycled)
    {
        return;
    }

    setNoDie(false);
    //for (int i = 0; i < m_bulletGroupArray.size(); i++)
    //{
    //  m_bulletGroupArray.at(i)->setPlane(NULL);
    //}
    stopShoot();

    for (int i = 0; i < m_wingmanArray.size(); i++)
    {
        //m_wingmanArray.at(i)->recycle();
        AircraftPool::getInstance()->recycle(m_wingmanArray.at(i));
    }

    pause();

    m_bRecycled = true;
}

//重用
void Aircraft::reuse()
{
    if (!m_bRecycled)
    {
        return;
    }

    if (m_pHpBar)
    {
        m_pHpBar->setMaxValue(m_data.maxHp);
        m_pHpBar->setCurValue(m_data.maxHp);
    }
    m_data.hp = m_data.maxHp;

    //for (int i = 0; i < m_bulletGroupArray.size(); i++)
    //{
    //  m_bulletGroupArray.at(i)->setPlane(this);
    //}
    m_iCurBulletGrade = 0;

    for (int i = 0; i < m_wingmanArray.size(); i++)
    {
        m_wingmanArray.at(i)->reuse();
    }

    if (m_pArmature)
    {
        m_pArmature->setColor(Color3B::WHITE);
        m_pArmature->getAnimation()->play(GlobalData::getInstance()->getArmatureData(m_data.armatureName)->defaultAction);
    }
    else
    {
        this->setColor(Color3B::WHITE);
    }

    resume();

    m_bRecycled = false;
}

//是否已回收(是否可用)
bool Aircraft::isRecycled()
{
    return m_bRecycled;
}

  

转载请注明:http://www.cnblogs.com/thorqq/p/6323156.html

源码库地址:https://git.oschina.net/thorqq/RaidenFree

发表评论

电子邮件地址不会被公开。 必填项已用*标注