继续打飞机之旅,这一篇主要加入敌机类,和碰撞检测,以及爆炸效果管理器。写完他,基本的打飞机游戏就出来了,只要在这个架构上继续完善,加入其他功能,一个完整的游戏就可以出来。废话不多说,现在开始。
1.敌机类
这个其实主要从子弹类那里copy过来,修改一下就可以了。因为他们的行为是类似的,所以不必重新构建。最终我们还要实现多种类型的敌机,现在只先实现一种类型的敌机,称之为EnemyNormal吧,代码如下:
#ifndef _ENEMY_NORMAL_H_ #define _ENEMY_NORMAL_H_ #include "EnemyBase.h" class EnemyNormal : public EnemyBase { public: EnemyNormal(); ~EnemyNormal(); //CREATE_FUNC(EnemyNormal); //virtual bool init(); static EnemyNormal* create(Sprite* sprite); bool init(Sprite* sprite); }; #endif #include "EnemyNormal.h" EnemyNormal::EnemyNormal() { m_speed = SPEED_ENEMY_NORMAL_DEFAULT; } EnemyNormal::~EnemyNormal() {} bool EnemyNormal::init(Sprite* sprite) { bool ret = false; bindSprite(sprite); ret = true; return ret; } EnemyNormal* EnemyNormal::create(Sprite* sprite) { EnemyNormal* enemy = new EnemyNormal(); if(enemy && enemy->init(sprite)) { enemy->autorelease(); } else { CC_SAFE_DELETE(enemy); } return enemy; }这个没什么好说的,到时候需要什么其他属性,直接在这个类添加即可。
忘了EnemyBase这个基类了,把敌机的共同属性都放到这里,以后不同敌机直接继承他便可。
#ifndef _ENEMY_BASE_H_ #define _ENEMY_BASE_H_ #include "Entity.h" #define SPEED_DEFAULT 10 #define SPEED_NORMAL 5 #define SPEED_ENEMY_NORMAL_DEFAULT 10 class EnemyBase:public Entity { public: EnemyBase(); ~EnemyBase(); bool getEnemyOutScreen(); void setEnemyOutScreen(bool out); bool getEnemyUsed(); void setEnemyUsed(bool used); int getEnemySpeed(); void setEnemySpeed(int speed); //virtual void bombDead(); private: bool m_isOutScreen; bool m_isUsed; protected: int m_speed; }; #endif #include "EnemyBase.h" EnemyBase::EnemyBase() {} EnemyBase::~EnemyBase() {} bool EnemyBase::getEnemyOutScreen() { return m_isOutScreen; } void EnemyBase::setEnemyOutScreen(bool out) { m_isOutScreen = out; } bool EnemyBase::getEnemyUsed() { return m_isUsed; } void EnemyBase::setEnemyUsed(bool use) { m_isUsed = use; } int EnemyBase::getEnemySpeed() { return m_speed; } void EnemyBase::setEnemySpeed(int speed) { m_speed = speed; }接着,我们敌机造出来了,那么怎么源源不断的生成飞机呢,我们先把敌机生成,然后在PlaneGameScene不断生成。所以,现在先生成敌机吧,简称敌机管理器:
#ifndef _ENEMY_MANAGER_H_ #define _ENEMY_MANAGER_H_ #include "cocos2d.h" USING_NS_CC; #define ENEMY_NORMAL_NUM 20 class EnemyBase; class PlaneHero; class EnemyManager : public Node { public: EnemyManager(); ~EnemyManager(); //获取未用的子弹 EnemyBase* getUnsedEnemy(); virtual bool init(); static EnemyManager* create(); //获取敌机列表 Vector<EnemyBase*> getEnemyList(); //注入英雄飞机 void setHeroPlane(PlaneHero* hero); PlaneHero* getHeroPlane(); private: Vector<EnemyBase*> m_enemyList;//敌机列表 void createEnemy(); void EnemyLogicCheck(float dt); void receiveMessge(Ref* pSender); private: PlaneHero* hero_plane;//持有英雄飞机 bool isGameOver; }; #endif #include "EnemyManager.h" #include "EnemyNormal.h" #include "PlaneHero.h" #include "BombManager.h" EnemyManager::EnemyManager() { hero_plane = NULL; isGameOver = false; } EnemyManager::~EnemyManager() {} EnemyBase* EnemyManager::getUnsedEnemy() { for(auto enemy : m_enemyList) { if(enemy->getEnemyUsed() == false) { enemy->setEnemyUsed(true); enemy->setEnemyOutScreen(false); return enemy; } } log("no bullet unused"); return NULL; } bool EnemyManager::init() { //创建敌机列表 createEnemy(); //循环检测子弹列表 this->schedule(schedule_selector(EnemyManager::EnemyLogicCheck),0.01f); NotificationCenter::getInstance()->addObserver(this,callfuncO_selector(EnemyManager::receiveMessge),"gameover",NULL); return true; } EnemyManager* EnemyManager::create() { EnemyManager* eMgr = new EnemyManager(); if(eMgr && eMgr->init()) { eMgr->autorelease(); } else { CC_SAFE_DELETE(eMgr); } return eMgr; } void EnemyManager::createEnemy() { EnemyBase* enemy = NULL; for(int i = 0; i < ENEMY_NORMAL_NUM; i++) { auto enemy_sprite = Sprite::create("enemy1.png"); enemy = EnemyNormal::create(enemy_sprite); enemy->setEnemyUsed(false); enemy->setEnemyOutScreen(false); enemy->setAnchorPoint(Point(0.2,0)); m_enemyList.pushBack(enemy); this->addChild(enemy); } log("m_enemyList size() = %d",m_enemyList.size()); } void EnemyManager::EnemyLogicCheck(float dt) { if(isGameOver == true) { return; } //auto visibleSize = Director::getInstance()->getVisibleSize(); for(auto enemy : m_enemyList) { if(enemy->getEnemyUsed() == true) { //敌机运行 Point pos = enemy->getPosition(); pos.y -= SPEED_DEFAULT; enemy->setPositionY(pos.y); //out of screen if(pos.y <= 0) { enemy->setEnemyOutScreen(true); enemy->setEnemyUsed(false); } //敌机运行 Point pos_enemy = enemy->getPosition(); //英雄位置 Point pos_hero = hero_plane->getPosition(); Rect enemyRect = enemy->getBoundingBox(); Rect heroRect = hero_plane->getBoundingBox(); enemyRect.size.width *= 0.8; enemyRect.size.height *= 0.8; heroRect.size.width *= 0.5; heroRect.size.height *= 0.5; //if(enemyRect.containsPoint(pos_hero) || heroRect.containsPoint(pos_enemy)) if(enemyRect.intersectsRect(heroRect)) { auto enemy_size = enemy->getContentSize(); auto hero_size = hero_plane->getContentSize(); //用于测试位置 log("enemyRect x = %f,y = %f,width = %f,height = %f",enemyRect.origin.x,enemyRect.origin.y,enemyRect.size.width,enemyRect.size.height); log("heroRect x = %f,heroRect.origin.x,heroRect.origin.y,heroRect.size.width,heroRect.size.height); log("enemy_size width = %f,enemy_size.width,enemy_size.height); log("hero_size width = %f,hero_size.width,hero_size.height); log("pos_enemy x = %f,y = %f",pos_enemy.x,pos_enemy.y); log("pos_hero x = %f,pos_hero.x,pos_hero.y); BombManager::getInstance()->bombHeroPlane(pos_hero); NotificationCenter::getInstance()->postNotification("gameover",NULL); //hero_plane->removeFromParentAndCleanup(true); //enemy->setEnemyUsed(false); //enemy->setPositionY(-105); break; } } } } Vector<EnemyBase*> EnemyManager::getEnemyList() { return m_enemyList; } void EnemyManager::setHeroPlane(PlaneHero* hero) { hero_plane = hero; } PlaneHero* EnemyManager::getHeroPlane() { return hero_plane; } void EnemyManager::receiveMessge(Ref* pSender) { isGameOver = true; log("EnemyManager::receiveMessge()"); }
最后,源源不断的生成敌机和生成子弹是类似的,在PlaneGameScene这里生成:
void PlayGameScene::makeBulletAndEnemy(float dt) { if(isGameOver == true) { return; } //Size visibleSize = Director::getInstance()->getVisibleSize(); makeBullet(); makeEnemy(); } //生成子弹 void PlayGameScene::makeBullet() { auto hero_plane = this->getChildByTag(HERO_PLANE); Point pos = hero_plane->getPosition(); BulletBase* bullet = bMgr->getUnusedBullet(); //BulletBase* bullet = NULL; if(bullet == NULL) { return; } bullet->setPosition(pos); } //生成敌机 void PlayGameScene::makeEnemy() { Size visibleSize = Director::getInstance()->getVisibleSize(); int posX = rand() % (int)(visibleSize.width); EnemyBase* enemy = eMgr->getUnsedEnemy(); if(enemy == NULL) { return; } enemy->setPosition(Point(posX,(int)(visibleSize.height))); }
2.碰撞检测
碰撞检测还是不那么精确,只能通过一步一步慢慢调,上面代码加了log大部分就是为了看那个位置。至今仍然未找到比较好的方法来检测碰撞。
首先是子弹与敌机的碰撞检测,在子弹管理器进行处理:
//检测子弹 void BulletManager::bulletLogicCheck(float dt) { if(isGameOver == true) { return; } auto visibleSize = Director::getInstance()->getVisibleSize(); for(auto bullet : m_bulletList) { if(bullet->isUsed() == true) { //子弹运行 Point pos_bullet = bullet->getPosition(); pos_bullet.y += SPEED_DEFAULT; bullet->setPositionY(pos_bullet.y); //碰撞处理 for(auto enemy : eMgr->getEnemyList()) { if(enemy->getEnemyUsed() == true) { //敌机运行 Point pos_enemy = enemy->getPosition(); Point pos_bullet = bullet->getPosition(); Rect enemyRect = enemy->getBoundingBox(); Rect bulletRect = bullet->getBoundingBox(); auto enemy_size = enemy->getContentSize(); auto bullet_size = bullet->getContentSize(); if(enemyRect.intersectsRect(bulletRect)) //if(bulletRect.containsPoint(pos_enemy)) { log("enemyRect x = %f,enemyRect.size.height); log("bulletRect x = %f,bulletRect.origin.x,bulletRect.origin.y,bulletRect.size.width,bulletRect.size.height); log("enemy_size width = %f,enemy_size.height); log("bullet_size width = %f,bullet_size.width,bullet_size.height); log("pos_enemy x = %f,pos_enemy.y); log("pos_bullet x = %f,pos_bullet.x,pos_bullet.y); bullet->setUsed(false); bullet->setPositionY(visibleSize.height * 10); BombManager::getInstance()->bombNormalEnemy(pos_enemy); enemy->setEnemyUsed(false); enemy->setPositionY(-105); continue; } } } //out of screen if(pos_bullet.y >= visibleSize.height) { bullet->setIsMoveOutScreen(true); bullet->setUsed(false); } } } }其次,是敌机与英雄飞机的碰撞检测,在敌机管理器里面处理,其实和子弹管理器差不多,敌机管理器需要持有英雄飞机的引用,才能实现碰撞检测。通过注入英雄飞机即可实现。
//碰撞检测 void EnemyManager::EnemyLogicCheck(float dt) { if(isGameOver == true) { return; } //auto visibleSize = Director::getInstance()->getVisibleSize(); for(auto enemy : m_enemyList) { if(enemy->getEnemyUsed() == true) { //敌机运行 Point pos = enemy->getPosition(); pos.y -= SPEED_DEFAULT; enemy->setPositionY(pos.y); //out of screen if(pos.y <= 0) { enemy->setEnemyOutScreen(true); enemy->setEnemyUsed(false); } //敌机运行 Point pos_enemy = enemy->getPosition(); //英雄位置 Point pos_hero = hero_plane->getPosition(); Rect enemyRect = enemy->getBoundingBox(); Rect heroRect = hero_plane->getBoundingBox(); enemyRect.size.width *= 0.8; enemyRect.size.height *= 0.8; heroRect.size.width *= 0.5; heroRect.size.height *= 0.5; //是否碰撞 if(enemyRect.intersectsRect(heroRect)) { auto enemy_size = enemy->getContentSize(); auto hero_size = hero_plane->getContentSize(); //用于测试位置 log("enemyRect x = %f,pos_hero.y); //爆炸效果 BombManager::getInstance()->bombHeroPlane(pos_hero); //发出游戏结束的通知 NotificationCenter::getInstance()->postNotification("gameover",NULL); break; } } } }
3.爆炸效果管理器
发现爆炸效果的代码还是挺多的,以后不同类型的敌机效果还要处理,所以就单独提取出来,放在一个类,称之为爆炸效果管理器。我们需要那种类型的爆炸效果,直接通过这个单例类获取就可以了。很是方便,易于管理。
#ifndef _BOMB_MANAGER_H_ #define _BOMB_MANAGER_H_ #include "cocos2d.h" class BombManager : public cocos2d::Node { public: BombManager(); ~BombManager(); //单例模式,获取实例 static BombManager* getInstance(); //普通飞机的爆炸效果 void bombNormalEnemy(cocos2d::Point pos); //主飞机的爆炸效果 void bombHeroPlane(cocos2d::Point pos); //初始化各个爆炸的animation void inite(); //爆炸后的回调,清除爆炸的痕迹,通用 void bombDone(Node* sender); private: static BombManager* _instance;//单例 }; #endif #include "BombManager.h" USING_NS_CC; BombManager* BombManager::_instance = NULL; BombManager::BombManager() {} BombManager::~BombManager() {} void BombManager::inite() { //添加普通敌机爆炸效果 auto spriteFrameCache = SpriteFrameCache::getInstance(); Vector<SpriteFrame*> framelist; for(int i = 1; i <=4; i++) { SpriteFrame* sf = SpriteFrame::create(String::createWithFormat("enemy1_down%d.png",i)->_string,Rect(0,57,51)); framelist.pushBack(sf); spriteFrameCache->addSpriteFrame(sf,String::createWithFormat("enemy1_down%d.png",i)->_string); } Animation* normal_enemy_animation = Animation::createWithSpriteFrames(framelist,0.1f); AnimationCache::getInstance()->addAnimation(normal_enemy_animation,"normal_enemy_bomb"); //添加英雄飞机爆炸效果 framelist.clear(); for(int i = 1; i <=4; i++) { SpriteFrame* sf = SpriteFrame::create(String::createWithFormat("hero_blowup_n%d.png",102,126)); framelist.pushBack(sf); spriteFrameCache->addSpriteFrame(sf,String::createWithFormat("hero_blowup_n%d.png",i)->_string); } Animation* hero_plane_animation = Animation::createWithSpriteFrames(framelist,0.3f); AnimationCache::getInstance()->addAnimation(hero_plane_animation,"hero_plane_bomb"); //add other bomb } //普通敌机爆炸效果 void BombManager::bombNormalEnemy(Point pos) { auto animate = Animate::create(AnimationCache::getInstance()->getAnimation("normal_enemy_bomb")); auto repeat = Repeat::create(animate,1); auto sprite = Sprite::create(); sprite->setPosition(pos); this->addChild(sprite); //回调函数 auto callback = CallFunc::create(CC_CALLBACK_0(BombManager::bombDone,this,sprite)); auto sequence = Sequence::create(repeat,callback,NULL); sprite->runAction(sequence); } //英雄飞机爆炸效果 void BombManager::bombHeroPlane(cocos2d::Point pos) { auto animate = Animate::create(AnimationCache::getInstance()->getAnimation("hero_plane_bomb")); auto repeat = Repeat::create(animate,NULL); sprite->runAction(sequence); } BombManager* BombManager::getInstance() { if(_instance == NULL) { _instance = new BombManager(); if(_instance) { //初始化 _instance->inite(); } } return _instance; } //爆炸后清除自己的痕迹 void BombManager::bombDone(Node* sender) { sender->removeFromParentAndCleanup(true); }4.修改游戏界面
以上实现后,最后还是要把上面的东西加入游戏界面,那么需要把上面的节点加入到PlaneGameScene中。在这个类的init函数里,加入以下代码:
bMgr = BulletManager::create(); //由于忘记添加进层里,导致bMgr->getUnusedBullet()一直出错 this->addChild(bMgr,1); //生成敌机管理器 eMgr = EnemyManager::create(); this->addChild(eMgr,1); //注入敌机管理器 bMgr->setEnemyManager(eMgr); //注入爆炸管理器 this->addChild(BombManager::getInstance(),1); //add plane hero Sprite* hero = Sprite::create("hero1.png"); PlaneHero* hero_plane = PlaneHero::create(hero); hero_plane->setPosition(Point(visibleSize.width / 2,visibleSize.height / 2)); hero_plane->setTag(HERO_PLANE); hero_plane->setAnchorPoint(Point(0,0)); this->addChild(hero_plane,1); //注入英雄飞机给敌机管理器,做检测碰撞使用 eMgr->setHeroPlane(hero_plane);拖动飞机的时候,点击感觉也不是很精确,换种方式实现touchevent,感觉精确度提高了不少。
// 添加监听器,监听主飞机 _eventDispatcher->addEventListenerWithSceneGraPHPriority(touchListener,hero_plane); //_eventDispatcher->addEventListenerWithSceneGraPHPriority(touchListener,enemy_plane); //Touch event handler bool PlayGameScene::onTouchBegan(Touch *touch,Event* event) { if(isGameOver == true) { return true; } // 获取事件所绑定的 target auto target = static_cast<Layer*>(event->getCurrentTarget()); if(target == NULL) { log("target = NULL"); return true; } bTouchPlane = false; // 获取当前点击点所在相对按钮的位置坐标 // getLocation得到的是openGL坐标系,也就是世界坐标系 Point touchPoint = touch->getLocation(); Point locationInNode = target->convertToNodeSpace(touchPoint); Size target_size = target->getContentSize(); Rect target_rect = Rect(0,target_size.width,target_size.height); //auto hero_plane = this->getChildByTag(HERO_PLANE); //Point hero_pos = hero_plane->getPosition(); //Size hero_size = hero_plane->getContentSize(); //log("hero_pos x = %f,hero_pos.x,hero_pos.y); log("touchPoint x = %f,touchPoint.x,touchPoint.y); log("locationInNode x = %f,locationInNode.x,locationInNode.y); log("target_size width = %f,target_size.height); log("target_rect x = %f,target_rect.origin.x,target_rect.origin.y,target_rect.size.width,target_rect.size.height); //log("hero_size width = %f,hero_size.height); //if(touchPoint.x >= hero_pos.x && touchPoint.x <= (hero_pos.x + hero_size.width) && touchPoint.y >= hero_pos.y && touchPoint.y <= (touchPoint.y + hero_size.height)) if(target_rect.containsPoint(locationInNode)) { //touch the plane,mark this flag for use bTouchPlane = true; } return true; }
不明觉厉,这个方法还有待研究一下。
好了,这篇到此结束,第三篇后续会更新上来,纯属想到哪写到哪。
效果图如下:
原文链接:https://www.f2er.com/cocos2dx/344135.html