本文實踐自 Ray Wenderlich 的文章《How To Make a Tile-Based Game with Cocos2D》,文中使用Cocos2D,我在這裡使用Cocos2D-x 2.0.4進行學習和移植。這個遊戲是關於一個忍者在沙漠中尋找西瓜的故事。
在這部分內容,將會學習到如何用Tile建立一個地圖,如何載入地圖到遊戲,如何讓地圖跟隨玩家滾動,以及如何使用對象層。下一部分內容,將介紹如何在地圖中建立可碰撞的地區,如何使用Tile屬性,如何建立可拾取的物品和動態修改地圖,以及如何確保忍者不會吃撐掉。
步驟如下:
1.建立Cocos2d-win32工程,工程名為"TileGame",去除"Box2D"選項,勾選"Simple Audio Engine in Cocos Denshion"選項;
2.下載本遊戲所需的資源,將資源放置"Resources"目錄下;
3.使用Tiled工具製作地圖。首先,下載開源的Tiled Map Editor工具,目前的版本為0.8.1,建議使用QT版本。在Tiled工具,點擊功能表列→"檔案"→"新檔案",在彈出的對話方塊中,填入如下內容:
地圖方向分為:正常、45度。地圖大小填入的是tile單位。塊大小是資源每個tile的實際像素大小,在本篇中,使用32x32大小。點擊"確定"。
4.接著,把所需要的tile集合加入到工具中。功能表列→"地圖"→"新圖塊",填入如下內容:
點擊"瀏覽",選擇"Resources"目錄下的tmw_desert_spacing.png檔案,會自動填滿"名稱"內容。塊的寬高都為32像素。邊距就是當前tile塊開始計算實際像素時,應該跳過多少像素,寬高一樣。間距就是兩個tile塊之間的像素距離,寬高一樣。看看tmw_desert_spacing.png檔案,可以看到每個tile塊都1像素的黑色邊框,這就是為什麼要設定邊距和間距為1像素。
點擊"確定"。
5.可以看到tile塊出現在"圖塊"視窗中。現在可以開始畫地圖。點擊功能表列→"視圖"→"顯示網格",可以開啟網格參照線。點擊工具列"圖章刷",然後在"圖塊"視窗點選一個tile塊,接著在地圖中,點擊放入你所想要的位置。
用工具列"填充",把地圖背景填充成同一個tile塊,這裡為沙漠背景。可以從"圖塊"視窗點選多個tile塊,可按Ctrl鍵多選,這樣可以一次性將多個tile塊放入地圖中。進行製作地圖,確保至少有一對建築在地圖上,因為後面需要一些東西來做碰撞。一旦完成了地圖的製作,雙擊"圖層"視窗中的當前層"塊層
1",重新命名為"Background"。然後點擊工具列"儲存",命名為"TileMap.tmx",儲存在"Resources"目錄下。
6.將Tile地圖加入到情境中。在HelloWorldScene.h檔案中,添加如下代碼:
1 2
|
|
CC_SYNTHESIZE_RETAIN(cocos2d::CCTMXTiledMap*, _tileMap, TileMap); CC_SYNTHESIZE_RETAIN(cocos2d::CCTMXLayer*, _background, Background); |
在HelloWorldScene.cpp檔案中,建構函式添加如下:
1 2 3 4 5
|
|
HelloWorld::HelloWorld() { _tileMap = NULL; _background = NULL; } |
初始化init函數,修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
|
bool HelloWorld::init() { bool bRet = false; do { CC_BREAK_IF(! CCLayer::init()); this->setTileMap(CCTMXTiledMap::create("TileMap.tmx")); this->setBackground(_tileMap->layerNamed("Background")); this->addChild(_tileMap, -1); bRet = true; } while (0); return bRet; } |
7.編譯運行,可以看到整個地圖的左下角,如所示:
要讓它成為一個遊戲,還需要三件事:玩家、玩家初始點、行動裝置檢視以便能夠看到玩家。
8.Tiled支援兩種類型層:tile層和對象層。對象層允許你在地圖上可能發生事件的地區繪製矩形。比如:你可能需要一個怪物出生地區,或者一個一進入就會掛掉的地區。在這裡,我們給玩家建立一個出生地。功能表列→"圖層"→"添加對象層",命名為"Objects",點擊工具列"插入對象",在地圖上選擇一個位置並點擊它,這時會出現一個灰色的矩形框,右鍵選擇"對象屬性",名稱填入"SpawnPoint",如所示:
按"確定"按鈕。儲存地圖。
9.在HelloWorldScene.h檔案中,添加如下聲明:
1
|
|
CC_SYNTHESIZE_RETAIN(cocos2d::CCSprite*, _player, Player); |
在HelloWorldScene.cpp建構函式中,添加如下:
在init初始化函數,添加如下:
1 2 3 4 5 6 7 8 9 10 11 12
|
|
CCTMXObjectGroup *objects = _tileMap->objectGroupNamed("Objects"); CCAssert(objects != NULL, "Objects' object group not found"); CCDictionary *spawnPoint = objects->objectNamed("SpawnPoint"); CCAssert(spawnPoint != NULL, "SpawnPoint object not found"); int x = spawnPoint->valueForKey("x")->intValue(); int y = spawnPoint->valueForKey("y")->intValue();this->setPlayer(CCSprite::create("Player.png")); _player->setPosition(ccp(x, y)); this->addChild(_player); this->setViewpointCenter(_player->getPosition()); |
添加一個方法setViewpointCenter,代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
|
void HelloWorld::setViewpointCenter(CCPoint position) { CCSize winSize = CCDirector::sharedDirector()->getWinSize(); int x = MAX(position.x, winSize.width / 2); int y = MAX(position.y, winSize.height / 2); x = MIN(x, (_tileMap->getMapSize().width * _tileMap->getTileSize().width) - winSize.width / 2); y = MIN(y, (_tileMap->getMapSize().height * _tileMap->getTileSize().height) - winSize.height / 2); CCPoint actualPosition = ccp(x, y); CCPoint centerOfView = ccp(winSize.width / 2, winSize.height / 2); CCPoint viewPoint = ccpSub(centerOfView, actualPosition); this->setPosition(viewPoint); } |
使用者傳任何的座標過來,但是有一些點不希望顯示出來,比如,我們不想螢幕移出地圖邊界,那裡只是一片空白。
攝像機的中心若是小於winSize.width/2或winSize.height/2,部分的視圖將會在螢幕之外。同樣的,我們需要檢查邊界條件。到目前為止,我們一直把這個函數看作是設定攝像機中心的位置。但是,這並不是我們實際做的。實際做的是移動整個層。看:
想象一下,身處在一個巨大的世界中,我們能看到的地區是從0到winSize.height/width的這部分。我們視野的中心是centerOfView,並且我們知道要讓中心在哪兒(actualPosition)。所以要讓我們視野的中心向上向右移動到actualPosition,我們只需要讓地圖相對的向下向左移動即可。這一步是通過actualPosition與centerOfView座標相減得到的,然後把HelloWorld層的座標設為這個結果。
10.編譯運行,可以看到忍者在螢幕中,如所示:
11.接下來,讓忍者可以移動。在HelloWorldScene.cpp檔案init函數中,添加如下代碼:
1
|
|
this->setTouchEnabled(true); |
開啟觸摸,然後重載registerWithTouchDispatcher函數,代碼如下:
1 2 3 4
|
|
void HelloWorld::registerWithTouchDispatcher(void) { CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true); } |
註冊觸摸事件,這樣單點觸摸ccTouchBegan和ccTouchEnded會被調用。重載ccTouchBegan函數,代碼如下:
1 2 3 4
|
|
bool HelloWorld::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) { return true; } |
返回true表明接受這個觸摸。添加setPlayerPosition方法,來設定玩家座標,代碼如下:
1 2 3 4
|
|
void HelloWorld::setPlayerPosition(CCPoint position) { _player->setPosition(position); } |
重載ccTouchEnded方法,代碼如下:
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
|
|
void HelloWorld::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent) { CCPoint touchLocation = this->convertTouchToNodeSpace(pTouch); CCPoint playerPos = _player->getPosition(); CCPoint diff = ccpSub(touchLocation, playerPos); if (abs(diff.x) > abs(diff.y)) { if (diff.x > 0) { playerPos.x += _tileMap->getTileSize().width; } else { playerPos.x -= _tileMap->getTileSize().width; } } else { if (diff.y > 0) { playerPos.y += _tileMap->getTileSize().height; } else { playerPos.y -= _tileMap->getTileSize().height; } } if (playerPos.x <= (_tileMap->getMapSize().width * _tileMap->getTileSize().width) && playerPos.y <= (_tileMap->getMapSize().height * _tileMap->getTileSize().height) && playerPos.y >= 0 && playerPos.x >= 0) { setPlayerPosition(playerPos); } setViewpointCenter(_player->getPosition()); } |
計算觸摸點與玩家座標之間的差值,來決定玩家的移動方向。
12.編譯運行,點擊螢幕,讓忍者動起來,如所示:
參考資料:
1.How To Make a Tile-Based Game with Cocos2D
http://www.raywenderlich.com/1163/how-to-make-a-tile-based-game-with-cocos2d
2.如何使用Cocos2D製作一款基於tile的遊戲
http://www.raywenderlich.com/zh-hans/16771/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8cocos2d%E5%88%B6%E4%BD%9C%E4%B8%80%E6%AC%BE%E5%9F%BA%E4%BA%8Etile%E7%9A%84%E6%B8%B8%E6%88%8F
3.(譯)如何使用cocos2d製作基於tile地圖的遊戲教程:第一部分
http://www.cnblogs.com/zilongshanren/archive/2011/04/11/2012852.html
非常感謝以上資料,本例子原始碼附加資源:http://download.csdn.net/detail/akof1314/4966622
如文章存在錯誤之處,歡迎指出,以便改正。