cocs2d-x 如何用ff14魂晶计时器器调用另外一个cpp文件里的 void x::x(float fDelta)?

我们将使用cocos2d-x制作一个简单的射击游戏。
本文所用的制作环境是cocos2d x 2.0.4,VS2010。
参考资料:
1、本文的原型其实是来自cocos2d-2.0-x-2.0.4官方下载的安装软件,解压缩后可以看到该例子,即在根目录下的Samples示例文件夹里面的SimpleGame。
2、博客无幻的SimpleGame制作的文章,这里是传送门:&&
我这里只是把他们横版的游戏变成竖版,取消白色游戏背景改为用图片背景,而图片素材则换成微信飞机的图片。将原本走掉1个就结束的游戏规则改为如果超过3个敌机跑到屏幕下方里,则游戏结束。飞机的速度和显示时间也稍做了调整。此外本文修改了以上两文中子弹路径算法的一个细微的小错漏,详情请看下文关于子弹路径的算法。
下面我们直接进入主题。制作方法如下:&
1、创建工程。新建Cocos2d-win32工程,工程名为&Star Fighter&,去除&Box2D&选项(该项目不需要用),勾选&Simple Audio Engine in Cocos Denshion&选项;
2、编译运行,查看HelloWorld是否正常显示。可以看到如下图所示:
3、下载本游戏所需的资源,将资源放置&Resources&目录下;
4、设置游戏屏幕尺寸。
打开main.cpp文件设置分辨率,
找到&setFrameSize函数把其括号里的数据,设置成你要的分辨率,用于开发。
市场上装有Andriod系统的手机分辨率常用的主要为240*320、320*480、480*800三种。
修改后的main.cpp文件代码为:
eglView-&setFrameSize(320, 480); //本文采用(320, 480);像素开发。
5、设置游戏背景,最简单的方法是将原本HelloWorldScene.cpp里面的HelloWorld.png文件名,直接替换为我们的背景图片bg_01.jpg。
我们可以看到修改后的代码为:
// 添加背景Add a splash screen, show the cocos2d splash image.
CCSprite* pSprite = CCSprite::create(&bg_01.jpg&);
CC_BREAK_IF(! pSprite);
// 设置坐标位置Place the sprite on the center of the screen
pSprite-&setPosition(ccp(size.width/2, size.height/2));
// 添加到图层显示出来Add the sprite to HelloWorld layer as a child layer.
this-&addChild(pSprite, 0);
cocos2d中图片的添加也是通过添加精灵实现的,由这里我们学会了cocos2d添加精灵的方法。
6、添加显示游戏名字。最简单的方法是将原本HelloWorldScene.cpp里面显示的Hello World文字,直接替换为我们的&Star Fighter&,及修改字体的大小。
添加游戏名字 Add a label shows &Star Fighter&.
// Create a label and initialize with string &Hello World&.
CCLabelTTF* pLabel = CCLabelTTF::create(&Star Fighter&, &Arial&, 36);
CC_BREAK_IF(! pLabel);
// Get window size and place the label upper.
CCSize size = CCDirector::sharedDirector()-&getWinSize();
pLabel-&setPosition(ccp(size.width / 2, size.height - 50));
// Add the label to HelloWorld layer as a child layer.
this-&addChild(pLabel, 1);//第二个数字是渲染的顺序
7、添加玩家player,让玩家位于下方屏幕的中间,在刚刚的HelloWorldScene.cpp文件的init函数里面,(仿照添加背景的代码)添加如下的代码:
//添加玩家player精灵
CCSize winSize = CCDirector::sharedDirector()-&getWinSize();
CCSprite *player = CCSprite::create(&player.png&);
player-&setPosition(ccp(winSize.width / 2, 50));//设置坐标位置,注意(x,y)的值
this-&addChild(player, 2);
8、把HelloWorldScene.cpp文件的init函数里面,我们不需要用到的以下这段删掉。
&&& // add a &close& icon to exit the progress. it's an autorelease object
&&& CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &CloseNormal.png&,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &CloseSelected.png&,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& this,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& menu_selector(HelloWorld::menuCloseCallback));
&pCloseItem-&setPosition(ccp(origin.x + visibleSize.width - pCloseItem-&getContentSize().width/2 ,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& origin.y + pCloseItem-&getContentSize().height/2));
&&& // create menu, it's an autorelease object
&&& CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
&&& pMenu-&setPosition(CCPointZero);
&&& this-&addChild(pMenu, 1);
9、运行编译程序,可以看到以下画面。
10.接下来添加敌机,并且让它可以移动。我们在屏幕上方中间创建,建立动作让它们向下移动。
我们在HelloWorldScene.h头文件中添加函数声明void addMonster();
然后在HelloWorldScene.cpp中增加addMonster方法,代码如下:
void HelloWorld::addMonster()
CCSprite *monster = CCSprite::create(&Monster1.png&);
//使敌人敌机从上面出现时,左右显示是完整的,并在屏幕上方随机产生
//如果其他游戏敌人是Y轴出现,可以把以下的X统一改为Y即可
CCSize winSize = CCDirector::sharedDirector()-&getWinSize();
int minX = monster-&getContentSize().width / 2;
int maxX = winSize.width - monster-&getContentSize().width / 2;
int rangeX = maxX - minX;
int actualX = (rand() % rangeX) + minX;//随机位置产生敌人
//设置敌机坐标 并显示出来
monster-&setPosition(ccp(actualX,winSize.height + monster-&getContentSize().height / 2));
this-&addChild(monster);
//使用随机函数控制飞机速度。改下面数字可以调整飞机速度,控制游戏难度,当然敌机、子弹的触碰区域大小等也会影响游戏难度。
int minDuration = 2.0;
int maxDuration = 5.0;
int rangeDuration = maxDuration - minD
int actualDuration = (rand() % rangeDuration) + minD
//控制敌机的结束坐标,即当整个飞机离开屏幕(注意默认锚点是图片的中心位置)
CCMoveTo *actionMove = CCMoveTo::create(actualDuration, ccp(actualX,-monster-&getContentSize().height / 2));
CCCallFuncN *actionMoveDone = CCCallFuncN::create(this, callfuncN_selector(HelloWorld::spriteMoveFinished));
monster-&runAction(CCSequence::create(actionMove, actionMoveDone, NULL));
在屏幕上方以随机的位置添加敌机精灵,注意计算精灵X轴的位置坐标(因为默认描点在中心,不能让敌机截断只有一部分)。
然后通过随机函数控制的敌机运动总时间,让敌机从上方移动到下方,
移动到离开边界后,调用回调函数spriteMoveFinished,进行删除精灵对象。
这里调用的回调函数,我们尚未对其进行声明和定义。
下面我们先在HelloWorldScene.h头文件中添加函数声明void spriteMoveFinished(CCNode *sender);
然后在HelloWorldScene.cpp中增加函数spriteMoveFinished的定义,代码如下:
void&HelloWorld::spriteMoveFinished(CCNode&*sender)& //回调函数spriteMoveFinished,进行删除精灵对象
&&&&CCSprite&*sprite&=&(CCSprite*)
&&&&this-&removeChild(sprite,&true);
接下去就是安装定时器从而定时创建敌机。我们在bool HelloWorld::init()函数的添加玩家player精灵的代码后面,安装定时器,每秒执行一次,代码如下:
this-&schedule(schedule_selector(HelloWorld::gameLogic),&1.0);
增加gameLogic方法函数,在HelloWorldScene.h头文件中添加函数声明void gameLogic( float dt );
在HelloWorldScene.cpp中增加代码如下:
void&HelloWorld::gameLogic(&float&dt&)
&&&&this-&addMonster();
编译运行,可以看到屏幕上方敌机定时增加,并且以不同的速度向下移动,如下图所示:
11.接着让玩家可以发射子弹。
当用户在屏幕点击时,就让玩家往点击的方向进行发射子弹。
用户的屏幕点击点并不是子弹移动的最终地,借用原文的一张图片来说明:
首先,我们用引擎的函数得到触摸点与子弹初始位置之差我们命名为(offset.x,offset.y)即上图的 (offx,offy)。用户的屏幕点击点并不是子弹移动的最终地,假设如果没有发生碰撞,子弹可以移动到屏幕的尽头直到子弹完全消失,我们将该点命名为(realX,realY)。
当用户点击屏幕后,我们创建子弹精灵,算出触摸点与子弹初始位置之差(offset.x,offset.y)。若触摸点(offset.y&0)在初始位置的上方(即玩家上方),则添加子弹到层上。以等比例原理方法,计算出子弹飞向屏幕上边的最终坐标。然后再用勾股定理计算飞行长度,假定速度为每秒480像素,则计算出飞行总时间。之后就是让子弹执行给定的飞行动作,以及之后的删除自身调用。
要让层可以支持触摸,需要在init方法里面添加如下代码:
this-&setTouchEnabled(true);
然后重载ccTouchesEnded方法。在HelloWorldScene.h头文件中添加函数声明virtual void ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
在HelloWorldScene.cpp中添加代码如下:
void HelloWorld::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
CCTouch *touch = (CCTouch*)pTouches-&anyObject();
CCPoint location = this-&convertTouchToNodeSpace(touch);
CCSize winSize = CCDirector::sharedDirector()-&getWinSize();
CCSprite *projectile = CCSprite::create(&zd.png&);
projectile-&setPosition(ccp(winSize.width / 2, 50));
CCPoint offset = ccpSub(location, projectile-&getPosition());
//关于子弹移动轨迹,主要是用了数学的勾股定理,平行线等分线段定理 CE:CB=DE:AB=CD:AC。
//若触摸点(offset.y&0)在初始位置的上方(即玩家上方),则添加子弹到层上.
if (offset.y &= 0)
this-&addChild(projectile);
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect(&shoot.mp3&);
int realY = winSize.height + projectile-&getContentSize().height / 2;
//这里是子弹的消失点,也是理解该段的重点,即子弹如果没有碰撞则会在屏幕的尽头消失。
float ratio = (float)offset.x/ (float)offset.y;
//offset.x,offset.y是上面CCPoint offset = ccpSub(location, projectile-&getPosition());电脑算出来的。
int realX = (realY - projectile-&getPosition().y) * ratio + projectile-&getPosition().x;//利用等比例原理算出了realY的位置。
//原文该行(realX - projectile-&getPosition().x) 直接写成了realX 错误导致两侧的子弹会跟鼠标稍有偏差,但偏差不大不仔细看不出来。
CCPoint realDest = ccp(realX, realY);//算出了预计子弹的消失点
//通过子弹经过的路径长度,等比例地控制子弹的运行时间
int offRealX = realX - projectile-&getPosition().x;
int offRealY = realY - projectile-&getPosition().y;
float length = sqrtf(offRealX * offRealX + offRealY * offRealY);
float velocity = 480 / 1;//修改480该数字可以调整子弹运行的快慢
float realMoveDuration = length /
projectile-&runAction(CCSequence::create(CCMoveTo::create(realMoveDuration, realDest),
CCCallFuncN::create(this, callfuncN_selector(HelloWorld::spriteMoveFinished)), NULL));
//如果横板游戏子弹发射点是x轴左侧的,就用该段。
//if (offset.x &= 0)
//this-&addChild(projectile);
//int realX = winSize.width + projectile-&getContentSize().width / 2; //这里是子弹的消失点,也是理解该段的重点,即子弹如果没有碰撞则会在屏幕的尽头消失。
//float ratio = (float)offset.y / (float)offset.x;//offset.x,offset.y是上面CCPoint offset = ccpSub(location, projectile-&getPosition());电脑算出来的。
//int realY = (realX - projectile-&getPosition().x) * ratio + projectile-&getPosition().y;//利用等比例原理算出了realY的位置。
////原文该行(realX - projectile-&getPosition().x) 直接写成了realX 错误 两侧的子弹会跟鼠标稍有偏差,偏差不大不仔细看不出来。
//CCPoint realDest = ccp(realX, realY);//算出了预计子弹的消失点
////通过子弹经过的路径长度,等比例地控制子弹的运行时间
//int offRealX = realX - projectile-&getPosition().x;
//int offRealY = realY - projectile-&getPosition().y;
//float length = sqrtf(offRealX * offRealX + offRealY * offRealY);
//float velocity = 480 / 1;//修改480该数字可以调整子弹运行的快慢
//float realMoveDuration = length /
//projectile-&runAction(CCSequence::create(CCMoveTo::create(realMoveDuration, realDest),
//CCCallFuncN::create(this, callfuncN_selector(HelloWorld::spriteMoveFinished)), NULL));
编译运行,往屏幕点击,可以看到子弹发射出去。如下图所示。
10.当子弹碰到怪物时,怪物被消灭,子弹消失,实现这些我们需要用碰撞检测。需要在场景中跟踪目标和子弹,在HelloWorldScene.h声明如下:
void update(float dt);
cocos2d::CCArray&*_
cocos2d::CCArray&*_
在HelloWorldScene.cpp构造函数和析构函数,添加如下:
HelloWorld::HelloWorld()
&&&&_monsters&=&NULL;
&&&&_projectiles&=&NULL;
HelloWorld::~HelloWorld()
&&&&if&(_monsters)
&&&&&&&&_monsters-&release();
&&&&&&&&_monsters&=&NULL;
&&&&if&(_projectiles)
&&&&&&&&_projectiles-&release();
&&&&&&&&_projectiles&=&NULL;
然后在init函数中初始化这两个数组:
this-&_monsters&=&CCArray::create();
this-&_monsters-&retain();
this-&_projectiles&=&CCArray::create();
this-&_projectiles-&retain();
修改addMonster函数,为怪物精灵添加标签,并加入到数组,代码如下:
monster-&setTag(1);
_monsters-&addObject(monster);
修改ccTouchesEnded函数,为子弹精灵添加标签,并加入到数组,代码如下:
projectile-&setTag(2);
_projectiles-&addObject(projectile);
然后修改spriteMoveFinished函数,增加如下代码:
if&(sprite-&getTag()&==&1)
&&&&_monsters-&removeObject(sprite);
else&if&(sprite-&getTag()&==&2)
&&&&_projectiles-&removeObject(sprite);
添加如下方法:
void&HelloWorld::update(float&dt)
&&&&CCArray&*projectilesToDelete&=&CCArray::create();
&&&&CCObject&*pObject&=&NULL;
&&&&CCObject&*pObject2&=&NULL;&
&&&&CCARRAY_FOREACH(_projectiles,&pObject)
&&&&&&&&CCSprite&*projectile&=&(CCSprite*)pO
&&&&&&&&CCArray&*monstersToDelete&=&CCArray::create();
&&&&&&&&CCARRAY_FOREACH(_monsters,&pObject2)
&&&&&&&&&&&&CCSprite&*monster&=&(CCSprite*)pObject2;
&&&&&&&&&&&&if&(CCRect::CCRectIntersectsRect(projectile-&boundingBox(),&monster-&boundingBox()))
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&monstersToDelete-&addObject(monster);
&&&&&&&&&&&&}&&&&&&&&&&&
&&&&&&&&CCARRAY_FOREACH(monstersToDelete,&pObject2)
&&&&&&&&&&&&CCSprite&*monster&=&(CCSprite*)pObject2;
&&&&&&&&&&&&_monsters-&removeObject(monster);
&&&&&&&&&&&&this-&removeChild(monster,&true);
&&&&&&&&if&(monstersToDelete-&count()&&&0)
&&&&&&&&&&&&projectilesToDelete-&addObject(projectile);
&&&&&&&&monstersToDelete-&release();
&&&&CCARRAY_FOREACH(projectilesToDelete,&pObject)
&&&&&&&&CCSprite&*projectile&=&(CCSprite*)pO
&&&&&&&&_projectiles-&removeObject(projectile);
&&&&&&&&this-&removeChild(projectile,&true);
&&&&projectilesToDelete-&release();
遍历子弹数组,计算每一个子弹所可能遇到的怪物,用它们各自的边界框进行交叉检测,检测到交叉,则将怪物对象放入ToDelete(待删除)数组,不能在遍历的时候删除一个对象。若是子弹遇到了怪物,也需要放入ToDelete(待删除)数组。然后从场景和数组中移动掉。同样,也在init函数,安装定时器,代码如下:
this-&schedule(schedule_selector(HelloWorld::update));
11.编译运行,这时当子弹和怪物碰撞时,它们就会消失;
12.添加音效,在init函数添加背景音乐,代码如下:
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playBackgroundMusic(&background-music-aac.wav&);
在ccTouchesEnded函数,添加子弹音效,代码如下:
CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect(&shoot.mp3&);
13.接下来,创建一个新的场景,来指示&You Win&或者&You Lose&。右键 工程,&Add&→&Class...&→&C++&→&Add&,&Base class&为CCLayerColor,&Class name&为GameOverLayer,如下图所示:
GameOverLayer.h文件代码为:
#pragma&once
#include&&cocos2d.h&
class&GameOverLayer&:
&&&&public&cocos2d::CCLayerColor
&&&&GameOverLayer(void);
&&&&~GameOverLayer(void);
&&&&bool&initWithWon(bool&won);
&&&&static&cocos2d::CCScene*&sceneWithWon(bool&won);
&&&&static&GameOverLayer*&createWithWon(bool&won);
&&&&void&gameOverDone();
GameOverLayer.cpp文件代码为:
#include&&GameOverLayer.h&
#include&&HelloWorldScene.h&
using&namespace&cocos2d;
GameOverLayer::GameOverLayer(void)
GameOverLayer::~GameOverLayer(void)
GameOverLayer*&GameOverLayer::createWithWon(bool&won)
&&&&GameOverLayer&*pRet&=&new&GameOverLayer();
&&&&if&(pRet&&&&pRet-&initWithWon(won))
&&&&&&&&pRet-&autorelease();
&&&&&&&&return&pR
&&&&&&&&CC_SAFE_DELETE(pRet);
&&&&&&&&return&NULL;
bool&GameOverLayer::initWithWon(bool&won)
&&&&bool&bRet&=&false;
&&&&&&&&CC_BREAK_IF(!&CCLayerColor::initWithColor(ccc4(<span style="color:#ff,&<span style="color:#ff,&<span style="color:#ff,&<span style="color:#ff)));//设置屏幕为白色
&&&&&&&&char&*
&&&&&&&&if&(won)
&&&&&&&&&&&&message&=&&You&Won!&;//显示你赢了
&&&&&&&&}&
&&&&&&&&else
&&&&&&&&&&&&message&=&&You&Lose&:[&;//显示你输了
&&&&&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
&&&&&&&&CCLabelTTF&*label&=&CCLabelTTF::create(message,&&Arial&,&32);//控制字体大小
&&&&&&&&label-&setColor(ccc3(0,&0,&0));//控制字体颜色
&&&&&&&&label-&setPosition(ccp(winSize.width&/&2,&winSize.height&/&2));//设置坐标位置
&&&&&&&&this-&addChild(label);//添加到屏幕
&&&&&&&//场景上的层显示一个文本,在3秒之后调用gameOverDone函数从而返回到HelloWorld场景中
&&&&&&&&this-&runAction(CCSequence::create(CCDelayTime::create(3),&
&&&&&&&&&&&&CCCallFunc::create(this,&callfunc_selector(GameOverLayer::gameOverDone)),
&&&&&&&&&&&&NULL));
&&&&&&&&bRet&=&true;
&&&&}&while&(0);
&&&&return&bR
cocos2d::CCScene*&GameOverLayer::sceneWithWon(bool&won)
&&&&CCScene&*&scene&=&NULL;
&&&&&&&&scene&=&CCScene::create();
&&&&&&&&CC_BREAK_IF(!&scene);
&&&&&&&&GameOverLayer&*layer&=&GameOverLayer::createWithWon(won);
&&&&&&&&CC_BREAK_IF(!&layer);
&&&&&&&&scene-&addChild(layer);
&&&&}&while&(0);
&&&&return&
//gameOverDone()函数控制场景,跳转回到HelloWorld游戏场景
void&GameOverLayer::gameOverDone()
&&&&CCDirector::sharedDirector()-&replaceScene(HelloWorld::scene());
游戏结束时,切换到以上所建的场景,场景上的层显示一个文本,在3秒之后返回到HelloWorld场景中。
14.最后,为游戏添加一些游戏逻辑。
我们将规则定位:当放走3个敌机后,则游戏结束。而如果游戏结束时,被消灭敌机数量超过30个,则判断为胜利,反之为失败。
记录玩家消灭怪物的数量,进而决定该玩家输赢。在HelloWorldScene.h文件中,添加如下:
int&_monstersD
在HelloWorldScene.cpp文件,HelloWorld()构造函数,添加如下代码:
_monstersDestroyed&=&0;
添加头文件引用:
#include&&GameOverLayer.h&
在update定时函数中,monstersToDelete循环removeChild(monster, true)的后面添加被消灭怪物的计数,并判断胜利条件,代码如下:
_monstersDestroyed&#43;&#43;; //计算消灭的敌机数量
if&(_monstersDestroyed&&&30)//当放走3个敌机后游戏结束时,如果敌机数量超过30个,则判断为胜利,反之为失败。
&&&&CCScene&*gameOverScene&=&GameOverLayer::sceneWithWon(true);
&&&&CCDirector::sharedDirector()-&replaceScene(gameOverScene);
最后为玩家添加游戏结束条件,规定如果超过3个敌机跑到屏幕下方里,则游戏结束。
在HelloWorldScene.h文件中,添加如下:
在HelloWorldScene.cpp文件中的spriteMoveFinished函数里,sprite-&getTag() == 1条件的后面,添加如下:
//统计放跑敌机的数量,如果超过3个则游戏结束
if(_life&=3)
CCScene *gameOverScene = GameOverLayer::sceneWithWon(false);
CCDirector::sharedDirector()-&replaceScene(gameOverScene);
最后编译并运行,即可看到游戏。到此已完成了一个简单的游戏,包含音效,并带有胜利和失败的结束。
本例子源代码附加资源下载地址:
可以运行压缩文件夹Star Fighter源码\Debug.win32里的exe文件运行查看游戏效果。
如文章存在错误之处,欢迎指出,以便改正。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:41012次
积分:1527
积分:1527
排名:第11420名
原创:116篇
转载:11篇
评论:18条游戏截图:
  &是一个支持多平台的开源框架,用于构建游戏、应用程序和其他图形界面交互应用。Cocos2d-x项目可以很容易地建立和运行在iOS,Android的三星Bada,黑莓Blackberry操作系统。Cocos2d-x还支持Windows、Mac和Linux等桌面操作系统,因此,我们可以编写的源代码很容易在桌面操作系统中编辑和调试。
cocos2d-x 具有以下特称:
  目前支持精灵(sprite)、动画、物理引擎、声音引擎以及许许多多非常酷的图像效果等等。
  目前已经有许多非常好的教程来教你如何使用cocos2d-x了。但关于cocos2d-x 3.0的资料非常少。同时很多人真正想要的是一个非常简单,但是可以跑起来的游戏。这个游戏包括怎么使用动画、碰撞检测和播放声音,这就够了,并不需要使用太多高级的特性。
  这篇教程将会从头至尾、一步一步地教你如何使用cocos2d来制作一个简单的iphone游戏。你可以按照教程一步步来,或者干脆直接跳到文章的最后,下载样例程序。没错!游戏里面有忍者。
Cocos2D-x!
你可以从来下载cocos2d-x的最新版本并解压
打开终端,进入cocos2d-x目录下的tools/project-creator,执行命令./create_project.py
-n [项目名] -k [包名] -l [使用语言cpp或javascript等],然后就能在cocos2d-x目录下的projects中看到新生成的项目了。或者直接执行./create_project.py会有图形界面,直接填写.
//sample1:
$ cd cocos2d-x-3.0beta/tools/project-creator/
# 用python运行脚本进入GUI界面,效果如下图
$ python create_project.py
//sample2:
$ cd cocos2d-x-3.0beta/tools/project-creator/
# 查询命令行的Usage,然后就可以自己创建了
$ python create_project.py --help
# 用命令行创建与sample1等同的工程
$ ./project-creator.py -n SampleGame -k com.MyCompany.SampleGame -l cpp -p /Cocos/cocos2d-x-3.0beta/projects
点击项目,proj.ios_mac/目录,直接双击打开SampleGame1.xcodeproj会调用Xcode打开项目.
点编译并运行这个工程,如何一切都ok的话,那么你会看到下面的内容:
  cocos2d是按照“场景”(scene)的概念组织的,对一个游戏来说,就好像某个关卡或者屏幕之类的。比如,你可能需要一个场景来为你的游戏建立初使化菜单界面,另外一个场景当作玩游戏的主要界面,还有一个游戏结束的时候的界面。在一个场景里面,你可以有许多“层”(layer)(这个和photoshop有点类&#20284;)。每一个层又可以包含一些结点,比如精灵、标签、菜单等。而且一个结点也可以包含其它的结点。(比如,一个精灵可以包含一个子精灵)
  如果你看一下样例工程,你会看到只有一个场景HelloWorldScene--我们接下来将会在这个场景里面实现我们的游戏逻辑。继续打开它,你会在init方法里面看到,从关闭按钮也就是声明closeItem变量那里到return语句之前全部删掉。我们将把这些代码去掉,并且放一个自己的精灵在上面。
增加一个精灵
  在我们增加一个精灵之前,我们需要一些图片。你可以自己去创建一些,或者使用原英文作者的夫人这个项目所创建的图片:&,&,&.如果上面的链接不幸失效的话,后面我会把源码放出,在我给出的工程里的Resources文件夹直接拷贝吧。
  一旦你获得了这些图片,你就可以把它们用鼠标拖到xcode的resource文件夹中去,并且保证“ Copy items into destination group’s folder (if needed)”勾上了。现在,我们拥有自己的图片了,我们先得计算一下,应该把player放在哪里。请注意,cocos2d-x坐标系的原点在屏幕的左下角,即(0,0)位置在左下角,这与一般的窗口坐标系统在左上角有所区别。x方向往右是正,y方向往上是正。由于项目是landscape(横版)模式,这就意味着右上角的坐标是(960,640)。
  还有需要注意的是,当我们设置一个对象的位置的时候,这个位置是相对于所加精灵的中心点来加的。因此,如果我们想让我们的player精灵与屏幕的左边界水平对齐,并且垂直居中的话。
那么对于x轴:我们需要设置为[player sprite's width]/2。y坐标设置为[window height]/2。
  下面有一张图,可以更加清楚一些:
好,让他开始射击吧!打开Class文件夹并点击HelloWorldScene.cpp,然后在init方法用下面的代码替换掉:
bool HelloWorld::init()
if ( !Layer::init() )
Size visibleSize = Director::getInstance()-&getVisibleSize();
//创建精灵
auto player = Sprite::create(&Player.png&, Rect(0, 0, 27, 40));
player-&setPosition(Point(player-&getContentSize().width/2,
visibleSize.height / 2));
this-&addChild(player, 0);
  编译并运行,你的精灵这时候应该出现在正确的位置上了。但是,这个前景默认是黑的。对于这张忍者图片来说,白色的背景可能看起来更好一些。在cocos2d-x里面,有一种非常简单的方式来改变层的背景颜色,那就是使用LayerColor类。好,跟我来吧!点击HelloWorldScene.h,然后把HelloWorld类继承声明改成下面这样:
class HelloWorld : public cocos2d::LayerColor
  然后点击HelloWorldScene.cpp文件,在init方法里面做一些小小的修改,以便使我们能把层的背景颜色改成白的:
if ( !LayerColor::initWithColor(Color4B(255,255,255,255)) )
  编译并运行,这时你将看到你的精灵是在一个白色的背景上面了。哈哈,我们的忍者看起来整装待发呢!
  接下来,我们想增加一些目标怪物来与我们的忍者战斗。为了使事情变得更加有趣,我想让这些目标可以移动--实际上这也并不是很难!因此,让我们先在屏幕的右边靠外一点点创建一些目标,然后设置一个action,并使之从右边移动到左边。
  接下来,紧接着上面init函数,添加下面代码:
void HelloWorld::addTarget()
Size visibleSize = Director::getInstance()-&getVisibleSize();
auto target = Sprite::create(&Target.png&, Rect(0, 0, 27, 40));
// Determine where to spawn the target along the Y axis
int minY = target-&getContentSize().height / 2;
int maxY = visibleSize.height - target-&getContentSize().height / 2;
int rangeY = maxY - minY;
int actualY = (CCRANDOM_0_1() * rangeY) &#43; minY;
target-&setPosition(Point(visibleSize.width &#43; target-&getContentSize().width / 2, actualY));
this-&addChild(target,0);
// Create the target slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
int minDuration = 2.0;
int maxDuration = 4.0;
int rangeDuration = maxDuration - minD
int actualDuration = (CCRANDOM_0_1() * rangeDuration) &#43; minD
// Determine speed of the target
auto actionMove = MoveTo::create(actualDuration,
Point(-target-&getContentSize().width, actualY));
auto actionMoveDone = CallFuncN::create(
CC_CALLBACK_1(HelloWorld::spriteMoveFinished, this));
target-&runAction(Sequence::create(actionMove, actionMoveDone, NULL));
  在这里我将以一种非常啰嗦的形式来介绍,目的是方便大家理解。第一部分需要解释的是我们之前已经讨论过了的:我们做一些简单的计算来决定把对象放在什么位置,然后设置对象的position,然后并把它加在场景上面,就和加载player精灵一样。
  这里增加的新的元素就是actions。cocos2d-x里面提供了许多非常方便的内置的action,你可以使用这样action来让你的精灵动起来。比如move action,jump action,fade action,animation action(就是播放图片序列)等等。这里,我们对目标对象使用了3种类型的action:
MoveTo: 我们使用MoveTo action让目标从屏幕右边一直往左移动,直到移出屏幕。注意,这里我们可以指定这个过程要花费多长时间。这里使用了变化的时间间隔2-4秒。
CallFuncN: 它可以让你为某个执行此action的对象指定一个回调函数。我们指定的回调函数是:spriteMoveFinished---目前并没有,到后面会具体给了来。其中CC_CALLBACK_1宏是将函数与对象绑定在一起,1表示这个函数有一个参数。
Sequence: 它允许我们把一系列的action组成一个action序列,并且这些acton可以按顺序执行。一次执行完所有的action。在上面的例子中,我们让对象首先执行MoveTo,等MoveTo完成后,马上就会执行CallFuncN action。 接下来, 为CallFuncN action增加一个回调函数。你可以在addTarget前面增加下面的代码:
void HelloWorld::spriteMoveFinished(Object* pSender)
Sprite *sprite = (Sprite *)pS
this-&removeChild(sprite);
  这个函数的目的是当精灵飞出屏幕之后,需要移除出当前的scene。这个非常重要,这样的话我们就不会因为屏幕外面积累太多没有用到的精灵而造成内存泄漏。注意,其实还有其它更好的方式来解决这个问题,比如使用一组可以重用的精灵等。不过,对于初学者来说,我在这篇教程里,尽量简单化。
  在我们继续之前,还有最后一件事没做。我们需要调用这个方法来创建我们的目标怪物。而且,为了使事情变得更加有趣,我们会随着时间连续不断地发射一些怪物出来。我们可以使用cocos2d-x的定时scheduler,并指定一个回调函数来完成此功能。一秒钟调用一次回调函数就可以了。因此,在init函数返回之前,我们再加入下面的代码:
this-&schedule(schedule_selector(HelloWorld::gameLogic), 1.0f);
然后简单的实现一下这个回调函数,如下:
void HelloWorld::gameLogic(float dt)
addTarget();
就这么多!现在编译再运行一下工作,你可以看到怪物在屏幕上面happy地移动了!
  在这里,我们的忍者需要有一些行动了--因此让我们增加一些射击吧!这里有许许多多实现射击的方式,但是在这个游戏里面,我们想让用户触摸一下屏幕,然后飞盘就会从player开始,沿着你触摸的位置发射出来。
  我们使用MoveTo action来实现这个功能。但是,为了使用这个功能,我们必须首先来做一些数学题。这是因为,MoveTo需要我们为飞盘指定目的地。但是我们又不能使用触摸点,因为触摸点仅仅代表飞盘飞的方向。我们实际上想让子弹超过触摸点,然后飞出屏幕之外去。
  下面这张图解释了这个问题:
  因此,就像你看到的,在触摸点和player之间有一个小的三角形,由origin点,offx和offy组成。我们只需要画一个更大的三角形,同时使用一样的比率就行了。然后我们就可以根据比例算出飞盘飞出屏幕的位置。
  好了,让我们看看代码怎么写。首先我们需要让layer能接收touch事件。cocos2d-x 3.0增加了新的事件分发机制,并且让setTouchEnabled为deprecated的方法。对某个方法和类标注deprecated的意思就是这个方法或类不再建议使用。所以我们继承虚函数onEnter,并重写:
void HelloWorld::onEnter()
LayerColor::onEnter();
auto listener = EventListenerTouchOneByOne::create();
listener-&setSwallowTouches(true);
listener-&onTouchBegan = [=](cocos2d::Touch* touch,cocos2d::Event* event)
listener-&onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
_eventDispatcher-&addEventListenerWithSceneGraphPriority(listener, this);
事件监听器包含以下几种:
触摸事件 (EventListenerTouch)
键盘响应事件 (EventListenerKeyboard)
加速记录事件 (EventListenerAcceleration)
鼠标响应事件 (EventListenerMouse)
自定义事件 (EventListenerCustom)
  其中的触摸监听类型触摸事件分为EventListenerTouchOneByOne和EventListenerTouchAllAtOnce。EventListenerTouchOneByOne 表示的是单点触摸;而EventListenerTouchAllAtOnce 表示的就是多点触摸。
  而我们以前的ccTouchBegan,ccTouchEnd,ccTouchMoved等方法都已经deprecated了。取而代之的是让监听器绑定事件处理函数。上面绑定的onTouchEnded我在后面马上会添加进去。onTouchEnded响应的是触摸点击结束事件。onTouchMove没有写出来,反正本游戏用不着。至于哪个看起来高端大气得onTouchBegan,其实是为了用一下3.0引入了C&#43;&#43;11得特征,哪个东西在C&#43;&#43;11里学名叫Lambda函数,其实就是匿名函数。跟onTouchEnded那种绑定个类函数得写法效果一样。主要是这里必须写onTouchBegan并且返回true,表示监听器已经接收到了。
  监听器创建完成后我们把它绑定给_eventDispatcher事件分发器,_eventDispatcher 是 Node 的属性,通过它管理当前节点(如 场景 、层、精灵等 )的所有事件分发情况。
在HelloWorldScene.h添加声明:
void onTouchEnded(cocos2d::Touch* touch, cocos2d::Event* event);
在HelloWorldScene.cpp添加函数定义:  
void HelloWorld::onTouchEnded(Touch* touch, Event* event)
Size visibleSize = Director::getInstance()-&getVisibleSize();
auto touchPoint = touch-&getLocation();
auto projectile = Sprite::create(&Projectile.png&, Rect(0, 0, 20, 20));
projectile-&setPosition(Point(20, visibleSize.height / 2));
// Determine offset of location to projectile
int offX = touchPoint.x - projectile-&getPosition().x;
int offY = touchPoint.y - projectile-&getPosition().y;
// Bail out if we are shooting down or backwards
if (offX &= 0)
// Ok to add now - we've double checked position
this-&addChild(projectile);
// Determine where we wish to shoot the projectile to
int realX = visibleSize.width &#43; (projectile-&getContentSize().width / 2);
float ratio = (float)offY / (float)offX;
int realY = (realX * ratio) &#43; projectile-&getPosition().y;
auto realDest = Point(realX, realY);
// Determine the length of how far we're shooting
int offRealX = realX - projectile-&getPosition().x;
int offRealY = realY - projectile-&getPosition().y;
float length = sqrtf((offRealX*offRealX) &#43; (offRealY*offRealY));
float velocity = 960 / 1;
// 960pixels/1sec
float realMoveDuration = length /
// Move projectile to actual endpoint
projectile-&runAction(
Sequence::create(MoveTo::create(realMoveDuration, realDest),
CallFuncN::create(CC_CALLBACK_1(HelloWorld::spriteMoveFinished, this)),
  在第一部分,我们选择一个touch来处理,获得它在当前view中的位置。
  接下来,我们加载飞盘精灵并且设置它的初始位置。然后,我们计算出它需要飞往何处,使用player和touch之间的向量并且根据前面描述的算法计算出来。
  注意,这个算法并不完美。我们强迫子弹飞出屏幕x轴的外边--即使在它已经飞出屏幕y轴的外边界了。这里有许多方向来解决这个问题,比如检查飞出屏幕的最短距离,或者使用一个游戏回调函数来检查一个飞盘是否飞出,飞出就移出场景。但是,在这里,我们尽量保持简单。
  最后一件事情就是,决定飞盘移动的时间。我们想让子弹以常量速度飞行,不管飞行方向如何。因此,我们不得不再做一点点数学。我们能够使用 Pythagorean Theorem来计算我们移动了多久。记得几何学中,三角形的斜边=两个直角边的平方和再开根号。
  一旦我们得到了距离,我们就可以通过除了速度来得到时间。因为速度=距离/时间。换句话说 时间=距离/速度。
  余下的部分就和设置我们target一样了。编译并运行,现在忍者可以射击侵犯的敌人了!
  现在,我们可以看到飞镖到处乱飞了!但是,我们的忍者真正想做的,是能够放倒一些怪物。好吧,让我们增加一些代码来检测什么时候我们的飞镖与怪物相撞了。
  在cocos2d-x里面,有许多方法可以解决这个问题,包括使用cocos2d-x内置的开源物理引擎box2d和chipmunk。然而,为了使事情变得简单一点,在这里我们自己实现了一个简单的碰撞检测。
  为了实现这个,我们首先需要当前场景中存在的飞镖和怪物。在HelloWorldScene类里面增加下面的声明:
cocos2d::Vector _
cocos2d::Vector _
  现在,我们修改addTarget方法,把一个新的target加到targets数组里面,并且为这个target设置一个tag,以便将来使用:
target-&setTag(1);
_targets.pushBack(target);
  然后,修改onTouchEnded方法,同样的,把新增加的projectile加到projectiles数组里面,并为之设置一个tag供后面使用:
projectile-&setTag(2);
_projectiles.pushBack(projectile);
  最后,修改你的spriteMoveFinished方法,基于tag标签来从正确的数组中移除相应的sprite。
if (sprite-&getTag() == 1) {
_targets.eraseObject(sprite);
} else if(sprite-&getTag() == 2) { // projectile
_projectiles.eraseObject(sprite);
  编译并运行程序,确保一切都ok。目前来说,应该没有什么可见的差别。但是,接下来我们就会去实现真正的碰撞检测了。
  现在,在HelloWorldScene里面增加如下方法:
```cpp  
void HelloWorld::update(float t)
Vector targetsToD
Vector projectilesToD
for (int i = 0; i & _projectiles.size(); i&#43;&#43;)
auto projectile = _projectiles.at(i);
auto projectileRect = Rect(
projectile-&getPositionX() - projectile-&getContentSize().width / 2,
projectile-&getPositionY() - projectile-&getContentSize().height / 2,
projectile-&getContentSize().width,
projectile-&getContentSize().height );
for (int j = 0; j & _targets.size(); j&#43;&#43;)
auto target = _targets.at(j);
auto targetRect = Rect(
target-&getPositionX() - target-&getContentSize().width / 2,
target-&getPositionY() - target-&getContentSize().height / 2,
target-&getContentSize().width,
target-&getContentSize().height);
if (projectileRect.intersectsRect(targetRect))
targetsToDelete.pushBack(target);
//C&#43;&#43;11 的 range-based for循环
for (Sprite* t : targetsToDelete)
_targets.eraseObject(t);
this-&removeChild(t);
if (targetsToDelete.size() &0)
projectilesToDelete.pushBack(projectile);
targetsToDelete.clear();
for (const auto& p : projectilesToDelete)
_projectiles.eraseObject(p);
this-&removeChild(p);
projectilesToDelete.clear();
  上面的代码应该非常清楚。我们仅仅通过遍历projectiles和targets数组,为每个projectile和target创建边界矩形,然后使用intersectsRect来检测碰撞。如果发现有碰撞了,我们就从场景中移除精灵,同时也把它移除出数组。注意,我们不得不添加一个toDelete数组,因为我们不能在遍历一个数组的时候去删除数组中的对象。当然,还有许多方式可以实现类&#20284;的逻辑,我只不过挑选了简单的方法。
  在你真正完成之前,还差最后一件事情。在你的init方法里面调用下面的函数:
this-&scheduleUpdate();
  编译并运行,现在,当你的飞镖和怪物相碰的时候,他们都会消失啦!
完成触摸事件
  我们离制作一个可以玩的游戏(但是非常简单)的目标已经越来越近了。我们仅仅需要增加一些音效和背景音乐(试想哪个游戏没有声音呢!),再增加一点点简单的逻辑就更好了。
  首先,把一些背景音乐和音效拖到工程的resource文件夹中。你可以使用&&或者我的,或者自制一些。如果上面的链接不幸失效的话,后面我会把源码放出,在我给出的工程里的Resources文件夹直接拷贝吧。同时准备了上面音乐的mp3版和wav版,方便大家在其他平台调试。关于cocos2d的Audio的平台差异,参见。上面哪个链接仅仅供参考,因为群众实践后发现windows其实也支持mp3,但上文说windows不支持。
  然后,在HelloWorldScene.cpp文件里导入下面的头文件:
#include &SimpleAudioEngine.h&
  在init函数return之前,添加播放背景音乐的简易代码:
CocosDenshion::SimpleAudioEngine::getInstance()-&playBackgroundMusic(&background-music-aac.caf&);
  然后,在你的ccTouchesEnded方法里面添加音效代码:
CocosDenshion::SimpleAudioEngine::getInstance()-&playEffect(&pew-pew-lei.caf&);
  现在,让我们创建一个新的场景,来作为“You Win”或者“You Lose”的标志。右击Classes文件夹,然后选择New File并选择C&#43;&#43; Class。同时,确保NSObject基类被选中。点击下一步,然后输入GameOverScene作为文件名,同时确保“Also create GameOverScene.h”复选框打上勾。
  然后把GameOverScene.h里面的文件替换成下面的代码:
#include &cocos2d.h&
class GameOverLayer : public cocos2d::LayerColor
GameOverLayer():_label(NULL) {};
virtual ~GameOverLayer();
bool init();
CREATE_FUNC(GameOverLayer);
void gameOverDone();
CC_SYNTHESIZE_READONLY(cocos2d::LabelTTF*, _label, Label);
class GameOverScene : public cocos2d::Scene
GameOverScene():_layer(NULL) {};
~GameOverScene();
bool init();
CREATE_FUNC(GameOverScene);
CC_SYNTHESIZE_READONLY(GameOverLayer*, _layer, Layer);
  上面的声明就是一个场景和一个层,及其他们的构造函数和析构函数。init函数是熟人,gameOverDone后面要用到的事件处理函数。CREATE_FUNC宏用于创建一个静态方法返回自身的一个实例。上面的就对应GameOverScene::create()方法,返回的对象是一个Scene*的对象。下面主要介绍上面出现的另一个东西CC_SYNTHESIZE_READONLY宏。CC_SYNTHESIZE_READONLY(varType, varName, funName)声明一个成员变量以及getfunName函数,没有set函数。简单的举例:
CC_SYNTHESIZE_READONLY(cocos2d::LabelTTF*, _label, Label);
  等价于下面
ocos2d::LabelTTF*
ocos2d::LabelTTF*
getLabel() { return
接下来替换掉GameOverScene.m文件里的内容:
#include &GameOverScene.h&
#include &HelloWorldScene.h&
USING_NS_CC;
bool GameOverScene::init()
if( Scene::init() )
this-&_layer = GameOverLayer::create();
this-&_layer-&retain();
this-&addChild(_layer);
GameOverScene::~GameOverScene()
if (_layer)
_layer-&release();
_layer = NULL;
bool GameOverLayer::init()
if ( LayerColor::initWithColor( Color4B(255,255,255,255) ) )
auto winSize = Director::getInstance()-&getWinSize();
this-&_label = LabelTTF::create(&&,&Artial&, 32);
_label-&retain();
_label-&setColor( Color3B(0, 0, 0) );
_label-&setPosition( Point(winSize.width/2, winSize.height/2) );
this-&addChild(_label);
this-&runAction( Sequence::create(
DelayTime::create(3),
CallFunc::create(CC_CALLBACK_0(GameOverLayer::gameOverDone, this)),
void GameOverLayer::gameOverDone()
Director::getInstance()-&replaceScene( HelloWorld::createScene() );
GameOverLayer::~GameOverLayer()
if (_label)
_label-&release();
_label = NULL;
  注意,这里有两个不同的对象:场景和层。场景可以包含任意数量的层,但是此例中只有一个层。这个层只是在屏幕的中间放置了一个label,然后运行了一个action。这个action的作用就是,等待3秒钟,然后调用一个回调函数切换回HelloWorld场景。
  最后,让我们增加一些基本的游戏逻辑。首先,让我们来追踪player销毁的飞镖projectiles。接下来,在HelloWorld类里面增加一个成员变量,如下所示:
int _projectilesD
  在HelloWorldScene.m里面,导入我们的GameOverScene类:
#include &GameOverScene.h&
  在update方法里,增加(销毁的projectile)计数,同时检测游戏胜利的条件。并在targetsToDelete循环里,紧接着this-&removeChild(target)的地方添加如下代码:
_projectilesDestroyed&#43;&#43;;
if (_projectilesDestroyed &3) {
auto gameOverScene = GameOverScene::create();
gameOverScene-&getLayer()-&getLabel()-&setString(&You Win!&);
Director::getInstance()-&replaceScene(gameOverScene);
  最后,让我们这样设计,只要有一个怪物穿过了屏幕左边,你就输了。修改spriteMoveFinished方法,通过在tag==1里面、_targets.eraseObject(sprite)后面添加下面的代码:
auto gameOverScene = GameOverScene::create();
gameOverScene-&getLayer()-&getLabel()-&setString(&You Lose :[&);
Director::getInstance()-&replaceScene(gameOverScene);
  继续,编译并运行程序。现在,你的游戏可以实现胜利或者失败的场景了!:)
获得源代码
  旁边是本版本教程使用的完整的源代码:。
  要注意的是,上面的链接下载的源码只包含Classs文件和Resources文件,因为cocos2d-x 3.0 beta 支持在任何目录创建工程,用过2.x的都知道,以前只能在cocos2d的安装路径的projects下创建。支持任意路径创建的代价就是在每个工程下都多了一个cocos2d的文件夹,把它打开看,你会神奇的发现它纯粹是cocos2d安装目录里的文件的子集。
何去何从?
  这个项目对于一个cocos2d-x的初学者来说非常有帮助,而且你还可以自己往项目里面添加更多新的特性。或许你可以尝试一下,添加一个提示框,提示当前你已经打中了多少个怪物了。或者你可以增加一些很酷的动画,比如怪物被击中后不是直接消失,而是用一段动画来模拟死去。(可以参考cocs2d-x TestCpp里面的ActionsTest,EffectsTest和EffectsAdvancedTest)。或者你还可以增加更多的图片和声音资源,或者更多的游戏逻辑。心情发挥吧!
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:63675次
积分:1093
积分:1093
排名:第17456名
原创:39篇
转载:40篇
评论:25条
(49)(4)(6)(2)(1)(17)

我要回帖

更多关于 倒计时器 的文章

 

随机推荐