|
学习的第一个例程来自下面连接
http://blog.csdn.net/akof1314/article/details/8268882
系统版本: Mac OS MotainLion 10.8.2+XCode 4.6 4H127+ cocos2d-x 2.04版
1.打开XCODE
新建一个COCOS2D-X工程,如图所示.与在WIN下不同的是 这里没有 Box2D选项
2.编译运行,如图所示:
3.下载游戏所需要的资源,链接如下:
链接来源:
http://www.raywenderlich.com/25736/how-to-make-a-simple-iphone-game-with-cocos2d-2-x-tutorial
资源链接:
http://cdn3.raywenderlich.com/downloads/Cocos2DSimpleGameResourcePack.zip
资源文件有两个文件夹,Art 和 Sounds,将资源拖入工程的 Resources 中
4.现在需要的文件已经准备就绪,开始这个简单的游戏.
首先,游戏需要一个白色背景,使用 CCLayerColor 来实现,将HelloWorldScene.h文件"HelloWorld"类改为如下:
class HelloWorld : public cocos2d::CCLayerColor
并且需要在 HelloWorldScene.cpp 文件的init函数中设置背景色(红色语句):
在变更类为CCLayerColor之后,如果不设置背景,似乎并不能执行下去.
之前在Visual Studio 2010+ cocos2d-x下也生成过这个HELLOWORD程序,在XCODE下重温的时候,在这里发现了不同.
VS中init的代码是包含在 do{} while(0) 这段语句中,但是在XCODE中并没有这部分.按下不表,以后再了解.
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
CCLayerColor::initWithColor(ccc4(255,255,255,255));
return true;
}
继续修改init代码,首先添加玩家:完成后的init 代码如下:
//////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
CCLayerColor::initWithColor(ccc4(255,255,255,255));
//获取屏幕size
CCSize size = CCDirector::sharedDirector()->getWinSize();
//创建一个精灵,加载忍着的图片
CCSprite *player = CCSprite::create("player.png",CCRectMake(0, 0, 27, 70));
//设定精灵的位置为 左边正中间
player->setPosition(ccp(player->getContentSize().width / 2, size.height / 2));
//将精灵加入场景
this->addChild(player);
return true;
编译运行,可以看到忍者已经显示在白色背景下.
接下来继续添加怪物,并且让怪物可以移动.
我们在HelloWorld这个类中添加一个方法addMonster,顺便添加一个spriteMoveFinished方法,待会要用到.
void spriteMoveFinished(CCNode* pSender);
void addMonster();
spriteMoveFinished 方法很简单,就是从场景中删除精灵对象.
void HelloWorld::spriteMoveFinished(CCNode* pSender)
{
CCSprite *sprite = (CCSprite*)pSender;
this->removeChild(sprite, true);
}
现在继续写addMonster方法代码,完整的代码如下:
void HelloWorld::addMonster()
{
CCSprite *monster = CCSprite::create("monster.png");
CCSize size = CCDirector::sharedDirector()->getWinSize();
//计算可以显示在屏幕上最上方和最下方的位置
int minY = monster->getContentSize().height / 2;
int maxY = size.height - monster->getContentSize().height / 2;
int rangeY = maxY - minY;
//随机一个出现的位置
int actualY = (rand() % rangeY) + minY;
//设置精灵的位置
monster->setPosition(ccp(size.width + monster->getContentSize().width / 2, actualY));
//将精灵加入场景
this->addChild(monster);
//设定怪物移动速度的最小最大数值,并且在其中取值
int minDuration = 2;
int maxDuration = 4;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (rand() % rangeDuration) + minDuration;
//精灵的移动方式,从右到左移动.
CCMoveTo *actionMove = CCMoveTo::create(actualDuration, ccp(-monster->getContentSize().width / 2, actualY));
//精灵移动完成之后的回调函数,用 CCCallFuncN 定义, 这里是调用之前写的类方法 spriteMoveFinished 删除精灵
CCCallFuncN *actionMoveDone = CCCallFuncN::create(this, callfuncN_selector(HelloWorld::spriteMoveFinished));
//精灵应用动作
monster->runAction(CCSequence::create(actionMove, actionMoveDone, NULL));
}
CCSequence相当于一个队列,它会按顺序执行里面的所有action,最后一个加NULL。表示到这里就没有了。
接下来就是定时创建怪物,并且每秒都执行一次,即增加一个怪物.
首先还是先增加一个 gameLogic 方法
void HelloWorld::gameLogic(float dt)
{
this->addMonster();
}
然后在 init 函数返回之前,安装定时器,
this->schedule(schedule_selector(HelloWorld::gameLogic), 1.0);
保存修改,现在运行试试,可以看到怪物在增加,并且以不同的速度从右到左移动.
接着,让忍着可以发射飞镖,当用户在屏幕点击时,让玩家往点击的方向发射飞镖.
首先,要让层支持触摸,得在 init 中添加如下代码
this->setTouchEnabled(true);
然后重载 ccTouchesEnabled 方法
virtual void ccTouchesEnded(cocos2d::CCSet* pTouches, cocos2d::CCEvent * pEvent);
方法实现代码:
void HelloWorld::ccTouchesEnded(cocos2d::CCSet* pTouches, cocos2d::CCEvent * pEvent)
{
//得到触摸点
CCTouch *touch = (CCTouch*)pTouches->anyObject();
CCPoint location = this->convertTouchToNodeSpace(touch);
CCSize size = CCDirector::sharedDirector()->getWinSize();
//创建一个飞镖精灵
CCSprite *projectile = CCSprite::create("projectile.png");
projectile->setPosition(ccp(20, size.height / 2));
CCPoint offset = ccpSub(location, projectile->getPosition());
if (offset.x <= 0) {
return;
}
this->addChild(projectile);
//各种计算
int realX = size.width + projectile->getContentSize().width / 2;
float ratio = (float)offset.y / (float)offset.x;
int realY = realX * ratio + projectile->getPosition().y;
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;
float realMoveDuration = length / velocity;
//指定飞镖的移动轨迹,为忍着和触摸点之前的一条线,移动距离
//原参考博客的代码合并到一起有点长,我这里分开了.
CCMoveTo *actionMove = CCMoveTo::create(realMoveDuration, realDest);
CCCallFuncN *actionMoveDone = CCCallFuncN::create(this, callfuncN_selector(HelloWorld::spriteMoveFinished));
projectile->runAction(CCSequence::create(actionMove, actionMoveDone, NULL));
}
完成这些之后,可以稍事休息,运行程序试试.
在屏幕上点击,可以看到飞镖向点击的位置飞出去了.
现在飞镖在飞,怪物也在跑,但是还缺少最重要的功能,就是消灭怪物.
回到XCODE里来.
在HelloWorldScene.h添加如下声明,用于记录怪物和飞镖
cocos2d::CCArray *_monsters;
cocos2d::CCArray *_projectiles;
并且添加构造函数和析构函数,添加如下:
HelloWorld();
~HelloWorld();
函数代码如下,分别用户在初始化类和销毁类时初始化数组和释放数组:
HelloWorld::HelloWorld()
{
_monsters = NULL;
_projectiles = NULL;
}
HelloWorld::~HelloWorld()
{
if (_monsters) {
_monsters->release();
_monsters = NULL;
}
if (_projectiles)
{
_projectiles->release();
_projectiles = NULL;
}
}
接下来,在 init 函数中初始化 数组
在这里我煩了个错误,红字部分,本来应该是 _projectiles 的,然而我录入的时候没录入正确,以至于在后面的调试不能继续.
回过头来查找好久终于找到问题所在
this->_monsters = CCArray::create();
this->_monsters->retain();
this->_projectiles= CCArray::create();
this->_monsters->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);
}
添加一个update(float dt)方法,用于消除飞镖和怪物
1 void HelloWorld::update(float dt)
2 {
3 CCArray *projectilesToDelete = CCArray::create();
4 CCObject *pObject = NULL;
5 CCObject *pObject2 = NULL;
6 //遍历飞镖精灵
7 CCARRAY_FOREACH(_projectiles, pObject)
8 {
9 CCSprite *projectile = (CCSprite*)pObject;
10 CCArray *monstersToDelete = CCArray::create();
11 //和每一个怪物精灵的边框交叉检测
12 CCARRAY_FOREACH(_monsters, pObject2)
13 {
14 CCSprite *monster = (CCSprite*)pObject2;
15 //如果有重合,代表击中目标,将怪物精灵加入待删除列表
16 //从这里的代码上来看,如果一个飞镖和两个怪物有所碰撞,两只怪物应该是都被消灭
17 if (CCRect::CCRectIntersectsRect(projectile->boundingBox(), monster->boundingBox())) {
18 monstersToDelete->addObject(monster);
19 }
20 }
21 //删除在待删除怪物数组中的怪物
22 CCARRAY_FOREACH(monstersToDelete, pObject2)
23 {
24 CCSprite *monster = (CCSprite*)pObject2;
25 _monsters->removeObject(monster);
26 this->removeChild(monster, true);
27 }
28 //如果
29 if (monstersToDelete->count() > 0) {
30 projectilesToDelete->addObject(projectile);
31 }
32 monstersToDelete->release();
33
34 }
35
36 CCARRAY_FOREACH(projectilesToDelete, pObject)
37 {
38 CCSprite *projectile =(CCSprite*)pObject;
39 _projectiles->removeObject(projectile);
40 this->removeChild(projectile, true);
41 }
42 projectilesToDelete->release();
43
44 }
根据原微博的评论,修改了Update函数的以下语句,如果使用原语句会提示:'CCRectIntersectsRect' is deprecated
1 if (CCRect::CCRectIntersectsRect(projectile->boundingBox(), monster->boundingBox())) {
2 monstersToDelete->addObject(monster);
3 }
替换为
if (projectile->boundingBox().intersectsRect(monster->boundingBox())) {
monstersToDelete->addObject(monster);
}
然后在init函数中安装定时器
1 this->schedule(schedule_selector(HelloWorld::update));
在ccTouchesEnded函数,添加子弹音效,代码如下:
1 CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("pew-pew-lei.caf");
添加音效,在init函数添加背景音乐,代码如下:
1 CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("background-music-aac.caf");
现在运行游戏,已经可以,点击模拟器屏幕,已经可以发射飞镖和消灭怪物.
并且还有动感的音乐.
再坚持一下,还有最后一步了,加入一个新的场景,用于显示游戏失败.
添加新的C++ Class 文件,输入文件名GameOverLayer确定.
就新增了GameOverLayer.cpp 和 GameOverLayer.h文件
修改 GameOverLayer.h 如下:
#ifndef __simpleGame__GameOverLayer__
#define __simpleGame__GameOverLayer__
#include "cocos2d.h"
class GameOverLayer : public cocos2d::CCLayerColor
{
public:
GameOverLayer(void);
~GameOverLayer(void);
bool initWithWon(bool won);
static cocos2d::CCScene* seneWithWon(bool won);
static GameOverLayer* createWithWon(bool won);
void gameoverDone();
}
#endif /* defined(__simpleGame__GameOverLayer__) */
修改.cpp文件如下
1 #include "GameOverLayer.h"
2 #include "HelloWorldScene.h"
3 using namespace cocos2d;
4
5 GameOverLayer::GameOverLayer(void)
6 {
7 }
8
9 GameOverLayer::~GameOverLayer(void)
10 {
11 }
12
13 GameOverLayer* GameOverLayer::createWithWon(bool won)
14 {
15 GameOverLayer *pRet = new GameOverLayer();
16 if (pRet && pRet->initWithWon(won))
17 {
18 pRet->autorelease();
19 return pRet;
20 }
21 else
22 {
23 CC_SAFE_DELETE(pRet);
24 return NULL;
25 }
26 }
27
28 bool GameOverLayer::initWithWon(bool won)
29 {
30 bool bRet = false;
31 do
32 {
33 CC_BREAK_IF(! CCLayerColor::initWithColor(ccc4(255, 255, 255, 255)));
34
35 char *message;
36 if (won)
37 {
38 message = "You Won!";
39 }
40 else
41 {
42 message = "You Lose :[";
43 }
44
45 CCSize winSize = CCDirector::sharedDirector()->getWinSize();
46 CCLabelTTF *label = CCLabelTTF::create(message, "Arial", 32);
47 label->setColor(ccc3(0, 0, 0));
48 label->setPosition(ccp(winSize.width / 2, winSize.height / 2));
49 this->addChild(label);
50
51 this->runAction(CCSequence::create(CCDelayTime::create(3),
52 CCCallFunc::create(this, callfunc_selector(GameOverLayer::gameOverDone)),
53 NULL));
54
55 bRet = true;
56 } while (0);
57
58 return bRet;
59 }
60
61 cocos2d::CCScene* GameOverLayer::sceneWithWon(bool won)
62 {
63 CCScene * scene = NULL;
64 do
65 {
66 scene = CCScene::create();
67 CC_BREAK_IF(! scene);
68
69 GameOverLayer *layer = GameOverLayer::createWithWon(won);
70 CC_BREAK_IF(! layer);
71
72 scene->addChild(layer);
73 } while (0);
74
75 return scene;
76 }
77
78 void GameOverLayer::gameOverDone()
79 {
80 CCDirector::sharedDirector()->replaceScene(HelloWorld::scene());
81 }
这里我暂时先直接拷贝贴过来.
14.最后,为游戏添加一些游戏逻辑。记录玩家消灭怪物的数量,进而决定该玩家输赢。在HelloWorldScene.h文件中,添加如下:
1
| |
int _monstersDestroyed;
| 在HelloWorldScene.cpp文件,HelloWorld()构造函数,添加如下代码:
1
| |
_monstersDestroyed = 0;
| 添加头文件引用:
1
| |
#include "GameOverLayer.h"
| 在update定时函数中,monstersToDelete循环removeChild(monster, true)的后面添加被消灭怪物的计数,并判断胜利条件,代码如下:
1
2
3
4
5
6
| |
_monstersDestroyed++;
if (_monstersDestroyed > 30)
{
CCScene *gameOverScene = GameOverLayer::sceneWithWon(true);
CCDirector::sharedDirector()->replaceScene(gameOverScene);
}
|
最后为玩家添加失败条件,规定只要有一只怪物跑到左边屏幕里,则玩家失败,在spriteMoveFinished函数里,sprite->getTag() == 1条件的后面,添加如下:
1
2
| |
CCScene *gameOverScene = GameOverLayer::sceneWithWon(false);
CCDirector::sharedDirector()->replaceScene(gameOverScene);
|
14.编译并运行,到此已完成了一个简单的游戏,包含音效,并带有胜利和失败的结束。游戏效果如下:
最后这段完了之后,由于本人C++新手,所以阅读了一下代码,来记录一下流程:
首先是两个静态函数
static cocos2d::CCScene* sceneWithWon(bool won);
static GameOverLayer* createWithWon(bool won);
第一个函数,作为类的入口
cocos2d::CCScene* GameOverLayer::sceneWithWon(bool won)
{
CCScene* scene = NULL;
do {
//创建一个场景
scene = CCScene::create();
CC_BREAK_IF(!scene);
//如果成功,则调用第二个静态函数来创建layer
GameOverLayer* layer = GameOverLayer::createWithWon(won);
CC_BREAK_IF(!layer);
scene->addChild(layer);
} while (0);
return scene;
}
创建layer
经过进一步的阅读代码,发现这个函数感觉上可以用宏CREATE_FUNC(GameOverLayer)来代替,来看看这个宏的原型,
在对比了blog中后来添加的类和原来的helloworld之后,可以发现参照官方的示例是不错的.
CREATE_FUNC实际上就是create函数.当然这里是传递参数的需要来写函数
1 #define CREATE_FUNC(__TYPE__) \
2 static __TYPE__* create() \
3 { \
4 __TYPE__ *pRet = new __TYPE__(); \
5 if (pRet && pRet->init()) \
6 { \
7 pRet->autorelease(); \
8 return pRet; \
9 } \
10 else \
11 { \
12 delete pRet; \
13 pRet = NULL; \
14 return NULL; \
15 } \
16 }
GameOverLayer* GameOverLayer::createWithWon(bool won)
{
//创建一个新的layer对象
GameOverLayer* pRet = newGameOverLayer();
//如果创建对象不为空,则返回这个对象
if (pRet && pRet->initWithWon(won)) {
pRet->autorelease();
return pRet;
}
else
{
CC_SAFE_DELETE(pRet);
returnNULL;
}
}
bool GameOverLayer::initWithWon(bool won)
{
bool bRet = false;
do
{
CC_BREAK_IF(!CCLayerColor::initWithColor(ccc4(255,255,255,255)));
char* message;
if (won) {
message = "you won";
}
else
{
message = "you lose";
}
CCSize size = CCDirector::sharedDirector()->getWinSize();
CCLabelTTF* label = CCLabelTTF::create(message, "Arial", 32);
label->setColor(ccc3(0, 0, 0));
label->setPosition(ccp(size.width / 2, size.height / 2));
this->addChild(label);
this->runAction(CCSequence::create(CCDelayTime::create(3),
CCCallFuncN::create(this, callfuncN_selector(GameOverLayer::gameOverDone)), NULL));
bRet = true;
} while (0);
return bRet;
}
参考资料
http://blog.sina.com.cn/s/blog_9c3d739101017d2n.html |
|