仿《雷霆战机》飞行射击手游开发--子d、跟踪导d和激光

仿《雷霆战机》飞行射击手游开发--子d、跟踪导d和激光,第1张

概述转载请注明:http://www.voidcn.com/article/p-dkovfuob-bpr.html 试玩版下载:https://pan.baidu.com/s/1mhLBNVa (Windows版) 源代码下载:https://git.oschina.net/thorqq/RaidenFree 飞机的武器类型众多,大致可分为子d、跟踪导d和激光。子d是直线飞行的;导d会自动跟踪目标,可

转载请注明:http://www.jb51.cc/article/p-dkovfuob-bpr.html
试玩版下载:https://pan.baidu.com/s/1mhLBNVa (windows版)
源代码下载:https://git.oschina.net/thorqq/RaidenFree

飞机的武器类型众多,大致可分为子d、跟踪导d和激光。子d是直线飞行的;导d会自动跟踪目标,可曲线飞行;而激光是一道光束,可持续地对照射到的敌机产生伤害。子d根据一次性发射的数量,可分为单发和多发,根据子d发射的方向可分为:自动瞄准、平行、散射。本文将结合代码讲述有关飞机武器的程序设计。

程序设计

先看一下本游戏中有关武器的类的设计

其中,BulletGroup可理解为d匣,Bullet就是其中一颗一颗的子d,每颗子d都有相同的属性,包括外观、伤害值以及飞行属性。而继承自Bullet的五个子类分别为:
- ScatterBullet 散d。含单发和多发,平行和散射。
- AimscatterBullet 自动瞄准子d。继承自ScatterBullet,但是初始发射角度指向距离最近的敌机(飞行轨迹是直线,发射后不会改变方向)。
- CustomBullet 可自定义每颗子d初始的方向、速度。
- Missile 跟踪导d。顾名思义,在飞行过程中会自动改变方向,始终对准敌机,但受限于飞行速度、角速度等参数,也可能无法射中目标。
- Laser 激光。一道光束,伤害值依赖于接触的时间。

参数

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

std::string name; //名称std::string type; //类型:散d、导d、激光std::vector<std::string> styleArray; //帧动画图片资源float anIDura;//帧动画时间间隔std::string armaturename; //骨骼动画名称int topOfAircraft;//出现在飞机的上层还是下层int musicID;//音效int attack; //攻击值int speed;  //飞行速度。单位:像素/秒int count;//每次发射的子d的颗数int max_count;//最多发射多少颗子d。当达到最大值时,停止发射或自动降档float timelimit; //时间限制。当达到这个时间时,停止发射或自动降档float angle_of_center;//中心法线的角度。float angle_interval;//多颗子d同时发射时,这个角度就是相邻子d间的夹角float x_interval;//多颗子d同时发射时,两两间的横向间距float delay;//发射第一颗子d时的延迟时间//假设发射的顺序为:1.1.1...1.1.1...1.1.1... 其中“1”表示发射子d,“.”表示间隔时间//我们称“1.1.1...”是一个大周期,“1.1.1”是三个小周期float interval;//大周期之间的时间间隔,这里是“...”代表的时间float interval_2;//小周期之间的时间间隔,这里是“.”代表的时间int interval_2_cnt;//小周期里子d数量,这里是3float rotate_angle; //连续发射时,每次发射偏转的角度float rotate_max_angle;//最大累计偏转角度int rotate_flag;//累计偏转角度达到最大值时的处理方法,0:逐渐减小偏转角度;1:立刻还原到零float bodyCenterX;//碰撞体相对子d中心点的坐标偏移float bodyCenterY;float bodySizeW;//碰撞体的大小float bodySizeH;//发射原点相对飞机中心点的坐标偏移float origin_offset_x;float origin_offset_y;std::vector<std::string> fireFamenameArray; //尾部火焰动画,图片列表float fireAnIDura; //动画帧时长int   fireOntop; //尾部火焰显示在子d的上层还是下层float fireOffsetX;//火焰中心点相对于飞机底部中心点的偏移。如果等于0,则只有一个火焰;否则是两个火焰float fireOffsetY;std::vector<std::string> blastStyleArray;//子d打到目标后产生的爆炸的帧动画资源列表
基类设计 Bullet

Bullet类很简单,仅仅用于维护子d的外观,所以重要的只有bool Bullet::init(BulletGrouP* pBulletGroup)这一个方法。详见如下代码:

class Bullet : public GameObject{public:    frIEnd class BulletGroup;    static Bullet* create(BulletGrouP* pBulletGroup);    //根据pBulletGroup中的子d相关属性创建子d    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中的子d相关属性创建子dbool 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,true);                }                else                {                    addFire(0,false);                }            }        }        bRet = true;    } while (0);    return bRet;}bool Bullet::addFire(float offsetX,bool isFlipped){    Sprite* fire = GameObject::createSpriteWithfileList(        m_pBulletGroup->m_data.fireFamenameArray,m_pBulletGroup->m_data.fireAnIDura);    //子d的飞行速度比较快,所以火焰不需要动画    //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);    // 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;}
BulletGroup

BulletGroup是比较重要的类,包含了子d的基本参数(结构体TBulletData,用于”复制“出一颗一颗的子d)、发射动作、与飞机以及敌机间的关系。详细代码如下:

//子d的基类。内部维护了一个子d池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();    //发射指定数量的子d    virtual voID startShoot(int cnt);    //停止射击    virtual voID stopShoot();    //是否正在射击    virtual bool isShooting();    //从子d池中删除一颗子d。超出屏幕范围,或者击中目标    virtual voID RemoveBullet(Bullet* pBullet);    //爆炸    virtual voID blast(Bullet* pBullet);    //当对方飞机增加或者减少时,通知导d    virtual voID nodifyTargetRemove(Aircraft* pAircraft) {}    //注册子d用完监听器    voID regBulletUseUpListener(IBulletUseUpListener* l);    voID notifyBulletUseUp();    inline bool isUseUp() { return m_bIsUseUp; }public:    //子d飞出屏幕后的完成动作(内部函数)    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:    //发射一次子d(可能会包含多颗子d)    virtual voID AddBullet(float dt) {};    //从子d池中获取一颗子d    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; //小间隔子d发射次数    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,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);}//子d飞出屏幕后删掉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;    }}

子d的每次发射的时间点控制在update方法中,其中使用到了延时delay、大小周期intervalinterval_2interval_2_cnt等相关参数。当确定好每次发射的时间点后,会通过AddBullet来添加一颗子d,放在某个位置上,并让其发射出去,具体的放置位置和发射的角度速度等参数留给子类来实现,这里仅仅定义一个空方法:virtual voID AddBullet(float dt) {};。从方法RemoveBullet可以看到,当子d飞出屏幕或者击中目标时,我们销毁子d的方法仅仅是停止其动画效果,并设置为不可见,这样可以减少频繁创建/销毁子d带来的性能损失。当子d用完时,会通过方法notifyBulletUseUp来通知飞机,这样,飞机就会对子d进行降档,例如从暴走状态恢复到普通状态。

下面我们看看散d、跟踪导d、激光等各个子类的具体实现。

散d

最简单的散d就是每次只发射一颗子d,并且所有子d都朝着一个不变的方向飞行,看起来就像一条直线。当每次发射两颗或多颗子d时,看起来就是两条或多条直线,这多条直线或平行,或相邻之间存在一个固定的夹角。在代码中是通过angle_interval参数来控制的。

还有一种偏转角度随时间变化的散d,即每发射一颗子d,其飞行角度都会向某个方向偏转一个固定的角度,当达到一个最大角度值时,又会逐渐减小偏转角度,看起来就像个“之”字形。如果最大偏转角度不存在时,子d轨迹看起来就是个螺旋形状了。在代码中是通过rotate_anglerotate_max_angle来控制的。 下面我们看下具体代码。

//可自定义子d的个数、夹角、偏移class ScatterBullet : public BulletGroup{public:    ScatterBullet();    virtual bool init(Node* pParent,const TBulletData* pData);    virtual voID reset();protected:    //发射一次子d    virtual voID AddBullet(float dt) overrIDe;    //计算发射中心线的角度    virtual float calculateCenterlineDegree(Bullet* pBullet,Vec2& src);    //发射一颗子d    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,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)//偶数    {        //计算最左边的子d的夹角、偏移        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){    //添加一个子d精灵    Bullet* pBullet = BulletGroup::getoneBullet();    if (!pBullet || !getPlane())    {        return false;    }    //子d的位置    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)) * sin(CC_degrees_TO_radians(centerlineDegree + degree));    pBullet->setbodyCenter(pos2);    pBullet->setbodySize(getbodySize());    //子d飞出屏幕所需的距离、飞行速度、飞行时间    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;}
跟踪导d

跟踪导d是一种特殊的子d,当发射出导d时,它首先会自动寻找并锁定距离最近敌机,然后从一个初速度开始,加速飞向目标(普通子d是匀速飞行)。由于目标是运动的,所以导d还具备自动调整飞行角度的能力。但是,导d还存在转弯半径这个限制,即导d并不能任意转弯,它存在角速度参数,每个单位时间内最大只能偏转一个固定的角度。上述所有的逻辑都在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],"        // "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] aimed emeny[NulL],angle[%f]",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);    //当子d旋转超过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;}
激光

激光看似与普通子d或者导d完全不同,但是仔细分析下来,也可以看成是一颗特殊的子d:
1. 激光始终都只是一颗子d(并没有多颗子d)
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_REPEAT }来创建一个重复的纹理,然后在update中让纹理不停的向上滚动。在rejustHeight中,我们根据目标的Y轴坐标来计算激光的高度,并在激光接触目标的位置增加一个击中效果的贴图。

转载请注明:http://www.jb51.cc/article/p-dkovfuob-bpr.html

总结

以上是内存溢出为你收集整理的仿《雷霆战机》飞行射击手游开发--子d、跟踪导d和激光全部内容,希望文章能够帮你解决仿《雷霆战机》飞行射击手游开发--子d、跟踪导d和激光所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1083326.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-27
下一篇 2022-05-27

发表评论

登录后才能评论

评论列表(0条)

保存