先说下版本   vs2010+cocos2dx2.2


本章主要是告诉大家如何实现创建背景、飞机、***精灵,并且然后他们动起来,然后做一个碰撞测试,当***和敌方飞机碰撞时就销毁精灵并且加一个爆炸的精灵。



  1. 创建背景、飞机、***精灵

先在GameScene.h中添加以下成员

GameScene();
~GameScene();
void addMonster();
void addBullet1();
void GameLogic(float dt);
void GameLogicaddBullet1(float dt);
void GameLogicaddBullet2(float dt);
void spriteMoveFinished(CCNode *sender);
void keyArrowClicked(int arrow);
void ccTouchesBegan(cocos2d::CCSet *pTouches,cocos2d::CCEvent *pEvent);
void ccTouchesMoved(cocos2d::CCSet *pTouches,cocos2d::CCEvent *pEvent);
cocos2d::CCArray     *_monsters;
    cocos2d::CCArray     *_bullets;
cocos2d::CCSprite    *player;
cocos2d::CCSprite *bgSprite1;
cocos2d::CCSize visibleSize;
cocos2d::CCPoint origin;
int bgHeight;
int monsterSpeed;

在GameScene::init()中添加以下代码

visibleSize = CCDirector::sharedDirector()->getVisibleSize();
    origin = CCDirector::sharedDirector()->getVisibleOrigin();
bgHeight =  (visibleSize.height + origin.y);
    bgSprite1=CCSprite::create("background.png");  
    bgSprite1->setAnchorPoint(ccp(0.5, 0));  
    bgSprite1->setPosition(ccp(visibleSize.width/2 + origin.x,bgHeight));  
      
    this->addChild(bgSprite1, 0);
player = CCSprite::create("player.png");
player->setPosition(ccp(origin.x + visibleSize.width/2,origin.y + player->getContentSize().height/2));
this->addChild(player,1);

这样,很简单我们就创建了一个背景精灵和一个飞机精灵,由于背景和飞机只有一个,所以我们可以考虑把他们设为类的成员


由于***和敌方飞机有很多,所以我们应该把他们单独写为函数,然后在init中设置一个计时器,通过计时器来设置多少秒调用一次函数,在函数中去添加***和敌方飞机精灵


首先先贴上添加***和敌方飞机的函数的代码

void GameScene::addMonster(){
CCSprite* pMonster = CCSprite::create("monster.png");
    int minX = pMonster->getContentSize().width  /2;
    int maxX = visibleSize.width - pMonster->getContentSize().width /2;
    int rangeX = maxX - minX;
    int actualX = (rand() % rangeX) + minX;
pMonster->setPosition(ccp(actualX,origin.y + visibleSize.height - pMonster->getContentSize().height));
this->addChild(pMonster,1);
int time = (origin.y + visibleSize.height - pMonster->getContentSize().height)/monsterSpeed;
    //time s内从原来位置到ccp(actualX,origin.y + pMonster->getContentSize().height/2)位置
    CCMoveTo *actionMove = CCMoveTo::create(time,ccp(actualX,origin.y + pMonster->getContentSize().height/2));
    CCCallFuncN *actionMoveDone = CCCallFuncN::create(this, callfuncN_selector(GameScene::spriteMoveFinished));
    pMonster->runAction(CCSequence::create(actionMove, actionMoveDone, NULL));
void GameScene::addBullet1(){
CCSprite* pBullet = CCSprite::create("bullet1.png");
float x = player->getPositionX();
float y = player->getPositionY();
pBullet->setPosition(ccp(x,y+40));
this->addChild(pBullet,2);
CCMoveTo *actionMove = CCMoveTo::create(1,ccp(x,origin.y + visibleSize.height - pBullet->getContentSize().height));
CCCallFuncN *actionMoveDone = CCCallFuncN::create(this, callfuncN_selector(GameScene::spriteMoveFinished));
    pBullet->runAction(CCSequence::create(actionMove, actionMoveDone, NULL));

第一段代码中首先我们先添加了一个敌方飞机的精灵,注意敌方飞机出现的位置的x值是随机的,所以我们应该把它设为他出现位置的最大x值和最小x值的随机数

然后我们开始设置他的移动,CCMoveTo::create函数中第一个参数是移动的时间,第二个参数是,从原来的点移动到的那个点的值,然后我们设置一个actionMoveDone来设置当敌方飞机移动完了之后就销毁它,销毁的代码我们就设置spriteMoveFinished中

void GameScene::spriteMoveFinished(CCNode *sender)
{
    CCSprite *sprite = (CCSprite*)sender;
    this->removeChild(sprite, true);
}

最后我们用runAction来移动精灵


在我们了解了如何移动敌方飞机了之后,我们移动***的代码就和容易理解了,几乎是一模一样的


我们现在只是完成了添加敌方飞机和***并让它们动了起来,然后我们还要在init中添加计时器还添加更多敌方飞机和***

在init中添加以下代码:

//生成敌方飞机
this->schedule(schedule_selector(GameScene::GameLogic),0.5);
//生成***
this->schedule(schedule_selector(GameScene::GameLogicaddBullet1),0.3);

上面函数的意思是,0.5s调用一次GameLogic函数,0.3s调用一次GameLogicaddBullet1函数。所以我们只需要在GameLogic中调用addMonster,在GameLogicaddBullet1中调用addBullet1就可以了

void GameScene::GameLogic(float dt)
{
    this->addMonster();
}
void GameScene::GameLogicaddBullet1(float dt){
this->addBullet1();
}

注意一定要在参数中加上float dt,它是表明多少秒调用一次这个函数。


2.通过触屏移动飞机

现在只有敌方飞机和***会移动,我们玩家的飞机还不会移动,下面就教大家如何移动玩家飞机


首先先在init中添加

this->setTouchEnabled(true);

让程序支持触屏


然后重载ccTouchesBegan函数

void GameScene::ccTouchesBegan(cocos2d::CCSet *pTouches,cocos2d::CCEvent *pEvent){
CCTouch *touch = (CCTouch*)pTouches->anyObject();
CCPoint location = this->convertTouchToNodeSpace(touch);
//如果触摸点在屏外则将点设在屏幕边缘
if(location.x < origin.x + player->getContentSize().width/2)
location.x = origin.x + player->getContentSize().width/2;
if(location.x > origin.x + visibleSize.width - player->getContentSize().width/2)
location.x = origin.x + visibleSize.width - player->getContentSize().width/2;
if(location.y < origin.y+player->getContentSize().height/2)
location.y = origin.y+player->getContentSize().height/2;
if(location.y > origin.y + visibleSize.height - player->getContentSize().height/2)
location.y = origin.y + visibleSize.height - player->getContentSize().height/2;

float x1 = player->getPositionX();
float y1 = player->getPositionY();
float x2 = location.x;
float y2 = location.y;
float length = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
float speed  = 480;
float time   = length/speed;
//停止之前的动作
player->stopAllActions();
CCMoveTo *actionMove = CCMoveTo::create(time,location);
    player->runAction(actionMove);
}

前两句是获取触摸点location,如果触摸点在屏幕外就把它设置在里面

因为CCMoveTo第一个参数是时间不是速度,我们为了让移动任何距离的速度都一样我们需要计算一下把时间设为变量,用勾股定理算出移动的长度,然后设置移动的速度,最后就可以算出移动的时间的


我们之前只是重载ccTouchesBegan函数,我们发现飞机只有在我们鼠标点下去的时候才会移动,飞机并不会随着我们鼠标的拖动而移动,所以我们还需要重载ccTouchesMoved函数

void GameScene::ccTouchesMoved(cocos2d::CCSet *pTouches,cocos2d::CCEvent *pEvent){
CCTouch *touch = (CCTouch*)pTouches->anyObject();
CCPoint location = this->convertTouchToNodeSpace(touch);
//如果触摸点在屏外则将点设在屏幕边缘
if(location.x < origin.x+player->getContentSize().width/2)
location.x = origin.x+player->getContentSize().width/2;
if(location.x > origin.x + visibleSize.width - player->getContentSize().width/2)
location.x = origin.x + visibleSize.width - player->getContentSize().width/2;
if(location.y < origin.y+player->getContentSize().height/2)
location.y = origin.y+player->getContentSize().height/2;
if(location.y > origin.y + visibleSize.height - player->getContentSize().height/2)
location.y = origin.y + visibleSize.height - player->getContentSize().height/2;
  
player->setPosition(location);
}

因为每一次拖动鼠标都会调用ccTouchesMoved函数,所以我们用setPosition就可以了

敌方飞机和***都会移动了,我们最后只需要设置碰撞就可以了


3.碰撞测试

因为,敌方飞机和***不是一个精灵所以我们要用一个容器把他们装起来

在init中添加

cocos2d::CCArray     *_monsters;

cocos2d::CCArray     *_bullets;

然后在构造函数中把他们初始化为NULL

在虚构函数中添加

GameScene::~GameScene(){
 if (_monsters)
    {
        _monsters->release();
        _monsters = NULL;
    }
    if (_bullets)
    {
        _bullets->release();
        _bullets = NULL;
    }
}

我们需要在添加飞机和***精灵的时候把他们装进容器里面

分别在addMonster和addBullet1中添加添加以下代码

//给敌方飞机添加标签,并把敌方飞机添加到数组中
pMonster->setTag(1);
    _monsters->addObject(pMonster);

//给***添加标签,并把***添加到数组中
pBullet->setTag(2);
    _bullets->addObject(pBullet);

然后在spriteMoveFinished中添加当销毁飞机和***和把他们从容器中删除

在spriteMoveFinished中添加以下代码

if (sprite->getTag() == 1)
{
_monsters->removeObject(sprite);
}
else if (sprite->getTag() ==2)
{
_bullets->removeObject(sprite);
}

最后我们就可以做碰撞测试了,我们把碰撞做成一个计时器,让他每一帧调用一次来判断碰撞

在init中添加

void GameScene::collision(float dt){
CCArray *bulletsToDelete = CCArray::create();
CCObject *pObject = NULL;
CCObject *pObject2 = NULL;
CCARRAY_FOREACH(_bullets, pObject){                           //遍历***数组
CCSprite *bullet = (CCSprite*)pObject;
CCArray *monstersToDelete = CCArray::create();
CCARRAY_FOREACH(_monsters, pObject2)                      //遍历敌方飞机数组
{
CCSprite *monster = (CCSprite*)pObject2;
if(bullet->boundingBox().intersectsRect(monster->boundingBox()))      //判断***的边界是否碰到敌方飞机的边界       
{
monstersToDelete->addObject(monster);    //***碰到飞机边界的话就放入monstersToDelete中
bulletsToDelete->addObject(bullet);              //同样***也要放入bulletsToDelete中
}           
}
CCARRAY_FOREACH(monstersToDelete, pObject2)//遍历monstersToDelete,把待删除的飞机都删除了
{
CCSprite *monster = (CCSprite*)pObject2;
_monsters->removeObject(monster);
this->removeChild(monster, true);
}
monstersToDelete->release();
}
CCARRAY_FOREACH(bulletsToDelete, pObject)    //遍历bulletsToDelete,把待删除的***都删除了
{
CCSprite *bullet = (CCSprite*)pObject;
_bullets->removeObject(bullet);
//爆炸点
CCSprite *monsterExplosion = CCSprite::create ("monsterExplosion.png");
float x = bullet->getPositionX();
float y = bullet->getPositionY();
monsterExplosion->setPosition (ccp(x,y));
this->removeChild(bullet, true);
//爆炸
this->addChild(monsterExplosion,2);
CCActionInterval * fadeout = CCFadeOut::create(1);
CCCallFuncN *actionMoveDone = CCCallFuncN::create(this, callfuncN_selector(GameScene::spriteMoveFinished));
monsterExplosion->runAction(CCSequence::create(fadeout,actionMoveDone,NULL));
//爆炸音效
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("explosion.mp3");
//分数加一
score += 1;
}
bulletsToDelete->release();
//玩家飞机与敌方飞机碰撞
CCARRAY_FOREACH(_monsters, pObject2)                             //遍历敌方飞机数组
{
CCSprite *monster = (CCSprite*)pObject2;
if(player->boundingBox().intersectsRect(monster->boundingBox()))          //判断玩家飞机的边界是否与敌方飞机碰撞
{
CCSprite *playerExplosion = CCSprite::create("playerExplosion.png");
playerExplosion->setPosition(player->getPosition());
this->addChild(playerExplosion,2);
CCActionInterval * fadeout = CCFadeOut::create(1);
CCCallFuncN *actionMoveDone = CCCallFuncN::create(this, callfuncN_selector(GameScene::spriteMoveFinished));
playerExplosion->runAction(CCSequence::create(fadeout,actionMoveDone,NULL));
//失败画面
GameScene::gameOver();
}
}
}

我们把之前的***数组和敌方飞机容器遍历一边,判断他们是否碰撞,碰撞的话就放到一个待删除的容器中去,最后再遍历这个待删除的容器,把里面的精灵都删除了。

另外我们还需要在删除他们之前添加一个爆炸的精灵,达到飞机爆炸的效果。

接着我们遍历敌方飞机容器,判断它们和玩家飞机是否碰撞,碰撞的话就添加一个爆炸精灵,并且弹出失败的画面。


失败的画面我们会在下一章介绍,在下一章的内容中我们会进一步的完善我们的游戏,现在我们的游戏虽然能玩了但是它还缺少一些游戏的因素,例如分数、画面滚动、主菜单界面等等,我们会在下一章中介绍。