游戏截图
Sky Defense 是cocos2d-x by Example Beginner's Guide,第4章的游戏示例。
先从GameLayer::init()说起,游戏的大部分初始化,都是在这里做的。
//zeek.
_screenSize = CCDirector::sharedDirector()->getWinSize();
gameCreateScreen(); //添加背景及其他元素
//
createPools();
createActions();//创建精灵的动作
//暂时还没有运行,所以设置为false(_running的值默认为false,也可以不用设置)
this->_running = false;
this->setTouchEnabled(true); //触控设置为可用
//_fallingObjects存放 falling Objects,在爆炸时候可以遍历数组
_fallingObjects = CCArray::createWithCapacity(40);//将下落物,放在数组里方便统一管理。
_fallingObjects->retain();//CCArray不是auotorelease()的
//zeek..
this->schedule(schedule_selector(GameLayer::update));//启用定时器,每帧调用一次GameLayer::update()
SimpleAudioEngine::sharedEngine()->playBackgroundMusic("background.mp3");//循环播放背景音乐
下面介绍 gameCreateScreen(),原文为createGameScreen()
void GameLayer::gameCreateScreen() {
CCSprite *bg = CCSprite::create("bg.png");
bg->setPosition(ccp(_screenSize.width * 0.5F, _screenSize.height * 0.5F));
this->addChild(bg);
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("sprite_sheet.plist");
_gameBatchNode = CCSpriteBatchNode::create("sprite_sheet.png");
this->addChild(_gameBatchNode);
CCSprite *sprite;
//贴图城市背景
for (int i = 0; i < 2; i++) {
sprite = CCSprite::createWithSpriteFrameName("city_dark.png");
sprite->setPosition(ccp(_screenSize.width * (0.25F + i * 0.5F), sprite->boundingBox().size.height * 0.5F));
_gameBatchNode->addChild(sprite,kForeground);
sprite = CCSprite::createWithSpriteFrameName("city_light.png");
sprite->setPosition(ccp(_screenSize.width * (0.25F + i * 0.5F), sprite->boundingBox().size.height * 0.9F));
_gameBatchNode->addChild(sprite,kBackground);
}
//种树
for (int i = 0; i < 3; i++) {
sprite = CCSprite::createWithSpriteFrameName("trees.png");
sprite->setPosition(ccp(_screenSize.width * (0.2F + i * 0.3F),sprite->boundingBox().size.height * 0.5F) );
_gameBatchNode->addChild(sprite,kForeground);
}
//添加云彩
this->_clouds = CCArray::createWithCapacity(4);
_clouds->retain();
float cloud_y;
for (int i = 0; i < 4; i++) {
CCSprite *cloud = CCSprite::createWithSpriteFrameName("cloud.png");
//云彩需要不规则的排列,否则不生动
cloud_y = i % 2 == 0 ? _screenSize.height * 0.4F : _screenSize.height * 0.5F ;
cloud->setPosition(ccp(_screenSize.width * 0.1F + i * _screenSize.width * 0.25F, cloud_y));
_gameBatchNode->addChild(cloud, kBackground);
this->_clouds->addObject(cloud); //添加到数组中去,方便管理
}
//得分
_scoreDisplay = CCLabelBMFont::create("0","font.fnt",_screenSize.width * 0.3F);
_scoreDisplay->setAnchorPoint(ccp(1.0F, 0.5F));
_scoreDisplay->setPosition(_screenSize.width * 0.9F, _screenSize.height * 0.95F);
this->addChild(_scoreDisplay);
//生命力
_energyDisplay = CCLabelBMFont::create("100","font.fnt", _screenSize.width * 0.1F,kCCTextAlignmentRight);
_energyDisplay->setPosition(_screenSize.width * 0.05F, _screenSize.height * 0.95F);
this->addChild(_energyDisplay);
//health_Icon图标
CCSprite *icon = CCSprite::createWithSpriteFrameName("health_icon.png");
CCAssert(icon,"icon(NULL),get health_icon.png failed in GameLayer::gameCreateScreen()");
icon->setPosition(ccp(_screenSize.width * 0.15F, _screenSize.height * 0.95F));
_gameBatchNode->addChild(icon,kForeground);
//bomb
_bomb = CCSprite::createWithSpriteFrameName("bomb.png");
_bomb->getTexture()->generateMipmap();
_bomb->setVisible(false);
CCSize bombBoundingBoxSize = _bomb->boundingBox().size ;
///add Halo inside to bomb
CCSprite *halo = CCSprite::createWithSpriteFrameName("halo.png");
halo->setPosition(ccp(bombBoundingBoxSize.width * 0.4F, bombBoundingBoxSize.height * 0.4F));
_bomb->addChild(halo,kMiddleground,kSpriteHalo);
///add sparkle inside to bomb
CCSprite *sparkle = CCSprite::createWithSpriteFrameName("sparkle.png");
sparkle->setPosition(ccp(bombBoundingBoxSize.width * 0.72F,bombBoundingBoxSize.height * 0.72F));
_bomb->addChild(sparkle, kMiddleground,kSpriteSparkle);
//添加 bomb
_gameBatchNode->addChild(_bomb,kForeground);
//shockwave
_shockWave = CCSprite::createWithSpriteFrameName("shockwave.png");
_shockWave->getTexture()->generateMipmap();
_shockWave->setVisible(false);
_gameBatchNode->addChild(_shockWave);
// introMessage, gameOverMessage
_introMessage = CCSprite::createWithSpriteFrameName("logo.png");
_introMessage->setPosition(ccp(_screenSize.width * 0.5F, _screenSize.height * 0.6F));
_introMessage->setVisible(true);
this->addChild(_introMessage, kForeground);
_gameOverMessage = CCSprite::createWithSpriteFrameName("gameover.png");
_gameOverMessage->setPosition(ccp(_screenSize.width * 0.5F, _screenSize.height * 0.65F));
_gameOverMessage->setVisible(false);
this->addChild(_gameOverMessage, kForeground);
}
在createGameScreen中添加了,可见的城市背景图片,种,云彩,生命值,得分,_introMessage,
还有暂时不可见的gameMessage和bomb。其中bomb又包含了2个子结点Halo和Sparkle。还有一个炸弹爆炸后的shockwave。
我对其中的_energyDisplay做了小小的调整。去掉了后面的“%”。
看完了,gameCreateScreen(),我们接下来看看createActions()
void GameLayer::createActions() {
//坠落动作
CCFiniteTimeAction *easeSwing = CCSequence::create(CCEaseInOut::create(CCRotateTo::create(1.2F,-10),2),
CCEaseInOut::create(CCRotateTo::create(1.2F,10),2), NULL);
this->_swingHealth = CCRepeatForever::create((CCActionInterval *)easeSwing);
this->_swingHealth->retain();
//冲击波,淡出,完成时回调shockwaveDone(),还有retain()
_shockwaveSequence = CCSequence::create(CCFadeOut::create(1.0F),CCCallFunc::create(this,callfunc_selector(GameLayer::shockwaveDone)),NULL);
_shockwaveSequence->retain();
//炸弹,膨胀
_growBomb = CCScaleTo::create(6.0F,1.0F);
_growBomb->retain();
//旋转
_rotateSprite = CCRepeatForever::create(CCRotateBy::create(0.5F,-90) );
_rotateSprite->retain();
//bomb爆炸的动画
CCAnimation *animation;
CCSpriteFrame *frame;
animation = CCAnimation::create();
CCString *name;
//利用10帧的动画(CCAnimation是资源)来创建CCAnimate(动作),通过动作序列来复合动作
for (int i = 1; i < 10; i++) {
name = CCString::createWithFormat("boom%i.png",i);
frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(name->getCString());
CCAssert(frame,"frame was NULL,in GameLayer::createActions()");
animation->addSpriteFrame(frame);
}
animation->setDelayPerUnit(1 / 10.0F);//动画的快慢在这里调整
animation->setRestoreOriginalFrame(true);//传入true
//撞到地面的动画,别忘了CCSequence::create(... ,NULL);
_groundHit = CCSequence::create(CCMoveBy::create( 0, ccp(0,_screenSize.height * 0.12F)),CCAnimate::create(animation),CCCallFuncN::create(this,callfuncN_selector(GameLayer::animationDone) ),NULL);
_groundHit->retain();
//爆炸动画
animation = CCAnimation::create();
int frameCount = 7;
for (int i= 1; i < frameCount; i++) {
name = CCString::createWithFormat("explosion_small%i.png", i);
frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(name->getCString());
CCAssert(frame,"frame was NULL,in GameLayer::createActions()");
animation->addSpriteFrame(frame);
}
animation->setDelayPerUnit(0.5F / frameCount);
animation->setRestoreOriginalFrame(true);
_explosion = CCSequence::create(CCAnimate::create(animation),CCCallFuncN::create(this,callfuncN_selector(GameLayer::animationDone)),NULL);
_explosion->retain();
}
注意CCCallFuncN::create(this,callfuncN_selector(GameLayer::animationDone) 这个this作为参数传入GameLayer::animationDone(this),N代表CCNode
animationDone()将执行动作的精灵通过setVisible(false)隐藏起来(通过参数传进来的this->,在暗中作梗)。
下面的GameLayer::ccTouchesBegan随着你的手指在屏幕轻点(tap)一下,整个游戏就被赋予了生命,动起来了。
void GameLayer::ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *event) {
//游戏如果不是running状态,要么是未开始,要么是已经结束
if (!_running) {
if (_introMessage->isVisible() ) {
_introMessage->setVisible(false);
}else if (_gameOverMessage->isVisible() ) {
_gameOverMessage->setVisible(false);
}
this->resetGame();
return ;
}
//running
CCTouch *touch = (CCTouch *)pTouches->anyObject();
//如果炸弹在膨胀
if (touch)
{
if (_bomb->isVisible() ) {
//不管炸弹是否可以爆炸(1.爆炸,2.无效(不具备爆炸条件,则销毁)), 停止所有动作包括(child's actions)
_bomb->stopAllActions();
CCSprite* child;
child = (CCSprite *)_bomb->getChildByTag(kSpriteSparkle);
child->stopAllActions();
child = (CCSprite *)_bomb->getChildByTag(kSpriteHalo);
child->stopAllActions();
//如果炸弹已经准备好,则创建shockWave
if (_bomb->getScale() > 0.3F) {
_shockWave->setScale(0.1F);
_shockWave->setPosition(_bomb->getPosition());
_shockWave->setVisible(true);
_shockWave->runAction(CCScaleTo::create(0.5F,_bomb->getScale() * 2.0F));
_shockWave->runAction((CCFiniteTimeAction *)_shockwaveSequence->copy()->autorelease() );
//播放音效
SimpleAudioEngine::sharedEngine()->playEffect("bombRelease.wav");
}
else {
SimpleAudioEngine::sharedEngine()->playEffect("bombFail.wav");
}
_bomb->setVisible(false);
_shockwaveHits = 0;
}
//当前screen中没有bomb创建一个
else {
//缩小,位置,可见,停止原有的动作,透明度50%。添加;shockWave和Halo
CCPoint tap = touch->getLocation();
_bomb->stopAllActions(); //zeek,child's actions是不是也要停止?,此时无雷,何谈child?
_bomb->setPosition(tap);
_bomb->setScale(0.1F);
_bomb->setOpacity(50);
_bomb->setVisible(true);
_bomb->runAction((CCAction*) _growBomb->copy()->autorelease() );
CCSprite *child;
child = (CCSprite *)_bomb->getChildByTag(kSpriteHalo);
CCAssert(child != NULL,"child(NULL)!,getChildByTag(kSpriteHalo) failed,in GameLayer::ccTouchesBegan()");
child->runAction((CCAction *)_rotateSprite->copy()->autorelease());
child = (CCSprite *)_bomb->getChildByTag(kSpriteSparkle);
CCAssert(child != NULL,"child(NULL)!,getChildByTag(kSpriteSparkle) failed,in GameLayer::ccTouchesBegan()");
child->runAction((CCAction *)_rotateSprite->copy()->autorelease());
}
}
}
再下面,就是游戏的源头了
void GameLayer::update (float dt) {
if (!_running) return;
int count;
int i;
CCSprite * sprite;
//update timers
_meteorTimer += dt;
if (_meteorTimer > _meteorInterval) {
_meteorTimer = 0;
this->resetMeteor();
}
_healthTimer += dt;
if (_healthTimer > _healthInterval) {
_healthTimer = 0;
this->resetHealth();
}
_difficultyTimer += dt;
if (_difficultyTimer > _difficultyInterval) {
_difficultyTimer = 0;
this->increaseDifficulty();
}
if (_bomb->isVisible()) {
if (_bomb->getScale() > 0.3f) {
if (_bomb->getOpacity() != 255)
_bomb->setOpacity(255);
}
}
//检测与冲击波有关的碰撞
if (_shockWave->isVisible() ) {
CCPoint shockWavePosition = _shockWave->getPosition();
CCSprite *sprite;
CCPoint currentSpritePosition;
float diffx, diffy;
char szScore[10] = { 0 };
for (int count = _fallingObjects->count() - 1; count >= 0; count--) {
sprite =(CCSprite *) _fallingObjects->objectAtIndex(count);
currentSpritePosition = sprite->getPosition();
diffx = shockWavePosition.x - currentSpritePosition.x;
diffy = shockWavePosition.y - currentSpritePosition.y;
if (pow(diffx,2)+pow(diffy,2) <= pow(_shockWave->boundingBox().size.width * 0.5F, 2)) {
//sprite 冲击波的范围内
sprite->stopAllActions();
sprite->runAction((CCAction *)_explosion->copy()->autorelease()); //zeek!
SimpleAudioEngine::sharedEngine()->playEffect("boom.wav"); //播放音效
//如果是流星 ,加分
if (sprite->getTag() == kSpriteMeteor) {
_shockwaveHits ++;
_score += _shockwaveHits * 13 + _shockwaveHits * 2;
sprintf(szScore,"%i",_score);
_scoreDisplay->setString(szScore);
}
_fallingObjects->removeObjectAtIndex(count); //被炸了(消失了),当然要从数组中移除
}//if(pow(diffx
}//for
}//if(_shockwave->isVisible())
//move clouds
count = _clouds->count();
for (i = 0; i < count; i++) {
sprite = (CCSprite *) _clouds->objectAtIndex(i);
sprite->setPositionX(sprite->getPositionX() + dt * 20);
if (sprite->getPositionX() > _screenSize.width + sprite->boundingBox().size.width * 0.5f)
sprite->setPositionX(-sprite->boundingBox().size.width * 0.5f);
}
}
下面的这段代码是原作者,大家比较一下有什么不同。
//check collision with shockwave
if (_shockWave->isVisible()) {
count = _fallingObjects->count();
float diffx;
float diffy;
for (i = count-1; i >= 0; i--) {
sprite = (CCSprite *) _fallingObjects->objectAtIndex(i);
diffx = _shockWave->getPositionX() - sprite->getPositionX();
diffy = _shockWave->getPositionY() - sprite->getPositionY();
if (pow(diffx, 2) + pow(diffy, 2) <= pow(_shockWave->boundingBox().size.width * 0.5f, 2)) {
sprite->stopAllActions();
sprite->runAction((CCAction *) _explosion->copy()->autorelease());
SimpleAudioEngine::sharedEngine()->playEffect("boom.wav");
if (sprite->getTag() == kSpriteMeteor) {
_shockwaveHits++;
_score += _shockwaveHits * 13 + _shockwaveHits * 2;
}
//play sound
_fallingObjects->removeObjectAtIndex(i);
}
}
char szValue[100] = {0};
sprintf(szValue, "%i", _score);
_scoreDisplay->setString(szValue);
}
后面重要的函数还有resetGame()
void GameLayer::resetGame() {
//得分,生命
_score = 0;
_energy = 100;
//时间和速度
_meteorInterval = 2.5F;
_meteorTimer = _meteorInterval * 0.99F;
_meteorSpeed = 10;//10s 落地
_healthInterval = 20;
_healthTimer = 0;
_healthSpeed = 15;
_difficultyInterval = 60;
_difficultyTimer = 0;
_running = true;
CCString *value;
value= CCString::createWithFormat("%i",_energy);
_energyDisplay->setString(value->getCString());
value = CCString::createWithFormat("%i",_score);
_scoreDisplay->setString(value->getCString());
}