仿《雷霆战机》飞行射击手游开发--飞机(含源码库地址)
转载请注明:http://www.cnblogs.com/thorqq/p/6323156.html
代码库:https://git.oschina.net/thorqq/RaidenFree
下面我们继续分析这款游戏。
飞机
这是一款打飞机游戏,所以主角当然是飞机。游戏里,飞机包括以下几种:
- 玩家飞机:玩家控制的主飞机
- 僚机:在玩家飞机左右的小飞机,相对于玩家飞机的位置是固定不变的。
- 普通敌机:不会变形的敌机
- 变形敌机:飞到指定位置后变形,然后开始射击。被击落时,屏幕会震动
- BOSS:从屏幕上方飞下来,飞到指定位置后变形,然后开始左右移动,同时开始射击。boss可以有多次变形,当血量低于一定值时会触发变形,同时攻击力增强。Boss被击落后,飞机上会产生多次爆炸,屏幕会伴随着震动。
- 必杀僚机:必杀技。当玩家点击必杀按钮时,会从屏幕下方出现一个庞大的飞机,发射超级激光
飞机的基本属性
- 名字
- 类型
- 骨骼动画(飞行、左右侧飞、变形、暴走、尾部火焰)
- 生命值
- 攻击值:当飞机与飞机相撞时,对对方飞机产生的伤害值
- 碰撞体(刚体)的中心坐标和大小(只有当子弹的碰撞体与飞机的碰撞体发生重叠时,才会产生伤害)
- 缩放比例
- 血槽的样式和位置
- 飞机的初始位置(对于玩家飞机来说,一般是从屏幕下方飞入;对于敌机来说,一般是从上半个屏幕的某个地方飞出来)
- 子弹(弹夹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; //飞机参数 }
基本方法
- 初始化、复位、销毁
- 生命的减少或增加、判断是否活着
- 开始射击、停止射击、是否正在射击
- 飞行
- 爆炸
高级方法
- 开始自动射击、停止自动射击、自动改变子弹级别
- 子弹升一个等级
- 子弹升到最高等级
- 子弹降一个等级
- 是否是最高级别子弹
- 是否是最低级别子弹
- 获取子弹等级
- 重置子弹等级
- 子弹用完发出的通知
- 更新装备
- 防御效果的增加、去除
详细代码如下:
//飞机的基类 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; } } };
飞机池
当界面上的飞机较多,并且频繁出现、被击落的时候,系统会不停的创建、销毁飞机对象,这样会严重影响游戏的帧率,所以,我们增加了一个简单的飞机池:当飞机被击落时,飞机对象并没有被销毁掉,而只是停止射击、停止所有动画并隐藏起来。当需要创建新的飞机时,会从池中查找有没有对应的已回收的飞机,如果找到,则对此对象重新进行初始化。代码如下:
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; }