zoukankan      html  css  js  c++  java
  • 仿《雷霆战机》飞行射击手游开发--飞机(含源码库地址)

    转载请注明: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;
    }
    

      

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

  • 相关阅读:
    python实用库:PrettyTable 学习
    centos启动错误:Inodes that were part of a corrupted orphan linked list found.
    C++:in namespace 'std' does not name a template type
    小程序实现单词查询搜索及搜索的历史记录
    小程序图片懒加载较完美解决方案
    下载文件到本地解压压缩包出现文件损坏,报错问题已解决
    彻底理解cookie,session,token
    vue全家桶(Vue+Vue-router+Vuex+axios)(Vue+webpack项目实战系列之二)
    与关系型数据库相比,MongoDB的优缺点
    漫谈JS 的继承方式
  • 原文地址:https://www.cnblogs.com/thorqq/p/6323156.html
Copyright © 2011-2022 走看看