知易遊戲開發教程cocos2d-x移植版006

原文:http://www.cnblogs.com/cocos2d-x/archive/2012/06/03/2532932.html


在上一節中,我們使用經典FC遊戲《坦克大戰》的元素設計了一張地圖,來演示Tiled Map Editor工具的基本用法,並在cocos2d-x程序中完成了tmx地圖加載、查看以及動態修改地圖元素的功能。

這一節,我們將對示例5進一步擴充、完善,使其成爲能“玩”一下的遊戲。

爲了這個目標,我們需要做以下調整:

1)增加雙方坦克,我方一輛,敵方八輛。
2)坦克在地圖上行走時,需要完成基本的碰撞檢測,不可以穿牆而過。
3)坦克可以開炮,炮彈可以摧毀磚牆和敵對方坦克。
4)視角鎖定在我方坦克上,顯示區域跟隨我方坦克的移動而改變。
5)敵方(電腦)坦克擁有基本的AI來增加點兒樂趣。

增加坦克

我們的目標只是一個簡單的示例遊戲,所以我準備用同一個類來表示雙方坦克,通過一個布爾型變量來區分敵我。

複製代碼
1 class TankSprite : public CCSprite
2 {
3 public:
4     TankSprite(void);
5     virtual ~TankSprite(void);
6 
7     bool bIsEnemy;
8 }
複製代碼

在地圖中漫遊

作爲一個“完整”的遊戲,我們需要實現在遊戲地圖中漫遊的功能。這包含兩方面的內容,下面我們來分別描述一下。

1)坦克精靈在地圖上的移動

遊戲講究一個代入感。是誰在玩遊戲?不是上帝,我只是一輛小小的坦克。當操作指令下達時,應該是一輛坦克去完成它,而不是通過上帝的眼睛來觀察。所以,現在需要完成的功能就是讓坦克在tmx地圖上移動。

要實現這樣的功能並不困難,因爲CCSprite精靈類爲我們準備好的大部分內容。我們要做的只是爲坦克增加當前的狀態、移動速度以及四個方向的移動函數。

複製代碼
1     // TankAction 是一個枚舉類型,用來表示坦克當期的狀態
2     TankAction kAct;
3     float moveStep;
4     void moveLeft(GameLayer *layer);
5     void moveRight(GameLayer *layer);
6     void moveUp(GameLayer *layer);
7     void moveDown(GameLayer *layer);
複製代碼

在實現四方向移動函數時,因爲坦克是tmx地圖的子節點,所以座標不需要太多計算。

複製代碼
 1 void TankSprite::moveLeft(GameLayer *layer)
 2 {
 3     kAct = kLeft;
 4     setRotation(-90.0f);
 5     CCPoint tankPos = getPosition();
 6     tankPos.x -= moveStep;
 7     CCSize tankSize = getTextureRect().size;
 8 
 9     // 越界檢測
10     if (tankPos.x - layer->mapX < tankSize.width / 2)
11         setPosition(ccp(tankSize.width / 2, tankPos.y));
12     else
13         setPosition(tankPos);
14 }
複製代碼

2)視口跟隨

完成上面的功能,坦克就可以在地圖上行走了。但是我們只能看見屏幕大小的地圖,坦克很容易就走到屏幕之外去了。我們不願意做一隻“井底之蛙”,眼睛要跟上坦克。

我們前面介紹過視口這個概念,它就是整個遊戲世界向我們打開的一扇窗子。而且,在示例5中我們也嘗試了移動視口來觀察整個遊戲地圖。我們現在要做的就是加一個視口跟隨的功能,讓視口跟隨主角(我方)坦克車移動,將合適的地圖區域展示給我們。

縱觀大多數人的設計,對於視口跟隨,一個普遍的做法是這樣的。

大部分時間都將主角精靈固定在視口中心,主角的移動是通過反方向移動背景地圖模擬的。只有當視口已經到達地圖邊緣,再沒有更多地圖供我們移動時,才移動主角本身。

句子有點兒拗口,沒理解的朋友請看下面的演示。

將上面的過程寫成代碼:

複製代碼
 1 void GameLayer::setWorldPosition(void)
 2 {
 3     CCSize tankSize = tank->getTextureRect().size;
 4     CCPoint tankPos = tank->getPosition();
 5 
 6     if (tankPos.x < (screenWidth - tankSize.width) / 2)
 7         mapX = 0;
 8     else if (tankPos.x > gameWorldWidth() - screenWidth / 2)
 9         mapX = -gameWorldWidth();
10     else
11         mapX = -(tankPos.x - screenWidth / 2 + tankSize.width / 2);
12 
13     if (tankPos.y < (screenHeight - tankSize.height) / 2)
14         mapY = 0;
15     else if (tankPos.y > gameWorldHeight() - screenHeight / 2)
16         mapY = -gameWorldHeight();
17     else
18         mapY = -(tankPos.y - screenHeight / 2 + tankSize.height / 2);
19 
20     // 越界復位
21     if (mapX > 0)
22         mapX = 0;
23     if (mapY > 0)
24         mapY = 0;
25     if (mapX < -(gameWorldWidth() - screenWidth))
26         mapX = -(gameWorldWidth() - screenWidth);
27     if (mapY < -(gameWorldHeight() - screenHeight))
28         mapY = -(gameWorldHeight() - screenHeight);
29     gameWorld->setPosition(mapX, mapY);
30 }
複製代碼

碰撞檢測

坦克也不是說是無堅不摧的,所以遇到河啊什麼的,還是從橋上走方便一些。所以我們就得判斷是不是沒路了,是不是撞牆了,於是“碰撞檢測”的概念就引入進來了。

試想,有8輛敵方坦克正在地圖上游蕩,他們撞牆要檢測,互相之間也要檢測,他們偶爾還會發射炮彈,每個炮彈的飛行和爆炸也都需要檢測。而且這些都是併發進行的。哇!多麼混亂的一個場面啊!

呵呵,其實“碰撞檢測”並沒有大家想的那麼複雜,不是說要做碰撞檢測就要弄個物理引擎進來,只要不是在實現逼真的物理效果,我們完全可以用自己的方法來檢測碰撞。

大家知道,什麼同時啊,並行啊,這些統統都是理論上的,或者說是在一定精度範圍上的東西。即便是多核CPU,在共享同一資源時,它們也要分優先級的。所以,在小遊戲這類簡單的應用上,我們完全可以認爲,一次碰撞發生時,世界是靜止的。

這樣一來,我們只需要按照一定的優先級,順次爲每一個需要檢測碰撞的對象進行檢測即可。

對於坦克的行走來說,我們需要做的檢測只有地形一個因素,又因爲是在同一座標系下,事情灰常簡單。

考慮到坦克是有體積的,這裏取3個採樣點檢測碰撞,以避免其邊緣進入牆裏。

複製代碼
 1     // 這是坦克左移時的碰撞檢測代碼
 2     CCPoint nextPos = ccp(tankPos.x - tankSize.width / 2, tankPos.y);
 3     unsigned int tid = layer->tileIDFromPosition(nextPos);
 4     if (tid != 4)
 5         return;
 6     nextPos = ccp(tankPos.x - tankSize.width / 2, tankPos.y + tankSize.height / 4);
 7     tid = layer->tileIDFromPosition(nextPos);
 8     if (tid != 4)
 9         return;
10     nextPos = ccp(tankPos.x - tankSize.width / 2, tankPos.y - tankSize.height / 4);
11     tid = layer->tileIDFromPosition(nextPos);
12     if (tid != 4)
13         return;
複製代碼

開火射擊

瞧,那輛破坦克搖頭擺尾地開過來了,讓我們幹掉他。Fire! BOOM...

好吧,既然你想開火,那就給每輛坦克都配上炮彈吧。

複製代碼
 1 BulletSprite* BulletSprite::bulletWithLayer(GameLayer *layer)
 2 {
 3     BulletSprite *sprite = new BulletSprite();
 4     if (sprite && sprite->initWithFile("bullet.png", CCRectMake(0, 0, 16, 16)))
 5     {
 6         sprite->autorelease();
 7         layer->gameWorld->addChild(sprite, 100);
 8         sprite->setIsVisible(false);
 9         sprite->setGameLayer(layer);
10         return sprite;
11     }
12     CC_SAFE_DELETE(sprite);
13     return NULL;
14 }
複製代碼

雖然炮彈是坦克的配備,但是爲了方便座標計算,我們將炮彈歸爲地圖的子節點。換個思路想,炮彈發射後就跟坦克分離了,所以我們這麼設計也是可以接受的。

理所當然的,我們還需要爲每一輛坦克增加一個開火按鈕。

複製代碼
 1 void TankSprite::onFire(GameLayer *layer)
 2 {
 3     CCPoint tankPos = getPosition();
 4     CCSize tankSize = getTextureRect().size;
 5     CCPoint bulletPos, bulletTarget;
 6     switch (kAct)
 7     {
 8     case kUp:
 9         bulletPos = ccp(tankPos.x, tankPos.y + tankSize.height / 2);
10         bulletTarget = ccp(bulletPos.x, bulletPos.y + 1024);
11         break;
12     case kDown:
13         bulletPos = ccp(tankPos.x, tankPos.y - tankSize.height / 2);
14         bulletTarget = ccp(bulletPos.x, bulletPos.y - 1024);
15         break;
16     case kLeft:
17         bulletPos = ccp(tankPos.x - tankSize.width / 2, tankPos.y);
18         bulletTarget = ccp(bulletPos.x - 1024, bulletPos.y);
19         break;
20     case kRight:
21         bulletPos = ccp(tankPos.x + tankSize.width / 2, tankPos.y);
22         bulletTarget = ccp(bulletPos.x + 1024, bulletPos.y);
23         break;
24     default:
25         break;
26     }
27     bullet->setPosition(bulletPos);
28     bullet->setIsVisible(true);
29     CCShow *ac1 = CCShow::action();
30     CCMoveTo *ac2 = CCMoveTo::actionWithDuration(5.0f, bulletTarget);
31     bullet->runAction(CCSequence::actions(ac1, ac2, NULL));
32     // 啓動炮彈的碰撞檢測
33     this->schedule(schedule_selector(TankSprite::checkExplosion), 1.0f / 30.0f);
34 }
複製代碼

大家可以看到,炮彈發射是用系統內置的MoveTo動作模擬的,最後一行開啓每秒30次的炮彈碰撞檢測。檢測的方法與行走時的檢測類似,這裏不再重複。

小結

至此,一個簡單的坦克大戰演示就初步完成了。爲了給大家一個更形象的認識,上一張層結構示意圖。

本章示例代碼基於cocos2d-1.0.1-x-0.13.0-beta編寫。雖然cocos2d-2.0-rc0a-x-2.0已經發布了,但是變動比較大,主要是得換模板嚮導,我比較懶,所以本系列結束之前,我先不換版本了。

下載地址

http://files.cnblogs.com/cocos2d-x/ZYG006.rar


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章