本文主要是介绍cocos2d-x教程:太空游戏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
原文来自:http://www.raywenderlich.com/33752/cocos2d-x-tutorial-for-ios-and-android-space-game
第一次翻译,本身自己英语也不好,还好有谷歌翻译,加上是初学者,很多地方也不是很明白,不对的地方大家直接指出来。
我这里只关注了IOS的部分,目前我用的是Cocos2d-x 2.2.1版本。
Getting Started
首先,从这里下载太空游戏资源包 ,然后解压到你的磁盘上。
然后将文件单个的放到工程的Resources目录下,就像这样:
加入一个太空船
1. 让我们尝试看一下这样是否可行,打开工程的Classes\HelloWorldScene.h,添加下面的代码到HelloWorldScene类成员变量声明的地方。(为了偷懒不写命名空间,先添加宏USING_NS_CC;)
CCSize visibleSize;
CCSpriteBatchNode *_batchNode;
CCSprite *_ship;
这里创建了两个私有的成员变量,一个是精灵批量节点,另一个表示太空船。
接着切换到HelloWorldScene.cpp文件,进入init()函数,从if语句的后面开始删除后面的代码,直到return true(不删除);
2. 添加一个菜单项到init()最后,然后再添加下面的代码:
visibleSize = CCDirector::sharedDirector()->getVisibleSize();_batchNode = CCSpriteBatchNode::create("Sprites.pvr.ccz");this->addChild(_batchNode);CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("Sprites.plist");_ship = CCSprite::createWithSpriteFrameName("SpaceFlier_sm_1.png");_ship->setPosition(ccp(visibleSize.width*0.1,visibleSize.height*0.5));_batchNode->addChild(_ship,1);
编译->运行,你将会看到一艘太空船出现在模拟器的屏幕上。
添加视图差滚动
接着,用parallax scrolling来让太空背景滚动起来是一个比较吊的方法。
然后,在HelloWorldScene.h中加一些私有的成员变量:
CCParallaxNode *_backgroundNode;CCSprite *_spacedust1;CCSprite *_spacedust2;CCSprite *_planetsunrise;CCSprite *_galaxy;CCSprite *_spacialanomaly;CCSprite *_spacialanomaly2;
在HelloWorldScene.cpp的init()方法中添加:(在return true;的前面)
_spacedust1 = CCSprite::create("bg_front_spacedust.png");_spacedust2 = CCSprite::create("bg_front_spacedust.png");_planetsunrise = CCSprite::create("bg_planetsunrise.png");_galaxy = CCSprite::create("bg_galaxy.png");_spacialanomaly = CCSprite::create("bg_spacialanomaly.png");_spacialanomaly2 = CCSprite::create("bg_spacialanomaly2.png");CCPoint dustSpeed = ccp(0.1,0.1);CCPoint bgSpeed = ccp(0.05,0.05);_backgroundNode->addChild(_spacedust1, 0, dustSpeed, ccp(0, visibleSize.height/2));_backgroundNode->addChild(_spacedust2, 0, dustSpeed,ccp(_spacedust1->getContentSize().width,visibleSize.height/2));_backgroundNode->addChild(_galaxy, -1, bgSpeed, ccp(0, visibleSize.height * 0.7));_backgroundNode->addChild(_planetsunrise, -1 , bgSpeed, ccp(600, visibleSize.height * 0));_backgroundNode->addChild(_spacialanomaly, -1, bgSpeed, ccp(900, visibleSize.height * 0.3));_backgroundNode->addChild(_spacialanomaly2, -1, bgSpeed, ccp(1500, visibleSize.height * 0.9));
编译->运行,你将看到这样的界面:
现在是让背景滚动的时候了,首先在HelloWorldScene.h中声明update方法——你可能会随便添加到private区或者public区,但是update方法是作为内部用,所以更适合加到private区。
void update(float dt);
然后到HelloWordScene.cpp中去实现它:
void HelloWorld::update(float dt)
{CCPoint backgroundScrollVert = ccp(-1000,0);_backgroundNode->setPosition(ccpAdd(_backgroundNode->getPosition(), ccpMult(backgroundScrollVert, dt)));
}
this->scheduleUpdate();
编译->运行,你就会看到背景滚动起来了。
Continuous Scrolling
但是,你会注意到背景滚出了屏幕边界,就没有重复滚了,你应该修改它。
在工程中,我们新建一个C++的类文件,命名为CCParallaxNodeExtras:
这样,我们的工程中就多增加了两个文件:
然后,用下面的代码替换掉CCParallaxNodeExtras.h中的内容:
#ifndef __SpaceGame__CCParallaxNodeExtras__
#define __SpaceGame__CCParallaxNodeExtras__#include "cocos2d.h"
USING_NS_CC;
class CCParallaxNodeExtras:public CCParallaxNode
{
public:CCParallaxNodeExtras();static CCParallaxNodeExtras *node();void incrementOffset(CCPoint offset,CCNode *node);
};#endif /* defined(__SpaceGame__CCParallaxNodeExtras__) */
这个类继承了CCParallaxNode,新加了incrementOffset方法用来更新parallax node的子节点的位置。背景从左边移出的部分就会加到背景的右边,形成连续滚动的效果。
接着,用下面的代码替换掉CCParallaxNodeExtras.cpp中的内容:
#include "CCParallaxNodeExtras.h"class CCPointObject : CCObject
{CC_SYNTHESIZE(CCPoint, m_tRatio, Ratio);CC_SYNTHESIZE(CCPoint, m_tOffset, Offset);CC_SYNTHESIZE(CCNode*, m_pChild, Child);
};CCParallaxNodeExtras::CCParallaxNodeExtras()
{CCParallaxNode();
}CCParallaxNodeExtras* CCParallaxNodeExtras::node()
{return new CCParallaxNodeExtras();
}void CCParallaxNodeExtras::incrementOffset(cocos2d::CCPoint offset, cocos2d::CCNode *node)
{for (int i=0; i<m_pParallaxArray->num; ++i) {CCPointObject *point = (CCPointObject*)m_pParallaxArray->arr[i];CCNode *curNode = point->getChild();if (curNode->isEqual(node)) {point->setOffset(ccpAdd(point->getOffset(), offset));break;}}
}
next,在HelloWorldScene.h中添加头文件:
#include "CCParallaxNodeExtras.h"
then,改变_backgroundNode的定义,如下
CCParallaxNodeExtras *_backgroundNode;
.cpp文件中:
_backgroundNode = CCParallaxNodeExtras::node();
finally,把下面代码加到update方法中:
CCArray *spaceDusts = CCArray::createWithCapacity(2);spaceDusts->addObject(_spacedust1);spaceDusts->addObject(_spacedust2);for (int i=0; i<spaceDusts->count(); ++i) {CCSprite *spaceDust = (CCSprite*)(spaceDusts->objectAtIndex(i));float xPosition = _backgroundNode->convertToWorldSpace(spaceDust->getPosition()).x;float size = spaceDust->getContentSize().width;if (xPosition < -size/2) {_backgroundNode->incrementOffset(ccp(spaceDust->getContentSize().width*2,0), spaceDust);}}CCArray *backGrounds = CCArray::createWithCapacity(4);backGrounds->addObject(_galaxy);backGrounds->addObject(_planetsunrise);backGrounds->addObject(_spacialanomaly);backGrounds->addObject(_spacialanomaly2);for (int i=0; i<backGrounds->count(); ++i) {CCSprite *background = (CCSprite*)backGrounds->objectAtIndex(i);float xPosition = _backgroundNode->convertToWorldSpace(background->getPosition()).x;float size = background->getContentSize().width;if (xPosition < -size) {_backgroundNode->incrementOffset(ccp(2000, 0), background);}}
CCArray是基于STL的,用createWithCapacity构造方法将会自动帮你释放对象。
编译->运行,现在你的背景就能无限的滚动了!
Adding Stars
添加星星相当简单,只需在init()方法中加入:
HelloWorld::addChild(CCParticleSystemQuad::create("Stars1.plist"));HelloWorld::addChild(CCParticleSystemQuad::create("Stars2.plist"));HelloWorld::addChild(CCParticleSystemQuad::create("Stars3.plist"));
cocos2d-x 提供了围绕加速器输入的一个抽象层。来看看它是怎么工作的?
First,在HelloWordScene.h中加入一个新的private变量:
float _shipPointPerSecY;
then,添加一个public方法:
virtual void didAccelerate(CCAcceleration *pAccelerationValue);
then, 在init()中让加速计可用:
this->setAccelerometerEnabled(true);
next,在HelloWordScene.cpp中实现上面的方法:
void HelloWorld::didAccelerate(cocos2d::CCAcceleration *pAccelerationValue)
{
#define KFILTERINGFACTOR 0.1
#define KRESTACCELX -0.6
#define KSHIPMAXPOINTSPERSEC (visibleSize.height*0.5)
#define KMAXDIFFX 0.2double rollingX;pAccelerationValue->x = pAccelerationValue->y;rollingX = (pAccelerationValue->x * KFILTERINGFACTOR) + (rollingX * (1.0 - KFILTERINGFACTOR));float accelX = pAccelerationValue->x - rollingX;float accelDiff = accelX - KRESTACCELX;float accelFraction = accelDiff / KMAXDIFFX;_shipPointPerSecY = KSHIPMAXPOINTSPERSEC * accelFraction;
}
float maxY = visibleSize.height - _ship->getContentSize().height/2;float minY = _ship->getContentSize().height/2;float diff = _shipPointPerSecY * dt;float newY = _ship->getPosition().y + diff;newY = MIN(MAX(newY, minY), maxY);_ship->setPosition(ccp(_ship->getPosition().x,newY));
didAccelerate方法的参数提供了一个包含x,y,z加速计数据信息CCAcceleration类型的变量,在这篇cocos2d-x教程中,你只要关心加速计上的x,就能让你的太空船在跟着手机旋转。
编译->运行,来看看效果。(加速计在模拟器上没有效果的,所以要用真机才能看到效果)
Adding Asteroids(小行星)
添加一些小行星到游戏中,同样先在HelloWordScene.h中加入private成员变量:
CCArray *_asteroids;int _nextAsteroid;float _nextAsteroidSpawn;
then,添加一些public方法:
float randomValueBetween(float low,float high);void setInvisible(CCNode *node);float getTimeTick();
next,去实现这些方法:
float HelloWorld::randomValueBetween(float low, float high)
{return (((float)arc4random() / 0xFFFFFFFFu) * (high - low)) + low;
}float HelloWorld::getTimeTick()
{timeval time;gettimeofday(&time, NULL);unsigned long millisecs = (time.tv_sec * 1000) + (time.tv_usec / 1000);return (float)millisecs;
}
randomValueBetween是一个辅助方法,用来获取在一个浮点数范围内的随机数。
getTimeTick是一个可移植的方法,用来获取以毫秒为单位的时间。(作为一个跨平台方法,替代了CACurrentMediaTime,但产生的是毫秒而不是秒)
next,为了创建一个asteroids数组,添加下面的代码到init()中。
#define KNUMASTEROIDS 15_asteroids = new CCArray();for (int i=0; i<KNUMASTEROIDS; ++i) {CCSprite *asteroid = CCSprite::createWithSpriteFrameName("asteroid.png");asteroid->setVisible(false);_batchNode->addChild(asteroid);_asteroids->addObject(asteroid);}
这里,用CCArray类来存储CCSprite类型的对象,注意你是手动new出来的(而不是用arrayWithCapacity)是为了避免autorelease机制。
finally,添加下面代码到update方法中:
float curTimeMillis = getTimeTick();if (curTimeMillis > _nextAsteroidSpawn){float randMillisecs = randomValueBetween(0.2, 1.0) * 1000;_nextAsteroidSpawn = randMillisecs + curTimeMillis;float randY = randomValueBetween(0.0, visibleSize.height);float randDuration = randomValueBetween(2.0, 10.0);CCSprite *asteroid = (CCSprite*)_asteroids->objectAtIndex(_nextAsteroid);_nextAsteroid++;if (_nextAsteroid >= _asteroids->count()) {_nextAsteroid = 0;}asteroid->stopAllActions();asteroid->setPosition(ccp(visibleSize.width + asteroid->getContentSize().width/2,randY));asteroid->setVisible(true);asteroid->runAction(CCSequence::create(CCMoveBy::create(randDuration, ccp(-visibleSize.width - asteroid->getContentSize().width, 0)),CCCallFuncN::create(this,callfuncN_selector(HelloWorld::setInvisible)),NULL));
但是为了兼容性,cocos2d-x开发者决定保持与Cocos2d兼容,这就需要用NULL来结尾,如果你没有加NULL的话,程序可能会崩溃。
最后一步,添加setInvisible回调方法:
void HelloWorld::setInvisible(cocos2d::CCNode *node)
{node->setVisible(false);
}
编译->运行,你就会看到下面的结果:出现了很多小行星
Shooting Lasers(激光射击)
到了让太空船射击的时候了,像以前一样添加private变量:
CCArray *_shipLasers;int _nextShipLaser;
virtual void ccTouchesBegan(CCSet *touches,CCEvent *evnet);
then,还是在init()中添加下面代码:
#define KNUMLASERS 5_nextShipLaser = 0;_shipLasers = new CCArray();for (int i=0; i< KNUMLASERS; ++i) {CCSprite* shipLaser = CCSprite::createWithSpriteFrameName("laserbeam_blue.png");shipLaser->setVisible(false);_batchNode->addChild(shipLaser);_shipLasers->addObject(shipLaser);}this->setTouchEnabled(true);
void HelloWorld::ccTouchesBegan(cocos2d::CCSet *touches, cocos2d::CCEvent *evnet)
{CCSprite *shipLaser = (CCSprite*)_shipLasers->objectAtIndex(_nextShipLaser++);if (_nextShipLaser >= _shipLasers->count()){_nextShipLaser = 0;}shipLaser->setPosition(ccpAdd(_ship->getPosition(), ccp(shipLaser->getContentSize().width/2,0)));shipLaser->setVisible(true);shipLaser->stopAllActions();shipLaser->runAction(CCSequence::create(CCMoveBy::create(0.5, ccp(visibleSize.width, 0)),CCCallFuncN::create(this, callfuncN_selector(HelloWorld::setInvisible)),NULL));}
编译->运行,点击屏幕就能看到我们发射的激光了
Basic Collision Detection(基本碰撞检测)
next,你将添加一些代码来检测激光和小行星的碰撞,当激光打到小行星时,发生爆炸。
first,添加private变量:
int _lives;
then,在update方法中添加:
CCObject *asteroid1;CCObject *shipLaser1;CCARRAY_FOREACH(_asteroids, asteroid1){CCSprite* asteroid = (CCSprite*)asteroid1;if (!asteroid->isVisible()){continue;}CCARRAY_FOREACH(_shipLasers, shipLaser1){CCSprite* shipLaser = (CCSprite*)shipLaser1;if (!shipLaser->isVisible()){continue;}if (shipLaser->boundingBox().intersectsRect(asteroid->boundingBox())){shipLaser->setVisible(false);asteroid->setVisible(false);continue;}}if (_ship->boundingBox().intersectsRect(asteroid->boundingBox())){asteroid->setVisible(false);_ship->runAction(CCBlink::create(1.0, 9));_lives-- ;}}
编译->运行,你就可以炸毁小行星了!
当然,你会注意到当激光打到小行星,并没有爆炸效果,因为我们没有给它添加爆炸粒子效果。前面在添加星星的时候,我们用到了粒子系统,这个问题留给读者。
Win/Lose Detection(输赢检测)
在HelloWorldScene.h中类定义前面加入一个枚举:
typedef enum{KENDREASONWIN,KENDREASONLOSE
}EndReason;
double _gameOverTime;bool _gameOver;
void endScene(EndReason endReason);void restartTapped();
_lives = 3;double curTime = getTimeTick();_gameOverTime = curTime + 30000;
if (_lives <=0) {_ship->stopAllActions();_ship->setVisible(false);this->endScene(KENDREASONLOSE);}else if(curTimeMillis>= _gameOverTime){this->endScene(KENDREASONWIN);}
finally,实现刚才的那两个方法:
void HelloWorld::restartTapped()
{CCDirector::sharedDirector()->replaceScene(CCTransitionZoomFlipX::create(0.5, this->scene()));this->scheduleUpdate();
}
void HelloWorld::endScene(EndReason endReason)
{if (_gameOver) {return;}_gameOver = true;char message[10] = "You Win";if (endReason == KENDREASONLOSE) {strcpy(message, "You Lose");}CCLabelBMFont *label = CCLabelBMFont::create(message, "Arial.fnt");label->setScale(0.1);label->setPosition(ccp(visibleSize.width/2,visibleSize.height/2));this->addChild(label);strcpy(message, "Restart");CCLabelBMFont *restartLabel = CCLabelBMFont::create(message, "Arial.fnt");CCMenuItemLabel *restartItem = CCMenuItemLabel::create(restartLabel, this, menu_selector(HelloWorld::restartTapped));restartItem->setScale(0.1);restartItem->setPosition(ccp(visibleSize.width/2, visibleSize.height*0.4));CCMenu *menu = CCMenu::create(restartItem,NULL);menu->setPosition(CCPointZero);this->addChild(menu);restartItem->runAction(CCScaleTo::create(0.5, 1.0));label->runAction(CCScaleTo::create(0.5, 1.0));this->unscheduleUpdate();}
音效
在HelloWordScene.cpp的顶部加入所需的头文件和命名空间
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
SimpleAudioEngine::sharedEngine()->playBackgroundMusic("SpaceGame.wav", true);SimpleAudioEngine::sharedEngine()->preloadEffect("explosion_large.wav");SimpleAudioEngine::sharedEngine()->preloadEffect("laser_ship.wav");
if (shipLaser->boundingBox().intersectsRect(asteroid->boundingBox())){<span style="color:#006600;"> SimpleAudioEngine::sharedEngine()->playEffect("explosion_large.wav");</span>shipLaser->setVisible(false);asteroid->setVisible(false);continue;}
SimpleAudioEngine::sharedEngine()->playEffect("laser_ship.wav");
编译->运行,还不错吧,算是完成了!
最后,完整的太空游戏代码点击这里下载。
这篇关于cocos2d-x教程:太空游戏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!