这节完成了许多的功能:
1、添加小飞机、中等飞机、大飞机。
2、每种飞机的移动速度和血量不同。
3、飞机被子弹打击效果。
4、飞机被摧毁效果。
cocos2d提供的Sprite类不足以提供以上功能,所以我们需要新建一个Enemy类重写Sprite。
Enemy.h代码段
#ifndef __ENEMY_H__
#define __ENEMY_H__
#include "cocos2d.h"
//敌机类型
typedef enum
{
SMALL_ENEMY,MIDDLE_ENEMY,BIG_ENEMY
}EnemyType;
class Enemy : public cocos2d::Sprite {
public:
Enemy();
static Enemy* create(EnemyType);
bool init(EnemyType);
//碰撞
void hit();
//摧毁
void down();
EnemyType e_type;
//敌机移动速度
float e_step;
//敌机血量
int e_hp;
};
#endif
这里的e_step、e_hp、e_type我直接public了,可以自行封装。
Enemy.cpp代码段
#include "Enemy.h" Enemy::Enemy() { this->e_type = SMALL_ENEMY; this->e_hp = 0; } Enemy* Enemy::create(EnemyType type) { auto enemy = new Enemy(); if( enemy && enemy->init(type) ) { enemy->autorelease(); return enemy; } delete enemy; enemy = NULL; return NULL; } //根据不同的敌机建立不同的图片效果 bool Enemy::init(EnemyType type) { this->e_type = type; switch (type) { case SMALL_ENEMY: this->e_hp = 1; this->e_step = 4; cocos2d::Sprite::initWithSpriteFrameName("enemy1.png"); break; case MIDDLE_ENEMY: this->e_hp = 6; this->e_step = 1; cocos2d::Sprite::initWithSpriteFrameName("enemy2.png"); break; case BIG_ENEMY: this->e_hp = 30; this->e_step = 0.5f; cocos2d::Sprite::initWithSpriteFrameName("enemy3_n1.png"); break; default: break; } return true; } //打击效果 void Enemy::hit() { auto animation = cocos2d::Animation::create(); switch (this->e_type) { case MIDDLE_ENEMY: animation->addSpriteFrame(cocos2d::SpriteFrameCache::getInstance()-> getSpriteFrameByName("enemy2_hit.png")); animation->addSpriteFrame(cocos2d::SpriteFrameCache::getInstance()-> getSpriteFrameByName("enemy2.png")); break; case BIG_ENEMY: animation->addSpriteFrame(cocos2d::SpriteFrameCache::getInstance()-> getSpriteFrameByName("enemy3_hit.png")); animation->addSpriteFrame(cocos2d::SpriteFrameCache::getInstance()-> getSpriteFrameByName("enemy3_n1.png")); break; default: break; } animation->setDelayPerUnit(0.2f); auto animate = cocos2d::Animate::create(animation); this->runAction(animate); } //摧毁效果 void Enemy::down() { auto animation = cocos2d::Animation::create(); switch(this->e_type) { case SMALL_ENEMY: for (int i = 0; i < 4; i++) { auto png = cocos2d::StringUtils::format("enemy1_down%d.png",i+1); animation->addSpriteFrame(cocos2d::SpriteFrameCache::getInstance()->getSpriteFrameByName(png)); } break; case MIDDLE_ENEMY: for (int i = 0; i < 4; i++) { auto png = cocos2d::StringUtils::format("enemy2_down%d.png",i+1); animation->addSpriteFrame(cocos2d::SpriteFrameCache::getInstance()->getSpriteFrameByName(png)); } break; case BIG_ENEMY: for (int i = 0; i < 6; i++) { auto png = cocos2d::StringUtils::format("enemy3_down%d.png",i+1); animation->addSpriteFrame(cocos2d::SpriteFrameCache::getInstance()->getSpriteFrameByName(png)); } break; default: break; } animation->setDelayPerUnit(0.1f); auto animate = cocos2d::Animate::create(animation); //CallFunc是动作,允许我们的代码作为动作执行 //Sequence是动作,作用是将多个动作按顺序依次执行 auto callFuncN = cocos2d::CallFuncN::create([](Node* node) { //Node参数是这个动作的执行者 //将自己从父容器删除并清除(定时器,动作) node->removeFromParentAndCleanup(true); }); this->runAction(cocos2d::Sequence::create(animate,callFuncN,NULL)); }
#include "cocos2d.h"
#include "Enemy.h"
class FlyPlane : public cocos2d::Layer {
public:
CREATE_FUNC(FlyPlane);
bool init();
static cocos2d::Scene* createScene();
//定时器函数必须是void返回类型,必须有一个float类型的参数
void update(float);
private:
cocos2d::Point m_vec;
//英雄子弹集合
std::vector<cocos2d::Sprite*> h_bullets;
//敌机集合
std::vector<Enemy*> h_enemies;
//创建子弹
void createBullet(float);
//创建敌机
void createSmallEnemy(float);
void createMiddleEnemy(float);
void createBigEnemy(float);
void createEnemy(EnemyType);
};
#include "FlyPlane.h"
#include "CommonData.h"
cocos2d::Scene* FlyPlane::createScene() {
//1、auto是c++11的新特性,是自动根据变量的值确定变量的类型,类似var
//2、大多数时候,cocos中的类想获取它的对象,不建议去new,而是调用它的create方法(oc语法导致)
//调用cocos2d空间中scene类的静态create方法,返回一个cocos2d::Scene*对象给scene连接
//‘场景’是一个自动关联的对象
auto scene = cocos2d::Scene::create();
auto layer = FlyPlane::create();
//把层添加到场景里
scene->addChild(layer);
return scene;
}
bool FlyPlane::init() {
//一定要先调用父类初始函数
if( !cocos2d::Layer::init() ) {
return false;
}
//使用精灵集需要两步
//1、将美工做好的plist文件读取到缓存中
//2、通过帧名字创建精灵并显示
cocos2d::CCSpriteFrameCache::getInstance()->
addSpriteFramesWithFile("shoot_background.plist");
auto bg1 = cocos2d::Sprite::createWithSpriteFrameName("background.png");
//把精灵bg1加到FlyPlane层中,第二个参数ZOrder表示距离用户的距离,第三个参数tag设为1
this->addChild(bg1,-1,1);
//默认锚点为(0.5,0.5),只会显示一半的图,必须设置锚点为(0,0)
bg1->setAnchorPoint(cocos2d::Point(0,0));
//texture:纹理,通过精灵找到对应的纹理,并开启抗锯齿
bg1->getTexture()->setAliasTexParameters();
auto bg2 = cocos2d::Sprite::createWithSpriteFrameName("background.png");
this->addChild(bg2,2);
bg2->setAnchorPoint(cocos2d::Point(0,0));
bg2->getTexture()->setAliasTexParameters();
//添加英雄
cocos2d::CCSpriteFrameCache::getInstance()->
addSpriteFramesWithFile("shoot.plist");
auto hero = cocos2d::Sprite::createWithSpriteFrameName("hero1.png");
hero->setPosition(VISIBLE_SIZE.width / 2,100);
this->addChild(hero,3,3);
//为英雄添加飞行动作,动作由动画组成,所以得到动作对象前,需要先得到动画对象
//一、创建动画对象
//1.1通过create得到动画对象
auto animation = cocos2d::Animation::create();
//1.2添加这个动画所要用的精灵帧
animation->addSpriteFrame(cocos2d::SpriteFrameCache::getInstance()->
getSpriteFrameByName("hero1.png"));
animation->addSpriteFrame(cocos2d::SpriteFrameCache::getInstance()->
getSpriteFrameByName("hero2.png"));
//1.3设置切换时间
animation->setDelayPerUnit(0.2f);
//1.4循环次数,默认为1,设为-1使其无限循环
animation->setLoops(-1);
//二、根据动画对象创建动作对象
auto animate = cocos2d::Animate::create(animation);
//三、让hero执行这个动作
hero->runAction(animate);
//用单个处理事件添加鼠标监听事件
auto listener = cocos2d::EventListenerTouchOneByOne::create();
//用lambda表达式处理分解事件
//Lambda表达式:
// {} 类似于普通函数的函数体
// () 类似于普通函数的参数列表
// [] 默认Lambda表达式不能访问外部的变量,如果想访问外部变量,就通过中括号传进来
listener->onTouchBegan = [=](cocos2d::Touch* touch,cocos2d::Event*) {
auto touchPos = touch->getLocation();
bool isContain = hero->getBoundingBox().containsPoint(touchPos);
if(isContain) {
m_vec = hero->getPosition() - touchPos;//my_vector,记录touchPos指向hero的向量
}
return isContain;
};
const float leftMinX = hero->getContentSize().width / 2; //英雄的X最小值(左边界线)
const float rightMaxX = VISIBLE_SIZE.width - hero->getContentSize().width / 2;//英雄X最大值(右边界线)
const float downMinY = hero->getContentSize().height / 2; //英雄Y最小值(下边界线)
const float upMaxY = VISIBLE_SIZE.height - hero->getContentSize().height / 2; //英雄Y最大值(上边界线)
listener->onTouchMoved = [=](cocos2d::Touch* touch,cocos2d::Event*) {
auto touchPos = touch->getLocation() + m_vec;
//让英雄跟着手指动并且不超出边界
hero->setPosition(cocos2d::Point(MAX(leftMinX,MIN(rightMaxX,touchPos.x)),MAX(downMinY,MIN(upMaxY,touchPos.y))));
};
//将监听器添加到事件分配器上
this->getEventDispatcher()->
addEventListenerWithSceneGraPHPriority(listener,hero);
//定时添加子弹
schedule(schedule_selector(FlyPlane::createBullet),0.1f);
//定时添加不同敌机
schedule(schedule_selector(FlyPlane::createSmallEnemy),0.8f,2);
schedule(schedule_selector(FlyPlane::createMiddleEnemy),10,8);
schedule(schedule_selector(FlyPlane::createBigEnemy),20,14);
//定时器。scheduleUpdate每帧调用一次update函数
scheduleUpdate();
return true;
}
void FlyPlane::update(float) {
auto bg1 = this->getChildByTag(1);
auto bg2 = this->getChildByTag(2);
//bg1下落,bg2跟随
bg1->setPositionY(bg1->getPositionY() - 5);
bg2->setPositionY(bg1->getPositionY() + bg1->getContentSize().height);
if(bg2->getPositionY() <= 0) {
bg1->setPositionY(0);
}
//重置迭代器
std::vector<Enemy*>::iterator it_enemy = h_enemies.begin();
std::vector<cocos2d::Sprite*>::iterator it_bullet = this->h_bullets.begin();
//让子弹飞
while( it_bullet != this->h_bullets.end() ) {
(*it_bullet)->setPositionY((*it_bullet)->getPositionY() + 12);
//如果子弹飞出游戏区则清除
if((*it_bullet)->getPositionY() > VISIBLE_SIZE.height) {
this->removeChild((*it_bullet));
it_bullet = h_bullets.erase(it_bullet);
} else {
++it_bullet;
}
}
//让敌机飞
while( it_enemy != h_enemies.end() ) {
auto enemy = (*it_enemy);
enemy->setPositionY(enemy->getPositionY() - enemy->e_step);
//如果敌机飞出游戏区则清除
if(enemy->getPositionY() <= 0) {
this->removeChild(enemy);
it_enemy = h_enemies.erase(it_enemy);
} else {
it_enemy++;
}
}
//碰撞检测
it_enemy = h_enemies.begin();
it_bullet = this->h_bullets.begin();
//第一层循环遍历敌机
while( it_enemy != h_enemies.end()) {
it_bullet = h_bullets.begin(); //每次遍历,子弹迭代器置于起始位置
bool isDown = false;
//第二层循环遍历子弹
while(it_bullet != h_bullets.end()) {
//如果相撞
if((*it_bullet)->getBoundingBox().intersectsRect((*it_enemy)->getBoundingBox())) {
//在层中与容器中清除子弹并用isDown记录相撞
this->removeChild((*it_bullet));
it_bullet = h_bullets.erase(it_bullet);
isDown = true;
} else {
++it_bullet;
}
}
//如果相撞,运行打击动画
if(isDown == true) {
(*it_enemy)->hit();
(*it_enemy)->e_hp--; //敌机减血
if( (*it_enemy)->e_hp <= 0 ) { // 如果为0则被摧毁
(*it_enemy)->down();
it_enemy = h_enemies.erase(it_enemy);
}
} else {
++it_enemy;
}
}
}
void FlyPlane::createBullet(float) {
auto hero = this->getChildByTag(3);
auto bullet = cocos2d::Sprite::createWithSpriteFrameName("bullet1.png");//初始子弹在shoot.plist中名字叫bullet1
bullet->setPosition(hero->getPosition() + cocos2d::Point(0,hero->getContentSize().height / 2));//子弹初始位置
this->addChild(bullet,0); //zorder为0,高于背景层-1,低于英雄的3
//每创建一个子弹就存在容器中
h_bullets.push_back(bullet);
}
void FlyPlane::createEnemy(EnemyType type) {
auto enemy = Enemy::create(type);
float leftMinX = enemy->getContentSize().width / 2;
float rightMaxX = VISIBLE_SIZE.width - enemy->getContentSize().width / 2;
float x = rand() % (int)(rightMaxX - leftMinX + 1) + leftMinX; //产生一个[leftMinX,rightMaxX]的随机数
float y = VISIBLE_SIZE.height;
enemy->setPosition(x,y);
h_enemies.push_back(enemy);
this->addChild(enemy,0);
}
void FlyPlane::createSmallEnemy(float) {
this->createEnemy(SMALL_ENEMY);
}
void FlyPlane::createMiddleEnemy(float) {
this->createEnemy(MIDDLE_ENEMY);
}
void FlyPlane::createBigEnemy(float) {
this->createEnemy(BIG_ENEMY);
}
这里有三个定时器来产生三种敌机
//定时添加不同敌机
schedule(schedule_selector(FlyPlane::createSmallEnemy),0.8f,2);
schedule(schedule_selector(FlyPlane::createMiddleEnemy),8);
schedule(schedule_selector(FlyPlane::createBigEnemy),14);
//让敌机飞
while( it_enemy != h_enemies.end() ) {
auto enemy = (*it_enemy);
enemy->setPositionY(enemy->getPositionY() - enemy->e_step);
//如果敌机飞出游戏区则清除
if(enemy->getPositionY() <= 0) {
this->removeChild(enemy);
it_enemy = h_enemies.erase(it_enemy);
} else {
it_enemy++;
}
}
//碰撞检测
it_enemy = h_enemies.begin();
it_bullet = this->h_bullets.begin();
//第一层循环遍历敌机
while( it_enemy != h_enemies.end()) {
it_bullet = h_bullets.begin(); //每次遍历,子弹迭代器置于起始位置
bool isDown = false;
//第二层循环遍历子弹
while(it_bullet != h_bullets.end()) {
//如果相撞
if((*it_bullet)->getBoundingBox().intersectsRect((*it_enemy)->getBoundingBox())) {
//在层中与容器中清除子弹并用isDown记录相撞
this->removeChild((*it_bullet));
it_bullet = h_bullets.erase(it_bullet);
isDown = true;
} else {
++it_bullet;
}
}
//如果相撞,运行打击动画
if(isDown == true) {
(*it_enemy)->hit();
(*it_enemy)->e_hp--; //敌机减血
if( (*it_enemy)->e_hp <= 0 ) { // 如果为0则被摧毁
(*it_enemy)->down();
it_enemy = h_enemies.erase(it_enemy);
}
} else {
++it_enemy;
}
}
运行后效果如图: