cocos2d-x教程:太空游戏

2023-12-11 09:40
文章标签 教程 游戏 cocos2d 太空

本文主要是介绍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.cppinit()方法中添加:(在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)));
}


最后,在init()函数返回前调用scheduleUpdate():

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"));


Moving the Ship with Accelerometer

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;
}


finally,在update方法后面添加下面代码:

    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));


注意:因为CCSequence::actions方法是被定义为具有可变数目的参数,你可能想忽略最后的NULL参数,因为它在C++中是可以这样的。

           但是为了兼容性,cocos2d-x开发者决定保持与Cocos2d兼容,这就需要用NULL来结尾,如果你没有加NULL的话,程序可能会崩溃。

最后一步,添加setInvisible回调方法:

void HelloWorld::setInvisible(cocos2d::CCNode *node)
{node->setVisible(false);
}


编译->运行,你就会看到下面的结果:出现了很多小行星



Shooting Lasers(激光射击)

到了让太空船射击的时候了,像以前一样添加private变量:

    CCArray *_shipLasers;int _nextShipLaser;


再添加public方法:

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);


finally,实现出处TouchesBegan 方法:

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;


然后是两个private成员变量:

    double _gameOverTime;bool _gameOver;


接着是两个private成员方法:

    void endScene(EndReason endReason);void restartTapped();


then,进入init()方法,加入:

    _lives = 3;double curTime = getTimeTick();_gameOverTime = curTime + 30000;


update的中加入:

    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;


init()返回前加入:

    SimpleAudioEngine::sharedEngine()->playBackgroundMusic("SpaceGame.wav", true);SimpleAudioEngine::sharedEngine()->preloadEffect("explosion_large.wav");SimpleAudioEngine::sharedEngine()->preloadEffect("laser_ship.wav");


当激光打到小行星时,加入爆炸声,也就是update()方法中:(绿色部分)

if (shipLaser->boundingBox().intersectsRect(asteroid->boundingBox())){<span style="color:#006600;"> SimpleAudioEngine::sharedEngine()->playEffect("explosion_large.wav");</span>shipLaser->setVisible(false);asteroid->setVisible(false);continue;}


最后就是发射激光声,ccTouchesBegan()的开头加入:

 SimpleAudioEngine::sharedEngine()->playEffect("laser_ship.wav");

编译->运行,还不错吧,算是完成了!




最后,完整的太空游戏代码点击这里下载。

 



这篇关于cocos2d-x教程:太空游戏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/480250

相关文章

Java使用Tesseract-OCR实战教程

《Java使用Tesseract-OCR实战教程》本文介绍了如何在Java中使用Tesseract-OCR进行文本提取,包括Tesseract-OCR的安装、中文训练库的配置、依赖库的引入以及具体的代... 目录Java使用Tesseract-OCRTesseract-OCR安装配置中文训练库引入依赖代码实

Ubuntu固定虚拟机ip地址的方法教程

《Ubuntu固定虚拟机ip地址的方法教程》本文详细介绍了如何在Ubuntu虚拟机中固定IP地址,包括检查和编辑`/etc/apt/sources.list`文件、更新网络配置文件以及使用Networ... 1、由于虚拟机网络是桥接,所以ip地址会不停地变化,接下来我们就讲述ip如何固定 2、如果apt安

PyCharm 接入 DeepSeek最新完整教程

《PyCharm接入DeepSeek最新完整教程》文章介绍了DeepSeek-V3模型的性能提升以及如何在PyCharm中接入和使用DeepSeek进行代码开发,本文通过图文并茂的形式给大家介绍的... 目录DeepSeek-V3效果演示创建API Key在PyCharm中下载Continue插件配置Con

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

在不同系统间迁移Python程序的方法与教程

《在不同系统间迁移Python程序的方法与教程》本文介绍了几种将Windows上编写的Python程序迁移到Linux服务器上的方法,包括使用虚拟环境和依赖冻结、容器化技术(如Docker)、使用An... 目录使用虚拟环境和依赖冻结1. 创建虚拟环境2. 冻结依赖使用容器化技术(如 docker)1. 创

Spring Boot整合log4j2日志配置的详细教程

《SpringBoot整合log4j2日志配置的详细教程》:本文主要介绍SpringBoot项目中整合Log4j2日志框架的步骤和配置,包括常用日志框架的比较、配置参数介绍、Log4j2配置详解... 目录前言一、常用日志框架二、配置参数介绍1. 日志级别2. 输出形式3. 日志格式3.1 PatternL

MySQL8.2.0安装教程分享

《MySQL8.2.0安装教程分享》这篇文章详细介绍了如何在Windows系统上安装MySQL数据库软件,包括下载、安装、配置和设置环境变量的步骤... 目录mysql的安装图文1.python访问网址2javascript.点击3.进入Downloads向下滑动4.选择Community Server5.

CentOS系统Maven安装教程分享

《CentOS系统Maven安装教程分享》本文介绍了如何在CentOS系统中安装Maven,并提供了一个简单的实际应用案例,安装Maven需要先安装Java和设置环境变量,Maven可以自动管理项目的... 目录准备工作下载并安装Maven常见问题及解决方法实际应用案例总结Maven是一个流行的项目管理工具

本地私有化部署DeepSeek模型的详细教程

《本地私有化部署DeepSeek模型的详细教程》DeepSeek模型是一种强大的语言模型,本地私有化部署可以让用户在自己的环境中安全、高效地使用该模型,避免数据传输到外部带来的安全风险,同时也能根据自... 目录一、引言二、环境准备(一)硬件要求(二)软件要求(三)创建虚拟环境三、安装依赖库四、获取 Dee

MySql9.1.0安装详细教程(最新推荐)

《MySql9.1.0安装详细教程(最新推荐)》MySQL是一个流行的关系型数据库管理系统,支持多线程和多种数据库连接途径,能够处理上千万条记录的大型数据库,本文介绍MySql9.1.0安装详细教程,... 目录mysql介绍:一、下载 Mysql 安装文件二、Mysql 安装教程三、环境配置1.右击此电脑