Java敌人跟地图的碰撞_(译)加入敌人和战斗:如果使用cocos2d制作基于tiled地图的游戏:第三部分...
免責(zé)申明(必讀!):本博客提供的所有教程的翻譯原稿均來(lái)自于互聯(lián)網(wǎng),僅供學(xué)習(xí)交流之用,切勿進(jìn)行商業(yè)傳播。同時(shí),轉(zhuǎn)載時(shí)不要移除本申明。如產(chǎn)生任何糾紛,均與本博客所有人、發(fā)表該翻譯稿之人無(wú)任何關(guān)系。謝謝合作!
程序截圖:
這篇教程是Ray Wenderlich的《如何使用cocos2d制作基于tiled地圖的游戲》系列教程的后續(xù)。如果你還沒(méi)有看過(guò)前面兩部分的教程,可以在我的博客上找到另外兩篇我翻譯Ray的教程。
在第二部分教程中,Ray教大家如何在地圖中制作可碰撞的區(qū)域,如何使用tile屬性,如何制作可以拾取的物品以及如何動(dòng)態(tài)修改地圖、如何使用“Heads up display”來(lái)顯示分?jǐn)?shù)。
在這個(gè)教程中,我們將加入敵人,這樣的話,你的忍者就可以向它們?nèi)语w鏢啦,同時(shí)還增加了勝利和失敗的游戲邏輯。但是,首先,你得下載一些相關(guān)的資源文件。
這個(gè)zip文件里面包含以下內(nèi)容:
1.一個(gè)敵人精靈
2.一個(gè)忍者飛鏢,從Ray的《如何使用cocos2d制作一個(gè)簡(jiǎn)單的iphone游戲》中拿過(guò)來(lái)的。
3.兩張按鈕的圖片,在教程的后面有使用。
在繼續(xù)學(xué)習(xí)之前,不要忘了把這些資源加入到你的工程中。
增加敵人
到第二部分教程結(jié)束的時(shí)候,游戲已經(jīng)很酷了,但是它還不是一個(gè)完整的游戲。你的忍者可以輕而易舉地四處游蕩,想吃就吃。但是,什么時(shí)候玩家會(huì)勝利或者失敗呢。我們不妨想象一下,有2個(gè)敵人在追殺你的忍者,那么這個(gè)游戲會(huì)顯得更加有趣。
敵人出現(xiàn)的位置點(diǎn)
好了,回到Tiled軟件(此教程使用java版),然后打開(kāi)你的Tile地圖(TileMap.tmx)。
往對(duì)象層中加入一個(gè)對(duì)象,在player附近就行,但是不要太近,否則敵人一出現(xiàn)玩家就Game over了。這個(gè)位置將成為敵人出現(xiàn)的位置點(diǎn),把它命名為“EnemySpawn1”。
對(duì)象組(對(duì)象層中的所有對(duì)象組成一個(gè)對(duì)象組)中的對(duì)象被存儲(chǔ)在一個(gè)NSMutableDictionary中,同時(shí)使用對(duì)象名字作為key。這意味著每一個(gè)位置點(diǎn)必須有一個(gè)唯一的名字。盡管我們可以遍歷所有的key來(lái)比較哪個(gè)是以“EnemySpawn”開(kāi)頭,但是這樣做效率很低下。相反,我們采用的做法是,使用一個(gè)屬性來(lái)表示,每個(gè)給定的對(duì)象代表一個(gè)敵人出現(xiàn)的位置點(diǎn)。
給這個(gè)對(duì)象一個(gè)屬性“Enemy”,同時(shí)賦一個(gè)值1.如果你想在這個(gè)教程的基礎(chǔ)上擴(kuò)展,并且增加其它的不同類型的敵人,你可以使用這些敵人的屬性值來(lái)表示不同類型的敵人。
現(xiàn)在,制作6-10個(gè)這種敵人出現(xiàn)位置點(diǎn)對(duì)象,相應(yīng)的它們離player的距離也要有一些不同。為每一個(gè)對(duì)象定義一個(gè)“Enemy”屬性,并且賦值為1.保存這張地圖并且回到Xcode。
開(kāi)始創(chuàng)建敵人
好了,現(xiàn)在我們將把敵人實(shí)際顯示到地圖上來(lái)。首先在HelloWorldScene.m中添加如下代碼:
//in the HelloWorld class-(void)addEnemyAtX:(int)x y:(int)y {
CCSprite*enemy=[CCSprite spriteWithFile:@"enemy1.png"];
enemy.position=ccp(x, y);
[self addChild:enemy];
}//in the init method - after creating the player//iterate through objects, finding all enemy spawn points//create an enemy for each oneNSMutableDictionary*spawnPoint;for(spawnPointin[objects objects]) {if([[spawnPoint valueForKey:@"Enemy"] intValue]==1){
x=[[spawnPoint valueForKey:@"x"] intValue];
y=[[spawnPoint valueForKey:@"y"] intValue];
[self addEnemyAtX:x y:y];
}
}
第一個(gè)循環(huán)遍歷對(duì)象列表,判斷它是否是一個(gè)敵人出現(xiàn)的位置點(diǎn)。如果是,則獲得它的x和y坐標(biāo)值,然后調(diào)用addEnemyAtX:Y方法把它們加入到合適的地方去。
這個(gè)addEnemyAtX:Y方法非常直白,它僅僅是在傳入的X,Y坐標(biāo)值處創(chuàng)建一個(gè)敵人精靈。
如果你編譯并運(yùn)行,你會(huì)看到這些敵人出現(xiàn)在你之前在Tiled工具中設(shè)定的位置處,很酷吧!
但是,這里有一個(gè)問(wèn)題,這些敵人很傻瓜,它們并不會(huì)追殺你的忍者。
使它們移動(dòng)
因此,現(xiàn)在我們將添加一些代碼,使這些敵人會(huì)追著我們的player跑。因?yàn)?#xff0c;player肯定會(huì)移動(dòng),我們必須動(dòng)態(tài)地改變敵人的運(yùn)動(dòng)方向。為了實(shí)現(xiàn)這個(gè)目的,我們讓敵人每次移動(dòng)10個(gè)像素,然后在下一次移動(dòng)之前,先調(diào)整它們的方向。在HelloWorldScene.m中加入如下代碼:
//callback. starts another iteration of enemy movement.-(void) enemyMoveFinished:(id)sender {
CCSprite*enemy=(CCSprite*)sender;
[self animateEnemy: enemy];
}//a method to move the enemy 10 pixels toward the player-(void) animateEnemy:(CCSprite*)enemy
{//speed of the enemyccTime actualDuration=0.3;//Create the actionsid actionMove=[CCMoveBy actionWithDuration:actualDuration
position:ccpMult(ccpNormalize(ccpSub(_player.position,enemy.position)),10)];
id actionMoveDone=[CCCallFuncN actionWithTarget:self
selector:@selector(enemyMoveFinished:)];
[enemy runAction:
[CCSequence actions:actionMove, actionMoveDone, nil]];
}//add this at the end of addEnemyAtX:y://Use our animation method and//start the enemy moving toward the player[self animateEnemy:enemy];
animateEnemy:方法創(chuàng)建兩個(gè)action。第一個(gè)action使之朝敵人移動(dòng)10個(gè)像素,時(shí)間為0.3秒。你可以改變這個(gè)時(shí)間使之移動(dòng)得更快或者更慢。第二個(gè)action將會(huì)調(diào)用enemyMoveFinished:方法。我們使用CCSequence action來(lái)把它們組合起來(lái),這樣的話,當(dāng)敵人停止移動(dòng)的時(shí)候就立馬可以執(zhí)行enemyMoveFinished:方法就可以被調(diào)用了。在addEnemyAtX:Y:方法里面,我們調(diào)用animateEnemy:方法來(lái)使敵人朝著玩家(player)移動(dòng)。(其實(shí)這里是個(gè)遞歸的調(diào)用,每次移動(dòng)10個(gè)像素,然后又調(diào)用enemyMoveFinished:方法)
很簡(jiǎn)潔!但是,但是,如果敵人每次移動(dòng)的時(shí)候面部都對(duì)著player那樣是不是更逼真呢?只需要在animateEnemy:方法中加入下列語(yǔ)句即可:
//immediately before creating the actions in animateEnemy//rotate to face the playerCGPoint diff=ccpSub(_player.position,enemy.position);floatangleRadians=atanf((float)diff.y/(float)diff.x);floatangleDegrees=CC_RADIANS_TO_DEGREES(angleRadians);floatcocosAngle=-1*angleDegrees;if(diff.x<0) {
cocosAngle+=180;
}
enemy.rotation = cocosAngle
這個(gè)代碼計(jì)算每次玩家相對(duì)于敵人的角度,然后旋轉(zhuǎn)敵人來(lái)使之面朝玩家。
忍者飛鏢
已經(jīng)很不錯(cuò)了,但是玩家是一個(gè)忍者啊!他應(yīng)該要能夠保護(hù)他自己!
我們將向游戲中添加模式(modes)。模式并不是實(shí)現(xiàn)這個(gè)功能的最好方式,但是,它比其他的方法要簡(jiǎn)單,而且這個(gè)方法在模擬器下也能運(yùn)行(因?yàn)椴⒉恍枰帱c(diǎn)觸摸)。因?yàn)檫@些優(yōu)點(diǎn),所以這個(gè)教程里面,我們使用這種方法。首先將會(huì)建立UI,這樣的話玩家可以方便地在“移動(dòng)模式”和“擲飛鏢”模式之間進(jìn)行切換。我們將增加一個(gè)按鈕來(lái)使用這個(gè)功能的轉(zhuǎn)換。(即從移動(dòng)模式轉(zhuǎn)到擲飛鏢模式)。
現(xiàn)在,我們將增加一些屬性,使兩個(gè)層之間可以更好的通信。在HelloWorldScene.h里面增加如下代碼:
//at the top of the file add a forward declaration for HelloWorld,//because our two layers need to reference each other@class HelloWorld;//inside the HelloWorldHud class declarationHelloWorld*_gameLayer;//After the class declaration@property (nonatomic, assign) HelloWorld*gameLayer;//Inside the HelloWorld class declarationint_mode;//After the class declaration@property (nonatomic, assign)intmode;
同時(shí)修改HelloWorldScene.m文件
//At the top of the HelloWorldHud implementation@synthesize gameLayer=_gameLayer;//At the top of the HelloWorld implementation@synthesize mode=_mode;//in HelloWorld's init method_mode=0;//in HelloWorld's scene method//after layer.hud = hudhud.gameLayer=layer;
如果想知道在cocos2d里面如何使用按鈕,可以參照我翻譯的另外一篇教程《在cocos2d里面如何制作按鈕:簡(jiǎn)單按鈕、單選按鈕和開(kāi)關(guān)按鈕》。
在HelloWorldScene.m中添加下面的代碼,這段代碼定義了一個(gè)按鈕。
Add the folowing code, which defines a button, to HelloWorldScene.m://in HelloWorldHud's init method//define the buttonCCMenuItem*on;
CCMenuItem*off;
on=[[CCMenuItemImage itemFromNormalImage:@"projectile-button-on.png"selectedImage:@"projectile-button-on.png"target:nil selector:nil] retain];
off=[[CCMenuItemImage itemFromNormalImage:@"projectile-button-off.png"selectedImage:@"projectile-button-off.png"target:nil selector:nil] retain];
CCMenuItemToggle*toggleItem=[CCMenuItemToggle itemWithTarget:self
selector:@selector(projectileButtonTapped:) items:off, on, nil];
CCMenu*toggleMenu=[CCMenu menuWithItems:toggleItem, nil];
toggleMenu.position=ccp(100,32);
[self addChild:toggleMenu];//in HelloWorldHud//callback for the button//mode 0 = moving mode//mode 1 = ninja star throwing mode-(void)projectileButtonTapped:(id)sender
{if(_gameLayer.mode==1) {
_gameLayer.mode=0;
}else{
_gameLayer.mode=1;
}
}
編譯并運(yùn)行。這時(shí)會(huì)在左下角出現(xiàn)一個(gè)按鈕,并且你可以打開(kāi)或者關(guān)閉之。但是這并不會(huì)對(duì)游戲造成任何影響。我們的下一步就是增加飛鏢的發(fā)射。
發(fā)射飛鏢
接下來(lái),我們將添加一些代碼來(lái)檢查玩家當(dāng)前處于哪種模式下面,并且在用戶點(diǎn)擊屏幕的時(shí)候影響不同的事件。如果是移動(dòng)模式則移動(dòng)玩家,如果是射擊模式,則擲飛鏢。在ccTouchEnded:withEvent:方法里面增加下面代碼:
if(_mode==0) {//old contents of ccTouchEnded:withEvent:}else{//code to throw ninja stars will go here}
這樣可以使得移動(dòng)模式下,玩家只能移動(dòng)。下一步就是要添加代碼使忍者能夠發(fā)射飛鏢。在else部分增加,在增加之前,先在HelloWorld.m中添加一些清理代碼:
-(void) projectileMoveFinished:(id)sender {
CCSprite*sprite=(CCSprite*)sender;
[self removeChild:sprite cleanup:YES];
}
好了,看到上面的else部分的注釋了嗎:
// code to throw ninja stars will go here
在上面的注釋后面添加下面的代碼:
//Find where the touch isCGPoint touchLocation=[touch locationInView: [touch view]];
touchLocation=[[CCDirector sharedDirector] convertToGL: touchLocation];
touchLocation=[self convertToNodeSpace:touchLocation];//Create a projectile and put it at the player's locationCCSprite*projectile=[CCSprite spriteWithFile:@"Projectile.png"];
projectile.position=_player.position;
[self addChild:projectile];//Determine where we wish to shoot the projectile tointrealX;//Are we shooting to the left or right?CGPoint diff=ccpSub(touchLocation, _player.position);if(diff.x>0)
{
realX=(_tileMap.mapSize.width*_tileMap.tileSize.width)+(projectile.contentSize.width/2);
}else{
realX=-(_tileMap.mapSize.width*_tileMap.tileSize.width)-(projectile.contentSize.width/2);
}floatratio=(float) diff.y/(float) diff.x;intrealY=((realX-projectile.position.x)*ratio)+projectile.position.y;
CGPoint realDest=ccp(realX, realY);//Determine the length of how far we're shootingintoffRealX=realX-projectile.position.x;intoffRealY=realY-projectile.position.y;floatlength=sqrtf((offRealX*offRealX)+(offRealY*offRealY));floatvelocity=480/1;//480pixels/1secfloatrealMoveDuration=length/velocity;//Move projectile to actual endpointid actionMoveDone=[CCCallFuncN actionWithTarget:self
selector:@selector(projectileMoveFinished:)];
[projectile runAction:
[CCSequence actionOne:
[CCMoveTo actionWithDuration: realMoveDuration
position: realDest]
two: actionMoveDone]];
這段代碼會(huì)在用戶點(diǎn)擊屏幕的方向發(fā)射飛鏢。對(duì)于這段代碼的完整的細(xì)節(jié),可以查看我翻譯的另一個(gè)文章《如何使用cocos2d來(lái)做一個(gè)簡(jiǎn)單的iphone游戲教程(第一部分)》。當(dāng)然,查看原作者的文章后面的注釋會(huì)更加清楚明白一些。
projectileMoveFinished:方法會(huì)在飛鏢移動(dòng)到屏幕之外的時(shí)候移除。這個(gè)方法非常關(guān)鍵。一旦我們開(kāi)始做碰撞檢測(cè)的時(shí)候,我們將要循環(huán)遍歷所有的飛鏢。如果我們不移除飛出屏幕范圍之外的飛鏢的話,這個(gè)存儲(chǔ)飛鏢的列表將會(huì)越來(lái)越大,而且游戲?qū)?huì)越來(lái)越慢。編譯并運(yùn)行工程,現(xiàn)在,你的忍者可以向敵人投擲飛鏢了。
碰撞檢測(cè)
接下來(lái),就是當(dāng)飛鏢擊中敵人的時(shí)候,要把敵人銷毀。在HelloWorldClass類中增加以下變量(在HelloWorldScene.h文件中):
NSMutableArray*_enemies;
NSMutableArray*_projectiles;
然后初使化_projectiles數(shù)組:
//at the end of the launch projectiles section of ccTouchEnded:withEvent:[_projectiles addObject:projectile];//at the end of projectileMoveFinished:[_projectiles removeObject:sprite];
然后在addEnemyAtX:y方法的結(jié)尾添加如下代碼:
[_enemies addObject:enemy];
接著,在HelloWorld類中添加如下代碼:
-(void)testCollisions:(ccTime)dt {
NSMutableArray*projectilesToDelete=[[NSMutableArray alloc] init];//iterate through projectilesfor(CCSprite*projectilein_projectiles) {
CGRect projectileRect=CGRectMake(
projectile.position.x-(projectile.contentSize.width/2),
projectile.position.y-(projectile.contentSize.height/2),
projectile.contentSize.width,
projectile.contentSize.height);
NSMutableArray*targetsToDelete=[[NSMutableArray alloc] init];//iterate through enemies, see if any intersect with current projectilefor(CCSprite*targetin_enemies) {
CGRect targetRect=CGRectMake(
target.position.x-(target.contentSize.width/2),
target.position.y-(target.contentSize.height/2),
target.contentSize.width,
target.contentSize.height);if(CGRectIntersectsRect(projectileRect, targetRect)) {
[targetsToDelete addObject:target];
}
}//delete all hit enemiesfor(CCSprite*targetintargetsToDelete) {
[_enemies removeObject:target];
[self removeChild:target cleanup:YES];
}if(targetsToDelete.count>0) {//add the projectile to the list of ones to remove[projectilesToDelete addObject:projectile];
}
[targetsToDelete release];
}//remove all the projectiles that hit.for(CCSprite*projectileinprojectilesToDelete) {
[_projectiles removeObject:projectile];
[self removeChild:projectile cleanup:YES];
}
[projectilesToDelete release];
}
最后,初始化敵人來(lái)飛鏢數(shù)組,并且調(diào)度testCollisions:方法,把這些代碼加在HelloWorld類的init方法中。
//you need to put these initializations before you add the enemies,//because addEnemyAtX:y: uses these arrays._enemies=[[NSMutableArray alloc] init];
_projectiles=[[NSMutableArray alloc] init];
[self schedule:@selector(testCollisions:)];
上面的所有的代碼,關(guān)于具體是如何工作的,可以在我的博客上查找《如何使用COCOS2D制作一個(gè)簡(jiǎn)單的iphone游戲》教程。當(dāng)然,原作者的文章注釋部分的討論更加清晰,所以我翻譯的教程,也希望大家多討論啊。代碼盡量自己用手敲進(jìn)去,不要為了省事,alt+c,alt+v,這樣不好,真的!
好了,現(xiàn)在可以用飛鏢打敵人,而且打中之后它們會(huì)消失。現(xiàn)在讓我們添加一些邏輯,使得游戲可以勝利或者失敗吧!
勝利和失敗
The Game Over Scene
好了,讓我們創(chuàng)建一個(gè)新的場(chǎng)景,來(lái)作為我們的“You Win”或者“You Lose”指示器吧。在Xcode中,選擇Classes文件夾,然后點(diǎn)擊File\New File,再選擇Objective-c類,確保NSObject是基類被選中。點(diǎn)擊下一步,然后輸入文件名GameOverScene,并且確保“Also create GameOverScene.h”復(fù)選中。
然后用下面的代碼替換掉模板生成代碼:
#import"cocos2d.h"@interface GameOverLayer : CCColorLayer {
CCLabel*_label;
}
@property (nonatomic, retain) CCLabel*label;
@end
@interface GameOverScene : CCScene {
GameOverLayer*_layer;
}
@property (nonatomic, retain) GameOverLayer*layer;
@end
相應(yīng)地修改GameOverScene.m文件:
#import"GameOverScene.h"#import"HelloWorldScene.h"@implementation GameOverScene
@synthesize layer=_layer;-(id)init {if((self=[super init])) {
self.layer=[GameOverLayer node];
[self addChild:_layer];
}returnself;
}-(void)dealloc {
[_layer release];
_layer=nil;
[super dealloc];
}
@end
@implementation GameOverLayer
@synthesize label=_label;-(id) init
{if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {
CGSize winSize=[[CCDirector sharedDirector] winSize];
self.label=[CCLabel labelWithString:@""fontName:@"Arial"fontSize:32];
_label.color=ccc3(0,0,0);
_label.position=ccp(winSize.width/2, winSize.height/2);
[self addChild:_label];
[self runAction:[CCSequence actions:
[CCDelayTime actionWithDuration:3],
[CCCallFunc actionWithTarget:self selector:@selector(gameOverDone)],
nil]];
}returnself;
}-(void)gameOverDone {
[[CCDirector sharedDirector] replaceScene:[HelloWorld scene]];
}-(void)dealloc {
[_label release];
_label=nil;
[super dealloc];
}
@end
GameOverLayer僅僅只是在屏幕中間旋轉(zhuǎn)一個(gè)label,然后調(diào)度一個(gè)transition隔3秒后回到HelloWorld場(chǎng)景中。
勝利場(chǎng)景
現(xiàn)在,讓我們添加一些代碼,使得玩家吃完所有的西瓜的時(shí)候,游戲會(huì)結(jié)束。在HelloWorld類的setPlayerPositoin:方法中添加以下代碼,(位于HelloWorldScene.m中,就是update代碼后面:)
//put the number of melons on your map in place of the '2'if(_numCollected==2) {
[self win];
}
然后,在HelloWorld類中創(chuàng)建win方法:
-(void) win {
GameOverScene*gameOverScene=[GameOverScene node];
[gameOverScene.layer.label setString:@"You Win!"];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
}
不要忘了包含頭文件:
#import"GameOverScene.h"
編譯并運(yùn)行,當(dāng)你吃完所有的西瓜后,就會(huì)出現(xiàn)如下畫(huà)面:
失敗場(chǎng)景
就這個(gè)教程而言,我們的玩家只要有一個(gè)敵人碰到他,游戲是結(jié)束了。在HelloWorld類的testCollision方法中添加以列循環(huán):
for(CCSprite*targetin_enemies) {
CGRect targetRect=CGRectMake(
target.position.x-(target.contentSize.width/2),
target.position.y-(target.contentSize.height/2),
target.contentSize.width,
target.contentSize.height );if(CGRectContainsPoint(targetRect, _player.position)) {
[self lose];
}
}
這個(gè)循環(huán)遍歷所有的敵人,只要有一個(gè)敵人精靈的圖片所在的矩形和玩家接觸到了,那么游戲就失敗了。接下,再創(chuàng)建lose方法:
-(void) lose {
GameOverScene*gameOverScene=[GameOverScene node];
[gameOverScene.layer.label setString:@"You Lose!"];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
}
編譯并運(yùn)行,一旦有一個(gè)敵人碰到你,你就會(huì)看到下面的場(chǎng)景:
完整源代碼
這里有這個(gè)教程的完整源代碼。謝謝你們有耐心看到這里。
接下來(lái)怎么做?
建議:
增加多個(gè)關(guān)卡
增加不同類型的敵人
在Hud層中顯示血條和玩家生命
制作更多的道具,比如加血的,武器等等
一個(gè)菜單系統(tǒng),可以選擇關(guān)卡,關(guān)閉音效,等等
使用更好的用戶界面,來(lái)使游戲畫(huà)面更加精美,投擲飛鏢更加瀟灑。
總結(jié)
以上是生活随笔為你收集整理的Java敌人跟地图的碰撞_(译)加入敌人和战斗:如果使用cocos2d制作基于tiled地图的游戏:第三部分...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 字符串拼接数字 java_使用JAVA代
- 下一篇: 新星科技有限公司java_「新星科技」