Java敌人跟地图的碰撞_(译)加入敌人和战斗:如果使用cocos2d制作基于tiled地图的游戏:第三部分...
免責申明(必讀!):本博客提供的所有教程的翻譯原稿均來自于互聯網,僅供學習交流之用,切勿進行商業傳播。同時,轉載時不要移除本申明。如產生任何糾紛,均與本博客所有人、發表該翻譯稿之人無任何關系。謝謝合作!
程序截圖:
這篇教程是Ray Wenderlich的《如何使用cocos2d制作基于tiled地圖的游戲》系列教程的后續。如果你還沒有看過前面兩部分的教程,可以在我的博客上找到另外兩篇我翻譯Ray的教程。
在第二部分教程中,Ray教大家如何在地圖中制作可碰撞的區域,如何使用tile屬性,如何制作可以拾取的物品以及如何動態修改地圖、如何使用“Heads up display”來顯示分數。
在這個教程中,我們將加入敵人,這樣的話,你的忍者就可以向它們扔飛鏢啦,同時還增加了勝利和失敗的游戲邏輯。但是,首先,你得下載一些相關的資源文件。
這個zip文件里面包含以下內容:
1.一個敵人精靈
2.一個忍者飛鏢,從Ray的《如何使用cocos2d制作一個簡單的iphone游戲》中拿過來的。
3.兩張按鈕的圖片,在教程的后面有使用。
在繼續學習之前,不要忘了把這些資源加入到你的工程中。
增加敵人
到第二部分教程結束的時候,游戲已經很酷了,但是它還不是一個完整的游戲。你的忍者可以輕而易舉地四處游蕩,想吃就吃。但是,什么時候玩家會勝利或者失敗呢。我們不妨想象一下,有2個敵人在追殺你的忍者,那么這個游戲會顯得更加有趣。
敵人出現的位置點
好了,回到Tiled軟件(此教程使用java版),然后打開你的Tile地圖(TileMap.tmx)。
往對象層中加入一個對象,在player附近就行,但是不要太近,否則敵人一出現玩家就Game over了。這個位置將成為敵人出現的位置點,把它命名為“EnemySpawn1”。
對象組(對象層中的所有對象組成一個對象組)中的對象被存儲在一個NSMutableDictionary中,同時使用對象名字作為key。這意味著每一個位置點必須有一個唯一的名字。盡管我們可以遍歷所有的key來比較哪個是以“EnemySpawn”開頭,但是這樣做效率很低下。相反,我們采用的做法是,使用一個屬性來表示,每個給定的對象代表一個敵人出現的位置點。
給這個對象一個屬性“Enemy”,同時賦一個值1.如果你想在這個教程的基礎上擴展,并且增加其它的不同類型的敵人,你可以使用這些敵人的屬性值來表示不同類型的敵人。
現在,制作6-10個這種敵人出現位置點對象,相應的它們離player的距離也要有一些不同。為每一個對象定義一個“Enemy”屬性,并且賦值為1.保存這張地圖并且回到Xcode。
開始創建敵人
好了,現在我們將把敵人實際顯示到地圖上來。首先在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];
}
}
第一個循環遍歷對象列表,判斷它是否是一個敵人出現的位置點。如果是,則獲得它的x和y坐標值,然后調用addEnemyAtX:Y方法把它們加入到合適的地方去。
這個addEnemyAtX:Y方法非常直白,它僅僅是在傳入的X,Y坐標值處創建一個敵人精靈。
如果你編譯并運行,你會看到這些敵人出現在你之前在Tiled工具中設定的位置處,很酷吧!
但是,這里有一個問題,這些敵人很傻瓜,它們并不會追殺你的忍者。
使它們移動
因此,現在我們將添加一些代碼,使這些敵人會追著我們的player跑。因為,player肯定會移動,我們必須動態地改變敵人的運動方向。為了實現這個目的,我們讓敵人每次移動10個像素,然后在下一次移動之前,先調整它們的方向。在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:方法創建兩個action。第一個action使之朝敵人移動10個像素,時間為0.3秒。你可以改變這個時間使之移動得更快或者更慢。第二個action將會調用enemyMoveFinished:方法。我們使用CCSequence action來把它們組合起來,這樣的話,當敵人停止移動的時候就立馬可以執行enemyMoveFinished:方法就可以被調用了。在addEnemyAtX:Y:方法里面,我們調用animateEnemy:方法來使敵人朝著玩家(player)移動。(其實這里是個遞歸的調用,每次移動10個像素,然后又調用enemyMoveFinished:方法)
很簡潔!但是,但是,如果敵人每次移動的時候面部都對著player那樣是不是更逼真呢?只需要在animateEnemy:方法中加入下列語句即可:
//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
這個代碼計算每次玩家相對于敵人的角度,然后旋轉敵人來使之面朝玩家。
忍者飛鏢
已經很不錯了,但是玩家是一個忍者啊!他應該要能夠保護他自己!
我們將向游戲中添加模式(modes)。模式并不是實現這個功能的最好方式,但是,它比其他的方法要簡單,而且這個方法在模擬器下也能運行(因為并不需要多點觸摸)。因為這些優點,所以這個教程里面,我們使用這種方法。首先將會建立UI,這樣的話玩家可以方便地在“移動模式”和“擲飛鏢”模式之間進行切換。我們將增加一個按鈕來使用這個功能的轉換。(即從移動模式轉到擲飛鏢模式)。
現在,我們將增加一些屬性,使兩個層之間可以更好的通信。在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;
同時修改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里面如何制作按鈕:簡單按鈕、單選按鈕和開關按鈕》。
在HelloWorldScene.m中添加下面的代碼,這段代碼定義了一個按鈕。
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;
}
}
編譯并運行。這時會在左下角出現一個按鈕,并且你可以打開或者關閉之。但是這并不會對游戲造成任何影響。我們的下一步就是增加飛鏢的發射。
發射飛鏢
接下來,我們將添加一些代碼來檢查玩家當前處于哪種模式下面,并且在用戶點擊屏幕的時候影響不同的事件。如果是移動模式則移動玩家,如果是射擊模式,則擲飛鏢。在ccTouchEnded:withEvent:方法里面增加下面代碼:
if(_mode==0) {//old contents of ccTouchEnded:withEvent:}else{//code to throw ninja stars will go here}
這樣可以使得移動模式下,玩家只能移動。下一步就是要添加代碼使忍者能夠發射飛鏢。在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]];
這段代碼會在用戶點擊屏幕的方向發射飛鏢。對于這段代碼的完整的細節,可以查看我翻譯的另一個文章《如何使用cocos2d來做一個簡單的iphone游戲教程(第一部分)》。當然,查看原作者的文章后面的注釋會更加清楚明白一些。
projectileMoveFinished:方法會在飛鏢移動到屏幕之外的時候移除。這個方法非常關鍵。一旦我們開始做碰撞檢測的時候,我們將要循環遍歷所有的飛鏢。如果我們不移除飛出屏幕范圍之外的飛鏢的話,這個存儲飛鏢的列表將會越來越大,而且游戲將會越來越慢。編譯并運行工程,現在,你的忍者可以向敵人投擲飛鏢了。
碰撞檢測
接下來,就是當飛鏢擊中敵人的時候,要把敵人銷毀。在HelloWorldClass類中增加以下變量(在HelloWorldScene.h文件中):
NSMutableArray*_enemies;
NSMutableArray*_projectiles;
然后初使化_projectiles數組:
//at the end of the launch projectiles section of ccTouchEnded:withEvent:[_projectiles addObject:projectile];//at the end of projectileMoveFinished:[_projectiles removeObject:sprite];
然后在addEnemyAtX:y方法的結尾添加如下代碼:
[_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];
}
最后,初始化敵人來飛鏢數組,并且調度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:)];
上面的所有的代碼,關于具體是如何工作的,可以在我的博客上查找《如何使用COCOS2D制作一個簡單的iphone游戲》教程。當然,原作者的文章注釋部分的討論更加清晰,所以我翻譯的教程,也希望大家多討論啊。代碼盡量自己用手敲進去,不要為了省事,alt+c,alt+v,這樣不好,真的!
好了,現在可以用飛鏢打敵人,而且打中之后它們會消失。現在讓我們添加一些邏輯,使得游戲可以勝利或者失敗吧!
勝利和失敗
The Game Over Scene
好了,讓我們創建一個新的場景,來作為我們的“You Win”或者“You Lose”指示器吧。在Xcode中,選擇Classes文件夾,然后點擊File\New File,再選擇Objective-c類,確保NSObject是基類被選中。點擊下一步,然后輸入文件名GameOverScene,并且確保“Also create GameOverScene.h”復選中。
然后用下面的代碼替換掉模板生成代碼:
#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
相應地修改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僅僅只是在屏幕中間旋轉一個label,然后調度一個transition隔3秒后回到HelloWorld場景中。
勝利場景
現在,讓我們添加一些代碼,使得玩家吃完所有的西瓜的時候,游戲會結束。在HelloWorld類的setPlayerPositoin:方法中添加以下代碼,(位于HelloWorldScene.m中,就是update代碼后面:)
//put the number of melons on your map in place of the '2'if(_numCollected==2) {
[self win];
}
然后,在HelloWorld類中創建win方法:
-(void) win {
GameOverScene*gameOverScene=[GameOverScene node];
[gameOverScene.layer.label setString:@"You Win!"];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
}
不要忘了包含頭文件:
#import"GameOverScene.h"
編譯并運行,當你吃完所有的西瓜后,就會出現如下畫面:
失敗場景
就這個教程而言,我們的玩家只要有一個敵人碰到他,游戲是結束了。在HelloWorld類的testCollision方法中添加以列循環:
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];
}
}
這個循環遍歷所有的敵人,只要有一個敵人精靈的圖片所在的矩形和玩家接觸到了,那么游戲就失敗了。接下,再創建lose方法:
-(void) lose {
GameOverScene*gameOverScene=[GameOverScene node];
[gameOverScene.layer.label setString:@"You Lose!"];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
}
編譯并運行,一旦有一個敵人碰到你,你就會看到下面的場景:
完整源代碼
這里有這個教程的完整源代碼。謝謝你們有耐心看到這里。
接下來怎么做?
建議:
增加多個關卡
增加不同類型的敵人
在Hud層中顯示血條和玩家生命
制作更多的道具,比如加血的,武器等等
一個菜單系統,可以選擇關卡,關閉音效,等等
使用更好的用戶界面,來使游戲畫面更加精美,投擲飛鏢更加瀟灑。
總結
以上是生活随笔為你收集整理的Java敌人跟地图的碰撞_(译)加入敌人和战斗:如果使用cocos2d制作基于tiled地图的游戏:第三部分...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 字符串拼接数字 java_使用JAVA代
- 下一篇: java的flush方法_Java中的B