(譯)碰撞檢測和收集物品:如何使用cocos2d製做基於tiled地圖的遊戲:第二部分

免責申明(必讀!):本博客提供的全部教程的翻譯原稿均來自於互聯網,僅供學習交流之用,切勿進行商業傳播。同時,轉載時不要移除本申明。如產生任何糾紛,均與本博客全部人、發表該翻譯稿之人無任何關係。謝謝合做! html

原文連接地址:http://www.raywenderlich.com/1186/collisions-and-collectables-how-to-make-a-tile-based-game-with-cocos2d-part-2 java

程序截圖: node

  這篇教程是《如何使用cocos2d製做基於tiled地圖的遊戲》的第二部分。在上一個教程中,咱們建立了一個簡單的基於tiled地圖的遊戲,裏面有一個忍者在沙漠裏尋找可口的西瓜! 編輯器

  在第一部分教程中,咱們介紹瞭如何基於tiled建立地圖,怎樣把地圖增長到遊戲中去,以及如何滾動地圖來跟隨主角移動、還有如何使用對象層。 ide

  在這部分教程中,咱們將會介紹如何在地圖中製做能夠碰撞的區域,如何使用tile屬性,若是收集遊戲物品而且動態地修改地圖、如何確保你的忍者不會吃得太飽! 函數

  所以,讓咱們繼續咱們上篇教程所學而且讓它更像一個真實的遊戲吧! 工具

tiled地圖和碰撞檢測

  你可能已經注意到了,目前咱們的忍者能夠毫無阻攔地穿過牆壁和障礙物。他是一個忍者,可是即便是真正的忍者,他也沒這麼厲害啊! 學習

  所以,咱們須要找到一種方法,經過把一些tile標記成「可碰撞的」,這樣的話,咱們就能夠防止玩家穿過那些點的位置。有不少方法能夠作獲得(包括使用對象層),可是,我想向大家展現一種新的技術。我認爲它更高效,而且也是一次好的學習鍛鍊--使用一個元層(meta layer)和層屬性。 測試

  讓咱們開始動手吧!再一次啓動Tiled軟件,點擊「Layer\Add tile Lyaer...」,而且命名爲「Meta」,而後選擇OK。咱們將在這個層裏面加入一些假的tile表明一些「特殊tile」。 google

  所以,如今咱們須要增長咱們的特殊tile。點擊「Map\New tileset...」,在你的Resources文件夾下面找到mate_tiles.png,而後選擇打開。設置Margin和Spacing都爲1並點擊OK。

  這時,你能夠在Tilesets區域看到一個新的標籤。打開它,並且你會看到2個tile:一個紅色的和一個綠色的。

  這些tile並無什麼特殊的東西--我只是製做了一個簡單的圖片,裏面包含了一個紅色的和一個綠色的半透明tile。接下來,咱們把紅色的tile看成是「可碰撞的」(後面咱們會用到綠色的),而後,合適地繪製咱們的場景。

   所以,確保Meta層被選中,選擇stamp工具,選擇紅色的tile,而後把任何你不想讓忍者經過的地圖都塗一遍。當你作完的時候,應該看起來像下面的圖示同樣:

  接下來,咱們能夠設置tile的屬性,這樣的話,咱們在代碼中就能夠識別這個tile是「能夠碰撞的(穿不過去的)」。在Tileset裏面的紅色tile上在,右擊,選擇「Properties...「。增長一個新的屬性,叫作」Collidable「,而且設置成」Ture「:

  (因爲版本的關係,我這裏補充我上傳的Tiled編輯器(java版本)如何設置屬性!!!)

       首先,選擇TileSets-->TileSetManager,並選中meta_tiles,出現以下所示圖:

    而後點擊右下角的「Edit」按鈕(就是垃圾回收站下面那個圖標),接下來會出現下圖所示:(接着就選中紅色tile和綠色tile,而後添加Collidable屬性並設置爲True就ok啦)

 


  保存map,並返回Xcode。在HelloWorldScene.h中作以下改動:

//  Inside the HelloWorld class declaration
CCTMXLayer  * _meta;

//  After the class declaration
@property (nonatomic, retain) CCTMXLayer  * meta;

  同時修改HelloWorldScene.m文件以下:

複製代碼
//  Right after the implementation section
@synthesize meta  =  _meta;

//  In dealloc
self.meta  =  nil;

//  In init, right after loading background
self.meta  =  [_tileMap layerNamed: @" Meta " ];
_meta.visible 
=  NO;

//  Add new method
-  (CGPoint)tileCoordForPosition:(CGPoint)position {
int  x  =  position.x  /  _tileMap.tileSize.width;
int  y  =  ((_tileMap.mapSize.height  *  _tileMap.tileSize.height)  -  position.y)  /  _tileMap.tileSize.height;
return  ccp(x, y);
}
複製代碼

  好了,讓咱們先停一下子。像以前同樣,我會meta層聲明瞭一個成員變量,並且從tile map中加載了一個引用。注意,咱們把這個字看成是不可見的,由於咱們並不想看見這些對象,它們的存在只是爲了說明,那個區域是能夠碰撞的。

  接下來,咱們增長一個新的幫助方法,這個方法能夠幫助咱們把x,y座標轉換成」tile座標「。每個tile都有一個座標,從左上角的(0,0)開始,到右下角的(49,49)。(本例中,地圖的大小是49×49)

  

  上面的截屏是java版本的tiled界面。可否顯示tile的座標,我不肯定這個功能在QT版本的tiled中是否存在。無論怎麼說,咱們將要使用的一些功能會使用tile座標,而不是x,y座標。所以,咱們須要一種方式,將x,y座標轉換成tile座標。這正是那個函數所須要作的。

   得到x座標很是容易--咱們只須要讓它除以一個tile的寬度就能夠了。爲了獲得y座標,咱們不得不翻轉一些東西,由於,在cocos2d裏面(0,0)是在左下角的,而不是在左上角。

  接下來,把setPlayerPosition替換成如下內容:

複製代碼
CGPoint tileCoord  =  [self tileCoordForPosition:position];
int  tileGid  =  [_meta tileGIDAt:tileCoord];
if  (tileGid) {
NSDictionary 
* properties  =  [_tileMap propertiesForGID:tileGid];
if  (properties) {
NSString 
* collision  =  [properties valueForKey: @" Collidable " ];
if  (collision  &&  [collision compare: @" True " ==  NSOrderedSame) {
return ;
}
}
}
_player.position 
=  position;
複製代碼

 

  在這裏,咱們把玩家的x,y座標轉換成tile座標。而後,咱們使用meta層中的tileGIDAt方法來獲取指定位置點的GID號。

  對了,什麼是GID呢?GID表明」全球惟一標誌符「(我我的意見)。可是,在這個例子中,我認爲它只是咱們使用的tile的一種標識,它能夠是咱們想要移動的紅色區域。

  當咱們使用GID來查找指定tile的屬性的時候。它返回一個屬性字典,所以,咱們能夠遍歷一下,看是否有」可碰撞的「物體被設置成」true「,或者是gij僅僅就是那樣。編譯並運行工程,所以尚未設置玩家的位置。

  就這麼多!編譯並運行程序,它將會向你展現,如今你不可以經過那些紅色的tile組成的地方了吧:

動態修改Tiled Map

  目前爲此,咱們的忍者已經有一個比較有意思的冒險啦,可是,這個世界有一點點無趣。並且簡單無任務事可作!加上,咱們的忍者看起來比較貪吃,並且背景將會隨着玩家移動而移動。所以,讓咱們建立一些東西讓忍者來玩吧!

  爲了使之可行,我將不得不建立一個前景層,這樣作可讓用戶收集東西。那樣的話,咱們僅僅從前景層中刪除不用的tile(當tile被玩角拾取的時候),這個過程當中,背景將會隨之移動。

  所以,打開Tiled,選擇」Layer\Add Tile Layer...「,把這個層命名爲」Foreground「,而後選擇OK。確保前景層被選擇,並且增長一對能夠拾取的物品在遊戲中。我喜歡放置一些向西瓜或者別的什麼東西。

  

  如今,咱們須要把這些tile標記成能夠拾取的,相似的,參照咱們是如何把tile標誌成能夠碰撞的。選擇Meta層,轉換到Meta_tiles。如今,咱們須要使這些tile能夠拾取,點擊」Layer\Move Layer Up「來確保你的meta層是在最頂層,而且保持綠色可見的。

  接下來,咱們須要爲tile增長屬性,這樣把它標記成可拾取的。點鍵點擊Tilesets選項卡里的綠色的tile,而後點「Properties...」,再增長一個新的屬性,命名爲「Collectable」,值設置爲「True」。

  保存地圖,而後返回到Xcode。在HelloWorldScene.h中作以下修改:

//  Inside the HelloWorld class declaration
CCTMXLayer  * _foreground;

//  After the class declaration
@property (nonatomic, retain) CCTMXLayer  * foreground;

  同時,相應地修改HelloWorldScene.m:

複製代碼
//  Right after the implementation section
@synthesize foreground  =  _foreground;

//  In dealloc
self.foreground  =  nil;

//  In init, right after loading background
self.foreground  =  [_tileMap layerNamed: @" Foreground " ];

//  Add to setPlayerPosition, right after the if clause with the return in it
NSString  * collectable  =  [properties valueForKey: @" Collectable " ];
if  (collectable  &&  [collectable compare: @" True " ==  NSOrderedSame) {
[_meta removeTileAt:tileCoord];
[_foreground removeTileAt:tileCoord];
}
複製代碼

 

  這裏是一個經常使用的方法,用來保存前景層的句柄。不一樣之處在於,咱們測試玩家正朝之移動的tile是否含有「Collectable」屬性。若是有,咱們就使用removeTileAt方法來把tile從mata層和前景層中移除掉。編譯並運行工程,如今你的忍者能夠嚐嚐西瓜的滋味啦!

  

建立一個計分器

  咱們忍者很是高興地吃西瓜啦,可是,做爲一個遊戲玩家,咱們想知道本身到底吃了多少個西瓜。你懂的,咱們並不想讓他吃得太胖。

  一般的作法是,咱們在層上面添加一個label。可是,等一下:咱們在不停地移動這個層,那樣的話,label就會看不到了,怎麼辦?

  這是一個很是好的機會,若是在一個場景中使用多個層--這正是咱們如今面臨的難題。咱們將保留HelloWorld層,可是,咱們會再增長一個HelloWorldHud層來顯示咱們的label。(Hud意味着Heads up display,你們能夠google一下,遊戲中經常使用的技術)

  固然,這兩個層之間須要一種方式聯繫起來--Hud層應該知道何時忍者吃了一個西瓜。有許許多多的方式可使2個不一樣的層相互通訊,可是,我只介紹最簡單的。咱們在HelloWorld層裏面保存一個HelloWorldHud層的句柄,這樣的話,當忍者吃了一個西瓜就能夠調用Hud層的一個方法來進行通知。

  所以,在HelloWorldScene.h裏面增長下面的代碼:

複製代碼
//  Before HelloWorld class declaration
@interface HelloWorldHud : CCLayer

CCLabel 
* label;
}

-  ( void )numCollectedChanged:( int )numCollected;
@end

//  Inside HelloWorld class declaration
int  _numCollected;
HelloWorldHud 
* _hud;

//  After the class declaration
@property (nonatomic, assign)  int  numCollected;
@property (nonatomic, retain) HelloWorldHud 
* hud;
複製代碼

 

  一樣的,修改HelloWorldScene.m文件:

複製代碼
//  At top of file
@implementation HelloWorldHud

- (id) init
{
if  ((self  =  [super init])) {
CGSize winSize 
=  [[CCDirector sharedDirector] winSize];
label 
=  [CCLabel labelWithString: @" 0 "  dimensions:CGSizeMake( 50 20 )
alignment:UITextAlignmentRight fontName:
@" Verdana-Bold "  
fontSize:
18.0 ];
label.color 
=  ccc3( 0 , 0 , 0 );
int  margin  = 10 ;
label.position 
=  ccp(winSize.width  -  (label.contentSize.width / 2
-  margin, label.contentSize.height / 2 +  margin);
[self addChild:label];
}
return  self;
}

-  ( void )numCollectedChanged:( int )numCollected {
[label setString:[NSString stringWithFormat:
@" %d " , numCollected]];
}

@end

//  Right after the HelloWorld implementation section
@synthesize numCollected  =  _numCollected;
@synthesize hud 
=  _hud;

//  In dealloc
self.hud  =  nil;

//  Add to the +(id) scene method, right before the return
HelloWorldHud  * hud  =  [HelloWorldHud node]; 
[scene addChild: hud];

layer.hud 
=  hud;

//  Add inside setPlayerPosition, in the case where a tile is collectable
self.numCollected ++ ;
[_hud numCollectedChanged:_numCollected];
複製代碼

 

  一切很明瞭。咱們的第二個層從CCLayer派生,只是在它的右下角加了一個label。咱們修改scene把第二個層也添加進去,而後傳遞一個Hud類的引用給HelloWorld層。而後修改HelloWorldLayer層,當計數器改變的時候,就調用Hud類的方法,這樣就能夠相應地更新Hud類了。

  編譯並運行,若是一切ok,你將會在屏幕右下角看到統計忍者吃西瓜的Label。

來點音效和音樂

  若是沒有很cool的音效和背景音樂的話,這就不能算做是一個完整的遊戲教程了。

  增長音效和音樂很是簡單,只需在HelloWolrdScene.m做以下修改:

複製代碼
//  At top of file
#import  " SimpleAudioEngine.h "

//  At top of init for HelloWorld layer
[[SimpleAudioEngine sharedEngine] preloadEffect: @" pickup.caf " ];
[[SimpleAudioEngine sharedEngine] preloadEffect:
@" hit.caf " ];
[[SimpleAudioEngine sharedEngine] preloadEffect:
@" move.caf " ];
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:
@" TileMap.caf " ];

//  In case for collidable tile
[[SimpleAudioEngine sharedEngine] playEffect: @" hit.caf " ];

//  In case of collectable tile
[[SimpleAudioEngine sharedEngine] playEffect: @" pickup.caf " ];

//  Right before setting player position
[[SimpleAudioEngine sharedEngine] playEffect: @" move.caf " ];
複製代碼

如今,咱們的忍者能夠開懷大吃了!

何去何從?

  這個系列的教程,就此完結了。距離上次翻譯時間長了點。經過這個教程的學習,你對cocos2d裏面的tiled map的使用,應該有一個很是好的理解了。這裏有這個教程的完整源代碼。

  接下來,我會接着翻譯下一篇,是原做者的一個朋友寫的,這個系列教程的終結版:《加入敵人和戰鬥:若是使用cocos2d製做基於tiled的地圖:第三部分》。

  若是你看了這個教程,有什麼好的意見或建議,能夠自由發言,謝謝!

相關文章
相關標籤/搜索