1. 程式人生 > >如何製作一個簡單的遊戲 Cocos2d-x 2 0 4

如何製作一個簡單的遊戲 Cocos2d-x 2 0 4

      本文實踐自 Ray Wenderlich 的文章《 How To Make A Simple iPhone Game with Cocos2D 2.X Tutorial》,文中使用Cocos2D,我在這裡使用Cocos2D-x 2.0.4進行學習和移植,前者是用Object-C所寫,所以移植到Cocos2D-x會有些差異,比如某些函式、某些功能不能跟原文一樣直接實現,需另轉換方法實現。之前已經對Cocos2D-x的安裝以及簡單使用進行了介紹,這裡不再介紹,直接進入主題。
 
步驟如下:
1.新建Cocos2d-win32工程,工程名為" SimpleGame",去除" Box2D"選項,勾選" Simple Audio Engine in Cocos Denshion
"選項;
2.編譯執行,可以看到如下圖所示:

3.下載本遊戲所需的資源,將資源放置" Resources"目錄下;

4.遊戲需要一個白色的背景,最簡單的方法是使用CCLayerColor,將 HelloWorldScene.h檔案"HelloWorld"類改為如下: 
1   class HelloWorld :  public cocos2d::CCLayerColor
首先新增玩家,讓玩家位於左邊螢幕中間,將 HelloWorldScene.cpp檔案的 init函式,改為如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  bool HelloWorld::init()
{
     bool bRet =  false;
     do 
    {
        CC_BREAK_IF(! CCLayerColor::initWithColor(ccc4( 255255255255)));

        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        CCSprite *player = CCSprite::create( "player.png", CCRectMake( 002740));
        player->setPosition(ccp(player->getContentSize().width /  2, winSize.height /  2));
         this->addChild(player);   

        bRet =  true;
    }  while ( 0);

     return bRet;
}
5.編譯執行,可以看到玩家精靈在白色背景上,如下圖所示:
6.接下來新增怪物,並且讓怪物可以移動,我們在螢幕右邊建立怪物,建立動作讓它們向左移動,增加 addMonster方法,程式碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  void HelloWorld::addMonster()
{
    CCSprite *monster = CCSprite::create( "monster.png");

    CCSize winSize = CCDirector::sharedDirector()->getWinSize();
     int minY = monster->getContentSize().height /  2;
     int maxY = winSize.height - monster->getContentSize().height /  2;
     int rangeY = maxY - minY;
     int actualY = (rand() % rangeY) + minY;

    monster->setPosition(ccp(winSize.width + monster->getContentSize().width /  2, actualY));
     this->addChild(monster);

     int minDuration =  2. 0;
     int maxDuration =  4. 0;
     int rangeDuration = maxDuration - minDuration;
     int actualDuration = (rand() % rangeDuration) + minDuration;

    CCMoveTo *actionMove = CCMoveTo::create(actualDuration, ccp(-monster->getContentSize().width /  2, actualY));
    CCCallFuncN *actionMoveDone = CCCallFuncN::create( this, callfuncN_selector(HelloWorld::spriteMoveFinished));
    monster->runAction(CCSequence::create(actionMove, actionMoveDone,  NULL));
}

在右邊螢幕以隨機的位置新增怪物精靈,注意計算精靈的位置座標,預設描點在中心,不要讓怪物截斷了。然後再以2~4秒的隨機總時間,讓怪物從右邊移動到左邊,移動出邊界後,即回撥函式spriteMoveFinished,進行刪除精靈物件,增加的spriteMoveFinished方法如下:

1
2
3
4
5
  void HelloWorld::spriteMoveFinished(CCNode *sender)
{
    CCSprite *sprite = (CCSprite*)sender;
     this->removeChild(sprite,  true);
}
接下去就是定時建立怪物,在 init函式返回之前,安裝定時器,每秒執行一次,程式碼如下:
1   this->schedule(schedule_selector(HelloWorld::gameLogic),  1. 0);
增加 gameLogic方法,程式碼如下:
1
2
3
4
  void HelloWorld::gameLogic(  float dt )
{
     this->addMonster();
}
7.編譯執行,可以看到右邊怪物定時增加,並且以不同的速度向左邊移動,如下圖所示:

8.接著讓玩家可以射擊子彈,當用戶在螢幕點選時,就讓玩家往點選的方向進行傳送子彈,使用者的螢幕點選點並不是子彈移動的最終地,借用原文的一張圖片來說明:

要讓層可以支援觸控,需要在 init方法,新增如下程式碼:
1   this->setTouchEnabled( true);
然後過載 ccTouchesEnded方法,程式碼如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  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( "projectile.png");
    projectile->setPosition(ccp( 20, winSize.height /  2));

    CCPoint offset = ccpSub(location, projectile->getPosition());

     if (offset.x <=  0)
    {
         return;
    }

     this->addChild(projectile);

     int realX = winSize.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;

    projectile->runAction(CCSequence::create(CCMoveTo::create(realMoveDuration, realDest), 
        CCCallFuncN::create( this, callfuncN_selector(HelloWorld::spriteMoveFinished)),  NULL));
}

首先,得到觸控點,然後建立子彈精靈,算出觸控點與子彈初始位置之差,若觸控點在初始位置的前方(即玩家前方),則新增子彈到層上。以同比例方法,計算出子彈飛向螢幕右邊的最終座標。然後再用勾股定理計算飛行長度,假定速度為每秒480畫素,則計算出飛行總時間。之後就是讓子彈執行給定的飛行動作,以及之後的刪除自身呼叫。
9.編譯執行,往螢幕點選,可以看到子彈發射出去,如下圖所示:

10.當子彈碰到怪物時,怪物被消滅,子彈消失,即碰撞檢測。需要在場景中跟蹤目標和子彈,在HelloWorldScene.h宣告如下:

1
2
  cocos2d::CCArray *_monsters;
cocos2d::CCArray *_projectiles;
在建構函式和解構函式,新增如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  HelloWorld::HelloWorld()
{
    _monsters =  NULL;
    _projectiles =  NULL;
}

HelloWorld::~HelloWorld()
{
     if (_monsters)
    {
        _monsters->release();
        _monsters =  NULL;
    }
     if (_projectiles)
    {
        _projectiles->release();
        _projectiles =  NULL;
    }
}

然後在init函式中初始化這兩個陣列:

1
2
3
4
  this->_monsters = CCArray::create();
this->_monsters->retain();
this->_projectiles = CCArray::create();
this->_projectiles->retain();
修改 addMonster函式,為怪物精靈新增標籤,並加入到陣列,程式碼如下:
1
2
  monster->setTag( 1);
_monsters->addObject(monster);
修改 ccTouchesEnded函式,為子彈精靈新增標籤,並加入到陣列,程式碼如下:
1
2
  projectile->setTag( 2);
_projectiles->addObject(projectile);
然後修改 spriteMoveFinished函式,增加如下程式碼:
1
2
3
4
5
6
7
8
  if (sprite->getTag() ==  1)
{
    _monsters->removeObject(sprite);
}
else  if (sprite->getTag() ==  2)
{
    _projectiles->removeObject(sprite);
}
新增如下方法:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  void HelloWorld::update( float dt)
{
    CCArray *projectilesToDelete = CCArray::create();
    
    CCObject *pObject =  NULL;
    CCObject *pObject2 =  NULL
    CCARRAY_FOREACH(_projectiles, pObject)
    {
        CCSprite *projectile = (CCSprite*)pObject;
        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*)pObject;
        _projectiles->removeObject(projectile);
         this->removeChild(projectile,  true);
    }
    
    projectilesToDelete->release();
}

遍歷子彈陣列,計算每一個子彈所可能遇到的怪物,用它們各自的邊界框進行交叉檢測,檢測到交叉,則將怪物物件放入ToDelete(待刪除)陣列,不能在遍歷的時候刪除一個物件。若是子彈遇到了怪物,也需要放入ToDelete(待刪除)陣列。然後從場景和陣列中移動掉。同樣,也在init函式,安裝定時器,程式碼如下:

1   this->schedule(schedule_selector(HelloWorld::update));
11.編譯執行,這時當子彈和怪物碰撞時,它們就會消失;
12.新增音效,在 init函式新增背景音樂,程式碼如下:
1   CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic( "background-music-aac.wav");
ccTouchesEnded函式,新增子彈音效,程式碼如下:
1   CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect( "pew-pew-lei.wav");
13.接下來,建立一個新的場景,來指示"You Win"或者"You Lose"。右鍵 工程,"Add"→"Class..."→"C++"→"Add","Base class"為CCLayerColor,"Class name"為 GameOverLayer,如下圖所示:

GameOverLayer.h檔案程式碼為:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  #pragma once
#include  "cocos2d.h"

class GameOverLayer :
     public cocos2d::CCLayerColor
{
public:
    GameOverLayer( void);
    ~GameOverLayer( void);
     bool initWithWon( bool won);

     static cocos2d::CCScene* sceneWithWon( bool won);
     static GameOverLayer* createWithWon( bool won);
     void gameOverDone();
};

 

GameOverLayer.cpp檔案程式碼為:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
  #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 pRet;
    }
     else
    {
        CC_SAFE_DELETE(pRet);
         return  NULL;
    }
}

bool GameOverLayer::initWithWon( bool won)
{
     bool bRet =  false;
     do 
    {
        CC_BREAK_IF(! CCLayerColor::initWithColor(ccc4( 255255255255)));

         char *message;
         if (won)
        {
            message =  "You Won!";
        } 
         else
        {
            message =  "You Lose :[";
        }
        
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        CCLabelTTF *label = CCLabelTTF::create(message,  "Arial"32);
        label->setColor(ccc3( 000));
        label->setPosition(ccp(winSize.width /  2, winSize.height /  2));
         this->addChild(label);

         this->runAction(CCSequence::create(CCDelayTime::create( 3), 
            CCCallFunc::create( this, callfunc_selector(GameOverLayer::gameOverDone)),
             NULL));
        
        bRet =  true;
    }  while ( 0);

     return bRet;
}

cocos2d::CCScene* GameOverLayer::sceneWithWon( bool won)
{
    CCScene * scene =  NULL;
     do 
    {
        scene = CCScene::create();
        CC_BREAK_IF(! scene);

        GameOverLayer *layer = GameOverLayer::createWithWon(won);
        CC_BREAK_IF(! layer);

        scene->addChild(layer);
    }  while ( 0);

     return scene;
}

void GameOverLayer::gameOverDone()
{
    CCDirector::sharedDirector()->replaceScene(HelloWorld::scene());
}

 

遊戲結束時,切換到以上所建的場景,場景上的層顯示一個文字,在3秒之後返回到HelloWorld場景中。
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.編譯並執行,到此已完成了一個簡單的遊戲,包含音效,並帶有勝利和失敗的結束。遊戲效果如下:

 

參考資料:
1.How To Make A Simple iPhone Game with Cocos2D 2.X Tutorial http://www.raywenderlich.com/25736/how-to-make-a-simple-iphone-game-with-cocos2d-2-x-tutorial
2.如何用Cocos2d來開發簡單的IPhone遊戲教程 http://www.cocoachina.com/bbs/read.php?tid-15554.html
3.Cocos2d Classic Tutorial Demo Revisit:(1) http://www.zilongshanren.com/cocos2d-classic-tutorial-demo-revisit-1/

非常感謝以上資料,本例子原始碼附加資源下載地址http://download.csdn.net/detail/akof1314/4857315
如文章存在錯誤之處,歡迎指出,以便改正。

 

再分享一下我老師大神的人工智慧教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智慧的隊伍中來!https://www.cnblogs.com/captainbed