本篇博客我们为游戏添加敌机,敌机一共有三种,他们具有不同的纹理图片,血量值是不一样的,爆炸所使用的动画纹理也是不一样的,移动的速度我这里设置为了相同,出现的频率也是不同的,最大的敌机出现的次数较少,同时还具有动画效果,最后我们要根据玩家的得分,来改变敌机出现的多少。所以,先新建一个敌机类,这个类同样继承自Sprite,代码如下。

#ifndef _ENEMY_BASE_H_
#define _ENEMY_BASE_H_
#include "cocos2d.h"

USING_NS_CC;

//敌机类的父类,继承自Sprite
class EnemyBase : public Sprite
{
public:
	EnemyBase(void);
	~EnemyBase(void);
public:
	bool init();
	CREATE_FUNC(EnemyBase);
public:
	//爆炸动画
	void blowUp();
	void onEnterTransitionDidFinish();
	//改变敌机的坐标
	void move(float tm);
	//初始化敌机,里边传入需要使用的纹理名字,提供默认参数
	void initEnemy(std::string name = "enemy1",int count = 4);
private:
	//获得start-end范围内的一个随机数
	int getRandomNumber(int start,int end);
	//敌机的偏移量
	enum {OFFSET = 5};
	//初始化敌机用到的纹理名称
	std::string m_frameName;
	//爆炸效果的纹理数量
	int m_count;
	//敌机的血量值
	int m_hp;
};

#endif
#include "EnemyBase.h"

EnemyBase::EnemyBase(void)
{
}

EnemyBase::~EnemyBase(void)
{
}

bool EnemyBase::init()
{
	if(!Sprite::init())
		return false;

	return true;
}

//这里传入纹理的名称来初始化敌机
void EnemyBase::initEnemy(std::string name,int count)
{
	auto size = Director::getInstance()->getWinSize();

	//为成员变量赋值
	m_frameName = name;
	m_count = count;
	//第五个字符是一个数字字符,减去字符0获得血量值
	m_hp = name[5]-'0';

	//使用精灵图片初始化
	std::string str = name+".png";
	this->initWithSpriteFrameName(str);
	//横纵坐标按照以下的方法设置
	auto point = Point(getRandomNumber(this->getContentSize().width/2,size.width-this->getContentSize().width/2),
		size.height);
	this->setPosition(point);
}

//如果要获得a~b之间的一个随机数,则应该是CCRANDOM_0_1*(b-a)+a,传入的参数就是a和b
int EnemyBase::getRandomNumber(int start,int end)
{
	return CCRANDOM_0_1()*(end-start)+start;
}

//场景切换完调用
void EnemyBase::onEnterTransitionDidFinish()
{
	//先调用父类的
	Sprite::onEnterTransitionDidFinish();
	//调用schedule函数,来改变敌机的坐标
	this->schedule(SEL_SCHEDULE(&EnemyBase::move));
}

//敌机的移动
void EnemyBase::move(float tm)
{
	auto point = this->getPositionY()-OFFSET;
	this->setPositionY(point);
	//如果移动出了屏幕则从父节点中移除
	if(point < -this->getContentSize().height)
	{
		this->removeFromParentAndCleanup(true);
	}
}

//敌机爆炸动画,这个类是敌机类的基类,所以敌机的名字和用到的动画帧数是不确定的,需要在参数中传递
void EnemyBase::blowUp()
{
	Vector<SpriteFrame *> vector;
	for(int i=0;i<m_count;i++)
	{
		auto frameName = __String::createWithFormat("_down%d.png",i+1);
		//将传入的字符串和frameName拼接起来,str是最后使用的spriteframe的名字
		std::string str = m_frameName+frameName->getCString();
		auto spriteFrame = SpriteFrameCache::getInstance()->getSpriteFrameByName(str);
		vector.pushBack(spriteFrame);
	}
	//设置动画帧的间隔,同时动画只执行一次
	auto animation = Animation::createWithSpriteFrames(vector,0.2f,1);
	auto animate = Animate::create(animation);
	this->runAction(animate);
}

敌机类的实现中,init函数只是初始化一下父类,主要的初始化工作放在了initEnemy中进行,这里需要传入我们新建敌机的纹理图片,爆炸场景需要的纹理数量,而血量值是根据纹理图片的名称获得的,如果你要想改变敌机移动的速度,这里可以再加一个参数或者使用其他的实现方法,所以,当我们使用create函数创建了一个敌机对象的时候,一定要调用initEnemy函数来初始化一些东西。然后当场景切换完毕的时候,我们需要调用move函数,来移动敌机,产生敌机行走的效果。敌机初始化时候坐标的设置是通过一个随机数获取的,x方向的范围不能超过了边界。最后是敌机爆炸时候调用的一个函数,使用的纹理图片是initEnemy函数中传递进来的。下面看看在游戏主场景中如何添加敌机吧。

//场景切换完毕调用
void MainGame::onEnterTransitionDidFinish()
{
	//必须先调用父类的函数
	Layer::onEnterTransitionDidFinish();
	//添加敌机,每秒添加一个
	this->schedule(SEL_SCHEDULE(&MainGame::addEnemy),1.0f);
}

//添加敌机
void MainGame::addEnemy(float tm)
{
	//这里是根据分数来改变敌机出现的快慢,先写死了,后期添加上了分数再来做这块
	static int score = 0;
	score++;
	if(score>50)
	{
		//重新调用schedule会更新时间的
		this->schedule(SEL_SCHEDULE(&MainGame::addEnemy),0.8f);
	}
	else if(score>100)
	{
		this->schedule(SEL_SCHEDULE(&MainGame::addEnemy),0.6f);
	}
	else if(score>200)
	{
		this->schedule(SEL_SCHEDULE(&MainGame::addEnemy),0.5f);
	}
	else if(score>400)
	{
		this->schedule(SEL_SCHEDULE(&MainGame::addEnemy),0.3f);
	}

	auto enemy = EnemyBase::create();
	//根据不同的概率来添加不同种类的飞机
	int enemy_x = CCRANDOM_0_1()*9+1;
	int count = 0;
	if(enemy_x > 0 && enemy_x <7)
	{
		enemy_x = 1;
		count = 4;
	}
	else if(enemy_x >= 7 && enemy_x < 9)
	{
		enemy_x = 2;
		count = 4;
	}
	else
	{
		//敌机三用到的背景图片不太一样,这里单独的建立下
		count = 6;
		enemy->initEnemy("enemy3_n1",count);
		//创建敌机三的动画
		Vector<SpriteFrame *> vector;
		vector.pushBack(SpriteFrameCache::getInstance()->getSpriteFrameByName("enemy3_n1.png"));
		vector.pushBack(SpriteFrameCache::getInstance()->getSpriteFrameByName("enemy3_n2.png"));
		auto animation = Animation::createWithSpriteFrames(vector,0.2f,-1);
		auto animate = Animate::create(animation);
		enemy->runAction(animate);
		//添加到当前的层中
		this->addChild(enemy);
		//直接返回,不再执行下面的语句
		return;
	}
	//以下的这句话一定要调用
	auto str = __String::createWithFormat("enemy%d",enemy_x);
	enemy->initEnemy(str->getCString(),count);
	this->addChild(enemy);
}

用3.0实现飞机大战——添加敌机