cocos2d-x小遊戲——飛機大戰
上週,我做了一個基於 cocos2d-x 的飛機大戰的遊戲,因為我剛學cocos2d-x沒多久,所以這個飛機大戰很多都是看著別人的教程,再加上自己的一些想法,來做的。
下面我想說一說我的思路。
飛機大戰有三個場景:
- LayerGameStart(遊戲開始)
- LayerGameMain(遊戲進行中)
- LayerGameOver(遊戲結束)
一、遊戲開始場景(LayerGameStart)
其中,遊戲開始和遊戲結束是比較簡單的,那我就先從簡單的說起,
首先說下游戲開始場景。我們在這個場景裡面只需要做一下工作:
- 預載入一些資源(聲音,圖片快取,這些資源都是全域性的)
- 注意要將同一類資源放在一起,便於管理
- 檢測遊戲遊戲的本地存數資料中是否有遊戲最高分(UserDefault)
在這個場景,我只想說下如果檢測遊戲的最高分
具體實現如下:
//判斷分數是否已經被儲存
bool LayerGameStart::isSaveFile()
{
//用一個bool值作為標誌,如果有則表示分數已經被儲存
if (!UserDefault::getInstance()->getBoolForKey("isSaveFileXml"))
{
//如果沒有就設定標誌並置為真
UserDefault::getInstance()->setBoolForKey("isSaveFileXml" ,true);
//設定最高分,預設值為0
UserDefault::getInstance()->setIntegerForKey("HightestScore",0);
////flush的作用是將資料寫入到xml檔案中。flush()在windows下是空的。。。呵呵。。。
UserDefault::getInstance()->flush();
return false;
}
else
return true;
}
void LayerGameStart::getHightestScore ()
{
if (isSaveFile())
{
//在這裡設定歷史最高得分
LayerGameOver::_hightestScore =
UserDefault::getInstance()->getIntegerForKey("HightestScore",0);
}
}
然後只需要將 getHightestScore( ) 函式放在 LayerGameStart 的 init( ) 函式中即可。
二、遊戲結束場景(LayerGameOver)
遊戲結束場景也很容易,主要實現下面的功能:
- 顯示本局遊戲的得分
- 顯示歷史最高分
- 設定“返回遊戲”按鈕,和“退出”按鈕
其中顯示本局所得分數,也是比較好實現的,需要注意的是,如何將主場景(LayerGameMain)中的分數傳遞到遊戲結束場景中去。做法如下:
static LayerGameOver * create(int score);
static cocos2d::Scene * scene(int score);
bool init(int score);
在建立場景時將分數傳入,即在 init( ) 函式中傳入分數,因為 init 在 create 有被呼叫,而 create 函式又在 scene 函式中被呼叫,所以這三個函式都有引數了。在切換場景的時候直接將分數傳遞過來就行。
然後就是顯示歷史最高分,顯示歷史最高分需要在遊戲結束場景的 class 中新增一個靜態成員變數
static int _hightestScore;//用於存到本地,記得要在class外進行初始化!
cocos2d::Label * hightestScore;//用於顯示
具體實現如下:
//顯示歷史最高分
Value strHightestScore(_hightestScore);
hightestScore = Label::createWithBMFont("font/font.fnt",strHightestScore.asString());
hightestScore->setColor(Color3B(30,50,240));
hightestScore->setAnchorPoint(Point::ANCHOR_MIDDLE_LEFT);
hightestScore->setPosition(Point(150,winSize.height-40));
this->addChild(hightestScore);
返回和退出按鈕就很簡單了,就是設定兩個圖片作為按鈕 (MenuItemSprite )backItem 和 exitItem 然後將這兩個精靈新增到 Menu 中去:Menu * menu = Menu::create(backItem, exitItem, nullptr); 這兩個按鈕點選的回撥函式也很簡單,返回就是切換到遊戲開始場景,退出就是直接退出程式 exit(1);
三、遊戲主場景
遊戲主場景就是最重要也最難的,主要有一下功能:
新增遊戲背景並讓遊戲背景滾動起來
新增玩家飛機
1.飛機的動作行為(閃三次後播放自身幀動畫,要方便控制,擴大其BoundingBox)
2.飛機不能飛出螢幕
3.由於其他層也需要飛機的位置,為了方便其他層獲得飛機,將其設計為單例getInstance( )
4.玩家飛機自身爆炸的動畫(因為爆炸後要切換到遊戲結束場景,所以要在這裡將本局遊戲得分傳到結束場景)新增子彈
1.首先得拿到玩家飛機的位置(因為子彈是從玩家飛機發出的)
2.設定定時器來產生子彈
3.讓子彈飛~
4.子彈的行為:與敵機碰撞或者什麼也沒碰撞到——飛出螢幕外
5.將子彈放在一個容器裡面,便於碰撞檢測
6.不只有單發子彈還有多發子彈(MultiBullets)新增敵機
1.首先有一個敵機類Enemy,用於產生所有敵機(小敵機,中敵機,打敵機)
2.敵機產生的位置(螢幕上方,隨機產生)
3.敵機消失在螢幕下方
4.與子彈和玩家飛機發生碰撞
5.敵機爆炸動畫,玩家得分。新增道具
1.在玩遊戲時,會用道具出現(從螢幕上方,隨機出現)
2.道具有兩種:炸彈和雙發子彈(bigBoom,multiBullets)
3.道具行為:與玩家飛機碰撞或者什麼也沒碰到——掉到螢幕外
4.炸彈可以讓當前螢幕中所有敵機爆
5.雙發子彈增加玩家飛機的威力(第一次吃到會變成雙發子彈,以後再吃到不會再加子彈而是直接給玩家加100分)新增控制層
1.更新玩家得分
2.實現遊戲的暫停和繼續的功能(新增遮蔽層)
怎麼樣,頭暈了嗎?要加這麼多層,實現這麼多功能。。。還有一些我沒寫(一時想不起來)
我也不準備,每個都詳細說明了,我就說說實現這些功能需要注意的地方吧,也是我覺得比較難的地方。。。
1. 如何實現螢幕滾動:
我在這裡其實只用了一個背景圖,但是把它載入了兩次,然後讓兩個圖片一起向下移動,具體過程請看下圖
程式碼實現如下:
//新增背景
void LayerGameMain::addBackground()
{
SimpleAudioEngine::getInstance()->playBackgroundMusic("sound/game_music.wav",true);
auto bg1 = Sprite::createWithSpriteFrameName("background.png");
bg1->setTag(BG1);
bg1->setAnchorPoint(Point::ZERO);
bg1->setPosition(Point(0,0));
this->addChild(bg1);
auto bg2 = Sprite::createWithSpriteFrameName("background.png");
bg2->setTag(BG2);
bg2->setAnchorPoint(Point::ZERO);
bg2->setPosition(0,bg1->getContentSize().height - 5);//為了不留空隙
this->addChild(bg2);
//利用幀迴圈來實現背景滾動
this->schedule(schedule_selector(LayerGameMain::movingBackgroundCallback),0.01f);
}
//使得背景滾動起來
void LayerGameMain::movingBackgroundCallback(float dt)
{
Sprite * bg1 = (Sprite *)this->getChildByTag(BG1);
Sprite * bg2 = (Sprite *)this->getChildByTag(BG2);
bg1->setPositionY(bg1->getPositionY() - 2);//每個迴圈下移2個畫素
bg2->setPositionY(bg1->getPositionY() + bg2->getContentSize().height - 2);
if (bg2->getPositionY() < 0)
{
bg1->setPositionY(0);//重置背景
}
}
2. 如何將玩家飛機設計為單例?
cocos2d中很多類都有獲得單例的函式getInstance( ),這裡我也寫了這麼一個函式來得到飛機單例
class MyPlane : public cocos2d::Sprite
{
/*省略部分程式碼*/
//將飛機設計成全域性的
static MyPlane * getInstance();
static MyPlane * _splane;
}
//初始化
MyPlane * MyPlane::_splane = nullptr;
MyPlane * MyPlane::getInstance()
{
if (!_splane)
{
_splane = new MyPlane();
if (_splane && _splane->init())
{
//不將其掛到渲染樹上,讓飛機的生命週期跟場景一樣
//_splane->autorelease();
}
}
return _splane;//return 在if語句外面
}
還有就是玩家飛機爆炸的函式,需要傳入飛機爆炸之前得到的分數。好讓遊戲結束場景能得到分數。
3. 子彈層的設計
因為玩家的飛機是不斷移動的,然後子彈的動作都是 MoveTo ,其 create 函式只有時間變數,我們如何使得子彈的速度是一樣的呢?很簡單,根據 v = s / t;若想要速度一樣則在距離不同的情況下,就必須改變子彈執行的時間,所以只要給不同位置發出的子彈不同的時間,就可以使得子彈的速度一樣。具體實現如下:
//得到子彈到螢幕上邊沿的距離
float distance =
winSize.height - plane->getPositionY() - plane->getBoundingBox().size.height/2;
//確定子彈的速度 一秒跨越800個畫素。
float velocity = 800/1;
//根據距離和速率求得時間
float movedt = distance / velocity;
//子彈在movedt的時間內移動到螢幕上邊沿之外的地方(加上的 bullet->getContentSize().height 就是超出螢幕的距離)
MoveTo * to = MoveTo::create(movedt,
Point(birthPlace.x,winSize.height + bullet->getContentSize().height));
4. 敵機類的設計
這個不說了直接看程式碼,程式碼都有註釋的
Enemy.h:
#ifndef __Enemy_H_
#define __Enemy_H_
#include "cocos2d.h"
class Enemy : public cocos2d::Node
{
public:
//構造器
Enemy();
//析構器
~Enemy();
//建立敵機
static Enemy * create();
//將敵機與其對應的Sprite(圖片)和生命值繫結(有三類敵機)
void bindEnemySprite(cocos2d::Sprite * spr,int life);
//得到敵機
cocos2d::Sprite * getSprite();
//得到生命值
int getLife();
//失去生命值
void loseLife();
//得到敵機在世界座標內的的位置和尺寸大小boundingbox
cocos2d::Rect Get_BoundingBox();
private:
cocos2d::Sprite * _sprite;
int _life;
};
#endif
Enemy.cpp
#include "Enemy.h"
USING_NS_CC;
Enemy::Enemy()
{
//在建構函式中初始化,其實也可以在init函式中初始化,但這裡沒有init函式
_sprite = nullptr;
_life = 0;
}
Enemy::~Enemy()
{
}
Enemy * Enemy::create()
{
Enemy * pRect = new Enemy();
if (pRect != nullptr)
{
pRect->autorelease();
return pRect;
}
else
return nullptr;
}
//繫結敵機,不同的敵機有不同的圖片,不同的生命值
void Enemy::bindEnemySprite(cocos2d::Sprite * spr,int life)
{
_sprite = spr;
_life = life;
//將_sprite加到 pRect 上!!pRect 實質就是一個Node
this->addChild(_sprite);
}
Sprite * Enemy::getSprite()
{
return _sprite;
}
int Enemy::getLife()
{
return _life;
}
void Enemy::loseLife()
{
_life--;
}
//自定義的getBoundingBox函式,便於主場景中的碰撞檢測
Rect Enemy::Get_BoundingBox()
{
Rect rect = _sprite->getBoundingBox();
//本來敵機是加到pRect上的它的座標是相對於pRect的
//這裡將敵機的座標轉換為世界座標
Point position = this->convertToWorldSpace(rect.origin);
//這裡只需要知道敵機的起始座標,因為敵機的寬度和長度是不會改變的
Rect enemyRect = Rect(position.x, position.y, rect.size.width, rect.size.height);
return enemyRect;
}
5.有了敵機類,就要將敵機新增到主場景中去(LayerEnemy)
因為要加3類敵機,其實每一類敵機的新增方法都一樣,只不過他們的圖片,生命值,出場概率,被擊毀後玩家所得的分數不相同罷了。在這裡就將新增小敵機的方法說一下,中敵機和大敵機都一樣。
//小敵機更新函式(在定時器裡面呼叫)
void addSmallEnemyCallback(float dt);
//小敵機移動完成後(沒有碰撞)
void smallEnemyMoveFinished(cocos2d::Node * node);
//小敵機爆炸
void smallEnemyBlowup(Enemy * smallEnemy);
//移除小敵機
void removeSmallEnemy(cocos2d::Node * target, void * data);
//移除所有小敵機
void removeAllSmallEnemy();
//容器,用來存放所有小敵機,便於碰撞檢測
cocos2d::Vector<Enemy *> _smallVec;
實現函式:
//新增敵機的回撥函式(在幀迴圈裡面呼叫)
void LayerEnemy::addSmallEnemyCallback(float dt)
{
Enemy * smallEnemy = Enemy::create();
//繫結
smallEnemy->bindEnemySprite(Sprite::createWithSpriteFrameName("enemy1.png"),SMALL_MAXLIFE);
//加到smallVec中
_smallVec.pushBack(smallEnemy);
//確定敵機的座標:橫座標x是一個隨機值
//smallEnemy->Get_BoundingBox().size.width/2 < x < winSize.width - smallEnemy->Get_BoundingBox().size.width/2
//注意:這裡要使用 Enemy 類裡面的Get_BoundingBox() 函式!
float x = CCRANDOM_0_1()*(winSize.width - 2*smallEnemy->Get_BoundingBox().size.width) +
smallEnemy->Get_BoundingBox().size.width/2;
float y = winSize.height + smallEnemy->Get_BoundingBox().size.height/2;
Point smallBirth = Point(x,y);
//設定座標
smallEnemy->setPosition(smallBirth);
this->addChild(smallEnemy);
MoveTo * to = MoveTo::create(3,Point(smallBirth.x,smallBirth.y -
winSize.height - smallEnemy->Get_BoundingBox().size.height));
CallFuncN * actionDone = CallFuncN::create(this,
callfuncN_selector(LayerEnemy::smallEnemyMoveFinished));
Sequence * sequence = Sequence::create(to,actionDone,NULL);
smallEnemy->runAction(sequence);
}
//敵機爆炸的函式
void LayerEnemy::smallEnemyBlowup(Enemy * smallEnemy)
{
SimpleAudioEngine::getInstance()->playEffect("sound/enemy1_down.wav");
Animate * smallAnimate =
Animate::create(AnimationCache::getInstance()->animationByName("smallBlowup"));
/*利用 CallFuncN 來完成 CallFuncND 的功能 !!
注意這裡(我花了很長時間才解決請看http://blog.csdn.net/crayondeng/article/details/18767407)*/
auto actionDone =
CallFuncN::create(CC_CALLBACK_1(LayerEnemy::removeSmallEnemy,this,smallEnemy));
Sequence * sequence = Sequence::create(smallAnimate,actionDone,NULL);
smallEnemy->getSprite()->runAction(sequence);//這麼寫可以嗎? smallEnemy->runAction(sequence)不行!
}
//這是沒有碰撞的remove
void LayerEnemy::smallEnemyMoveFinished(cocos2d::Node * node)
{
Enemy * smallEnemy = (Enemy *)node;
this->removeChild(smallEnemy,true);
_smallVec.eraseObject(smallEnemy);
//node->removeAllChildrenWithCleanup(true);
}
//這是碰撞之後的remove
void LayerEnemy::removeSmallEnemy(cocos2d::Node * target,void * data)
{
Enemy * smallEnemy = (Enemy *)data;
if (smallEnemy)
{
_smallVec.eraseObject(smallEnemy);
smallEnemy->removeFromParentAndCleanup(true);//和這句等效:this->removeChild(smallEnemy,true);
}
}
//去掉所有小敵機
void LayerEnemy::removeAllSmallEnemy()
{
for (auto node : _smallVec)
{
Enemy * enemy = (Enemy *)node;
if (enemy->getLife() > 0)
{
this->smallEnemyBlowup(enemy);
}
}
}
6. 然後就新增道具層
道具有兩種,一個是大炸彈,一個是雙發子彈。它們產生的位置都是在螢幕上方,隨機產生。
這裡主要說一說炸彈,因為炸彈是可以點選的,一點選後,當前螢幕的所有敵機都會爆炸。炸彈的數量減一。所以炸彈需要在主場景的幀迴圈中不斷檢測,用一個容器來存放炸彈,玩家飛機一吃到炸彈道具,就更新炸彈數
void LayerGameMain::updateBigBoomCount(int bigBoomCount)
{
String strBoomCount;//用來顯示炸彈的數量
Sprite * norBoom = Sprite::createWithSpriteFrameName("bomb.png");//正常的圖片
Sprite * selBoom = Sprite::createWithSpriteFrameName("bomb.png");//選擇的圖片
if (bigBoomCount < 0)//如果小於0
{
return;//則什麼也不做
}
else if (bigBoomCount == 0)//如果炸彈數等於0
{
if (this->getChildByTag(TAG_BIGBOOM))//在主場景裡檢查是否有炸彈圖示
{
this->removeChildByTag(TAG_BIGBOOM,true);//如果有,就將其刪除
}
if (this->getChildByTag(TAG_BIGBOOMCOUNT))//在主場景裡面檢查是否有炸彈數字標籤
{
this->removeChildByTag(TAG_BIGBOOMCOUNT,true);//如果有,則刪除
}
}
else if (bigBoomCount == 1)//如果炸彈數等於1
{
if ( !(this->getChildByTag(TAG_BIGBOOM)) )//檢查是否有炸彈圖示
{
//如果沒有,就新增一個炸彈圖示(其實是一個選單項)
MenuItemSprite * boomItem = MenuItemSprite::create(norBoom,
selBoom,
CC_CALLBACK_1(LayerGameMain::boomMenuCallback,this));
boomItem->setPosition(norBoom->getContentSize().width/2,
norBoom->getContentSize().height/2);
Menu * boomMenu = Menu::create(boomItem,nullptr);
boomMenu->setPosition(Point::ZERO);
this->addChild(boomMenu,0,TAG_BIGBOOM);
}
if ( !(this->getChildByTag(TAG_BIGBOOMCOUNT)) )//檢查是否有炸彈數字標籤
{
//如果沒有,就新增一個炸彈數字標籤
strBoomCount.initWithFormat("X %d",bigBoomCount);
LabelBMFont * labelBoomCount =
LabelBMFont::create(strBoomCount.getCString(),"font/font.fnt");
labelBoomCount->setAnchorPoint(Point::ANCHOR_MIDDLE_LEFT);
labelBoomCount->setPosition(Point(norBoom->getContentSize().width,
norBoom->getContentSize().height - 30));
this->addChild(labelBoomCount,0,TAG_BIGBOOMCOUNT);
}
}
else if (bigBoomCount > 1 )//如果炸彈數大於1
{
//則只更新炸彈數目
strBoomCount.initWithFormat("X %d",bigBoomCount);
LabelBMFont * labelCount =
(LabelBMFont *)this->getChildByTag(TAG_BIGBOOMCOUNT);
labelCount->setString(strBoomCount.getCString());//設定炸彈數目
}
}
7. 最後來新增控制層
控制層主要是兩個作用:1,暫停和繼續遊戲(新增遮蔽層)2,更新玩家得分
暫停和繼續遊戲需要兩個按鈕來控制。剛開始遊戲時,遊戲是進行著的,沒有暫停。當玩家按了暫停按鈕後,遊戲暫停,按鈕變成繼續狀態(三角形)這個還是比較簡單的。下面來看實現程式碼:
LayerControl.h
#ifndef __LayerControl_H_
#define __LayerControl_H_
#include "cocos2d.h"
#include "LayerNoTouch.h"
class LayerControl : public cocos2d::Layer
{
public:
CREATE_FUNC(LayerControl);
bool init();
void menuCallback(cocos2d::Ref * ref);
void updateScore(int score);
private:
cocos2d::MenuItemSprite * pauseMenuItem;
cocos2d::LabelBMFont * scoreItem;
LayerNoTouch * _noTouchLayer;
};
#endif
LayerControl.cpp
#include "LayerControl.h"
#include "AppMacros.h"
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
USING_NS_CC;
bool LayerControl::init()
{
if (!Layer::init())
{
return false;
}
_noTouchLayer = nullptr;//初始化
//暫停按鈕不同狀態下的兩個圖片
Sprite * nor = Sprite::createWithSpriteFrameName("game_pause_nor.png");
Sprite * press = Sprite::createWithSpriteFrameName("game_pause_pressed.png");
pauseMenuItem =
MenuItemSprite::create(nor,press,CC_CALLBACK_1(LayerControl::menuCallback,this));
Point menuBrith = Point(pauseMenuItem->getContentSize().width/2 + 10,
winSize.height - pauseMenuItem->getContentSize().height);
pauseMenuItem->setPosition(menuBrith);
Menu * pauseMenu = Menu::create(pauseMenuItem,nullptr);
pauseMenu->setPosition(Point::ZERO);
this->addChild(pauseMenu,101);//將暫停/繼續 按鈕放在最前面
scoreItem = LabelBMFont::create("0","font/font.fnt");
scoreItem->setColor(Color3B(255,255,0));
scoreItem->setAnchorPoint(Point(0,0.5));
scoreItem->setPosition(Point(pauseMenuItem->getPositionX() + nor->getContentSize().width/2 + 5,
pauseMenuItem->getPositionY()));
this->addChild(scoreItem);
return true;
}
//按鈕回撥函式
void LayerControl::menuCallback(cocos2d::Ref * ref)
{
if (!Director::getInstance()->isPaused())//如果點選按鈕之前遊戲沒有暫停
{
if (SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying())
{
//如果背景音樂還在播放,則暫停其播放
SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
}
//則將 暫停/繼續 按鈕設定為繼續狀態的按鈕
pauseMenuItem->setNormalImage(Sprite::createWithSpriteFrameName("game_resume_nor.png"));
pauseMenuItem->setSelectedImage(Sprite::createWithSpriteFrameName("game_resume_pressed.png"));
//並暫停遊戲
Director::getInstance()->pause();
//新增遮蔽層,遮蔽層一定要加到其它所有層前面,暫停/繼續 按鈕的後面!!!
_noTouchLayer = LayerNoTouch::create();
this->addChild(_noTouchLayer);
}
else
{
SimpleAudioEngine::getInstance()->resumeBackgroundMusic();//恢復背景音樂
pauseMenuItem->setNormalImage(Sprite::createWithSpriteFrameName("game_pause_nor.png"));
pauseMenuItem->setSelectedImage(Sprite::createWithSpriteFrameName("game_pause_pressed.png"));
Director::getInstance()->resume();
this->removeChild(_noTouchLayer,true);
}
}
//更新遊戲得分,這個函式也可以放在主場景中
void LayerControl::updateScore(int score)
{
/*2.0版本
String * strScore = String::createWithFormat("%d",score);
scoreItem->setString(strScore->getCString());*/
//3.0版本
Value strScore(score);
scoreItem->setString(strScore.asString());//更新成績轉換為字串
}
8. 最後,我們來看看主場景裡面都做了什麼
bool LayerGameMain::init()
{
if (!Layer::init())
{
return false;
}
_bigBoomCount = 0;
_score = 0;//不要將_score 設定為static ,否則在場景切換時,它不能清零
this->addBackground();
this->addMyPlane();
this->addBulletLayer();//執行了startShoot()
this->addMultiBulletsLayer();//沒有執行startShoot()
this->addEnemyLayer();
this->addFoodLayer();
this->addControlLayer();
this->scheduleUpdate();//開啟定時器便於碰撞檢測
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);//觸控吞噬
listener->onTouchBegan = CC_CALLBACK_2(LayerGameMain::onTouchBegan,this);
listener->onTouchMoved = CC_CALLBACK_2(LayerGameMain::onTouchesMoved,this);
this->_eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this);
return true;
}
這是主場景的初始化函式,大家都應該看到了,依次添加了背景、玩家飛機、子彈層、敵機層、食物層、控制層、最後開啟幀迴圈定時器、設定觸控事件的監聽。
怎麼樣,是不是很簡單,很清晰? 哈哈,當做完之後再會過來看自己寫的程式碼還真是挺好。。
最後的最後
來說一說主場景裡面的 update 函式。這個函式裡面做了整個遊所有的碰撞檢測。其原理都一樣,這裡我拿子彈與小敵機的碰撞檢測為例說明一下:
//單發子彈與小敵機碰撞
/*思路:
兩次遍歷(即兩個for迴圈),第一次遍歷子彈容器(_bulletVector),取出其第一個子彈,
第二次遍歷小敵機容器(_smallVec)將這個取出的子彈與當前螢幕上所有的小敵機做碰撞檢測,
如果檢測到碰撞,再判斷當前碰撞到的小敵機的生命值_life 若等於1,則小敵機失去生命值
再分別將當前的子彈和當前的小敵機加到容器 bulletToDel_Small 和 smallToDel 中去,
當第一個子彈與螢幕上的敵機全部碰撞檢測完以後,就把 bulletToDel_Small 和 smallToDel
裡面的物件全部刪除,這樣可以防止在遍歷時發生錯誤!*/
Vector<Sprite *> bulletToDel_Small;
for (auto bt : _bulletLayer->_bulletVector)
{
Sprite * bullet = bt;
Vector<Enemy *> smallToDel;
for (auto et : _enemyLayer->_smallVec)
{
Enemy * enemy = et;
if (bullet->getBoundingBox().intersectsRect(enemy->Get_BoundingBox()))
{
if (enemy->getLife() == 1)
{
enemy->loseLife();
bulletToDel_Small.pushBack(bullet);
smallToDel.pushBack(enemy);
_score += SMALL_SCORE;//加上小敵機的分數
_controlLayer->updateScore(_score);
}
}
}
for(auto et : smallToDel)//注意for迴圈的位置,要與建立時的語句在同一層
{
Enemy * enemy = et;
_enemyLayer->smallEnemyBlowup(enemy);//敵機爆炸(刪除)
}
}
for (auto bt : bulletToDel_Small)//注意for迴圈的位置,要與建立時的語句在同一層
{
Sprite * bullet = bt;
_bulletLayer->removeBullet(bullet);//刪除子彈
}
好了,就寫到這裡了,寫的很亂,不知道大家能不能看懂。
要是有什麼寫錯了的地方,還望斧正。
最後附上本遊戲的完整程式碼地址: PlaneFight