(译)如何使用cocos2d制作一个塔防游戏:第二部分

转自:http://www.cnblogs.com/zilongshanren/archive/2011/07/10/2102407.html


教程截图:

  欢迎来到《如何使用cocos2d制作一个塔防游戏》的第二部分--今天,我们将添加代码来放置炮塔。哈哈,放置炮塔,很hgih吧!如果你还没有读过(译)如何使用cocos2d来制作一个塔防游戏:第一部分,你可能要先看看再继续。

  好吧,你从上个教程可能学到了,那个还不是完整真实的游戏--我们需要使之更真实。但是,我们并不需要一个复杂的A*算法,或者DFS/BFS(深度优先和广度优先),或者Best-first算法来查找路径。实际上,我们根本不需要基于tile的地图。但是,为了遵循KISS原则(keep it simple,stupid),我们还是采用tild map的做法。

  所以,让我们来看看如何放置炮塔,以及炮塔是怎么工作的----请原谅我的烂枪法。

  这里有本教程的完整源代码

  炮塔放置的方式就是,玩家从Game HUD 中点选中一个塔,然后拖动到某个可以放置的位置,然后松手就ok了。塔也有自己的攻击范围,用一个带阴影的圆表示。这个圆会在你点选中Hud中的炮塔的时候显示出来,放置好炮塔之后就消失了,表明不可以再移动了,除非把它卖掉!现在,有意思的部分来了----如果用户把塔拖到沙漠地带,我们就在那个位置显示一个”机关炮“。如果拖到其他地方(比如creep行走的路上面),我们就让它不可以放置,如果松手,塔就会回到hud里面。 

  你可以看到,好多地方都有沙子,一个塔可以放置在一个给定的tile上面。我们实现的方式就是两个函数---”tileCoordForPosition“和”addTower“方法。  “tileCoordForPosition” 是一种快速定位给定point是哪个tile的坐标点,而“addTower”函数就是实际放置炮塔的方法。具体实现如下:

复制代码
-  (CGPoint) tileCoordForPosition:(CGPoint) position
{
int  x  =  position.x  /  self.tileMap.tileSize.width;
int  y  =  ((self.tileMap.mapSize.height  *  self.tileMap.tileSize.height)  -  position.y)  /  self.tileMap.tileSize.height;
return  ccp(x,y);
}

- ( void )addTower: (CGPoint)pos {
DataModel 
* =  [DataModel getModel];
Tower 
* target  =  nil;
CGPoint towerLoc 
=  [self tileCoordForPosition: pos];

int  tileGid  =  [self.background tileGIDAt:towerLoc];
NSDictionary 
* props  =  [self.tileMap propertiesForGID:tileGid];
NSString 
* type  =  [props valueForKey: @" buildable " ];

NSLog(
@" Buildable: %@ " , type);
if ([type isEqualToString:  @" 1 " ]) {
target 
=  [MachineGunTower tower];
target.position 
=  ccp((towerLoc.x  * 32 + 16 , self.tileMap.contentSize.height  -  (towerLoc.y  * 32 - 16 );
[self addChild:target z:
1 ];

target.tag 
= 1 ;
[m._towers addObject:target];
else  {
NSLog(
@" Tile Not Buildable " );
}
}
复制代码

  你可能看到“addTower”方法在我们用户想让他放置在某个位置的时候被调用了---然后我们使用“tileCoordPosition”方法来得到tile位置,并且使用这个位置来得到tile本身。然后查找tile的属性是不是“buildable”的。如果属性值==1,那么就可以放置。相应的,我们就创建一个tower,然后计算一下放置的位置,然后把塔加到层里去。如果属性“buidable”==null或者0的话,那么就什么也不做,只输出一句话。

  由于我们刚刚提到了tower类,所以,它的实现非常重要,让我们来看一看:

复制代码
#import  " cocos2d.h "
#import 
" DataModel.h "

@interface Tower : CCSprite {
int  _range;
CCSprite 
*  selSpriteRange;
}

@property (nonatomic, assign) 
int  range;
@end

@interface MachineGunTower : Tower {
}
+  (id)tower;
-  ( void )towerLogic:(ccTime)dt;
@end
复制代码

 

  这是一个非常基础的类---我们定义了炮塔可以攻击的范围,同时还有选中一个炮塔并移动它的时候,会出现的一个虚拟的射程范围精灵。你可能也注意到了,我们还定义了一个MachineGunTower类,它从Tower派生过来的。因为,每一种tower都有一些特殊的属性,所以我们定义不同的类来使得事情变得更加简单,并且更容易管理。基本上,所有的tower都有一些共同的特点,比如射程是多少,杀伤力是多少等。

  接下来,看它的实现:

复制代码
#import  " Tower.h "

@implementation Tower
@synthesize range 
=  _range;
@end

@implementation MachineGunTower
+  (id)tower {

MachineGunTower 
* tower  =  nil;
if  ((tower  =  [[[super alloc] initWithFile: @" MachineGunTurret.png " ] autorelease])) {
tower.range 
= 200 ;
[tower schedule:@selector(towerLogic:) interval:
0.2 ];
}
return  tower;
}

- (id) init
{
if  ((self = [super init]) ) {
// [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
return  self;
}

- ( void )towerLogic:(ccTime)dt {
}
@end
复制代码

 

  这里也没太多好说的---只是一个基类,我们甚至不需要实现init方法。因为,我们将在下一个教程中添加一些代码,这里暂时空白,不过并没有关系。我们加载炮塔的图片,同时设置射程,并且创建了一个towerLogic调度器,时间间隔是0.2秒。这样,塔就可以坐在那儿了,但是,这个towerLogic方法还是空的,不过下个教程中,我们将添加一些代码!

  现在,你看到了tower的代码,接下来一件比较重要的事,就是向你展示如何实现游戏的hud--它里面包含了可以被选中的tower,而且可以把它们拖到游戏layer中去。这时,我们有两个层被加到场景中去了,下面是gameHud的代码:

复制代码
#import  " cocos2d.h "
@interface GameHUD : CCLayer {
CCSprite 
*  background;
CCSprite 
*  selSpriteRange;
CCSprite 
*  selSprite;
NSMutableArray 
*  movableSprites;
}
+  (GameHUD  * )sharedHUD;
@end
复制代码

  这里,我们定义了层的背景图片,selSprite是tower图片的一个拷贝,可以选中它,并在屏幕上拖动。而selSpriteRange是一个射程图片,当我们选中一个塔在屏幕上移动的时候,在塔的周围会有一个圆形的阴影,代表了塔的射程。最后,我们定义了movableSprites数组,它是gameLayer中可以供玩家选取的所有的炮塔图片的集合。

  让我们看看gameHud的具体实现吧:

复制代码
- (id) init
{
if  ((self = [super init]) ) {
CGSize winSize 
=  [CCDirector sharedDirector].winSize;

//  Draw the background of the game HUD
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGB565];
background 
=  [CCSprite spriteWithFile: @" hud.png " ];
background.anchorPoint 
=  ccp( 0 , 0 );
[self addChild:background];
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_Default];

//  Load the images of the towers we'll have and draw them to the game HUD layer
movableSprites  =  [[NSMutableArray alloc] init];
NSArray 
* images  =  [NSArray arrayWithObjects: @" MachineGunTurret.png " @" MachineGunTurret.png " @" MachineGunTurret.png " @" MachineGunTurret.png " , nil];
for ( int  i  = 0 ; i  <  images.count;  ++ i) {
NSString 
* image  =  [images objectAtIndex:i];
CCSprite 
* sprite  =  [CCSprite spriteWithFile:image];
float  offsetFraction  =  (( float )(i + 1 )) / (images.count + 1 );
sprite.position 
=  ccp(winSize.width * offsetFraction,  35 );
[self addChild:sprite];
[movableSprites addObject:sprite];
}
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:
0  swallowsTouches:YES];
}
return  self;
}
复制代码

 

  我希望代码中那两行注释已经解释清楚所有的一切了。首先,我们为gameLayer加载了一张背景图片,然后我们循环遍历一个图片名字数组,并用这些图片名字创建精灵,然后把这些精灵存到movableSprites数组中去。你可以从Ray的教程中找到一种方法来把四个图片放置到同一行上面。

  现在,让我们来处理一下gameLayer的touch事件:

复制代码
-  (BOOL)ccTouchBegan:(UITouch  * )touch withEvent:(UIEvent  * ) event  {
CGPoint touchLocation 
=  [self convertTouchToNodeSpace:touch];

CCSprite 
*  newSprite  =  nil;
for  (CCSprite  * sprite  in  movableSprites) {
if  (CGRectContainsPoint(sprite.boundingBox, touchLocation)) {
DataModel 
* =  [DataModel getModel];
m._gestureRecognizer.enabled 
=  NO;

selSpriteRange 
=  [CCSprite spriteWithFile: @" Range.png " ];
selSpriteRange.scale 
= 4 ;
[self addChild:selSpriteRange z:
- 1 ];
selSpriteRange.position 
=  sprite.position;

newSprite 
=  [CCSprite spriteWithTexture:[sprite texture]];  // sprite;
newSprite.position  =  sprite.position;
selSprite 
=  newSprite;
[self addChild:newSprite];
break ;
}
}
return  TRUE;
}
复制代码

 

  上面这段代码做了些什么事呢--首先,遍历“movableSprite”数组,然后使用CCRectContainsPoint方法来判断touchLocation是不是在图片的范围之内。如果是的话,那么就通知Model对象,把gestureRecognizer禁用掉。这个变量是指向  "UIPanGestureRecognizer"的。当我们选中一个炮塔的时候,就存储当前选中的炮塔放到selSprite中,并且加载射程范围图片。这样的话,当你放置一个炮塔,你就知道它能够射击多远了。

复制代码
-  ( void )ccTouchMoved:(UITouch  * )touch withEvent:(UIEvent  * ) event  {
CGPoint touchLocation 
=  [self convertTouchToNodeSpace:touch];

CGPoint oldTouchLocation 
=  [touch previousLocationInView:touch.view];
oldTouchLocation 
=  [[CCDirector sharedDirector] convertToGL:oldTouchLocation];
oldTouchLocation 
=  [self convertToNodeSpace:oldTouchLocation];

CGPoint translation 
=  ccpSub(touchLocation, oldTouchLocation); 

if  (selSprite) {
CGPoint newPos 
=  ccpAdd(selSprite.position, translation);
selSprite.position 
=  newPos;
selSpriteRange.position 
=  newPos;

DataModel 
* =  [DataModel getModel];
CGPoint touchLocationInGameLayer 
=  [m._gameLayer convertTouchToNodeSpace:touch];

BOOL isBuildable 
=  [m._gameLayer canBuildOnTilePosition: touchLocationInGameLayer];
if  (isBuildable) {
selSprite.opacity 
= 200 ;
else  {
selSprite.opacity 
= 50 ;
}
}
}
复制代码

 

  现在,上面的代码可以移动炮塔在屏幕上来回移动,并且可以知道哪些地方可以放塔,哪些地方不可以。这里更有趣的地方是,我添加了 canBuildOnTilePosition方法,它可以帮助我们判断那个地方是否可以放置一个炮塔。

复制代码
-  (BOOL) canBuildOnTilePosition:(CGPoint) pos
{
CGPoint towerLoc 
=  [self tileCoordForPosition: pos];
int  tileGid  =  [self.background tileGIDAt:towerLoc];
NSDictionary 
* props  =  [self.tileMap propertiesForGID:tileGid];
NSString 
* type  =  [props valueForKey: @" buildable " ];

if ([type isEqualToString:  @" 1 " ]) {
return  YES;
}
return  NO;
}
复制代码

  这里还是采用检查对于位置tile的属性的值,如果是1,则表明可以放置,返回yes。否则就返回no。如果是不可以建筑的,我们就把选中的精灵设置透明度为50,如果是的话,则把透明度设置为200.我们也可以这样,显示红色的圈,则表示不能建筑,如果是绿色的,则表示可以建筑。这个功能留给读者自行添加。

  现在回到touchEnd事件处理:

复制代码
-  ( void )ccTouchEnded:(UITouch  * )touch withEvent:(UIEvent  * ) event  {
CGPoint touchLocation 
=  [self convertTouchToNodeSpace:touch];
DataModel 
* =  [DataModel getModel];

if  (selSprite) {
CGRect backgroundRect 
=  CGRectMake(background.position.x,
background.position.y,
background.contentSize.width,
background.contentSize.height);

if  ( ! CGRectContainsPoint(backgroundRect, touchLocation)) {
CGPoint touchLocationInGameLayer 
=  [m._gameLayer convertTouchToNodeSpace:touch];
[m._gameLayer addTower: touchLocationInGameLayer];
}

[self removeChild:selSprite cleanup:YES];
selSprite 
=  nil;
[self removeChild:selSpriteRange cleanup:YES];
selSpriteRange 
=  nil;
}
m._gestureRecognizer.enabled 
=  YES;
}
复制代码

  这里需要检查的就是,我们可能取消我们的放置操作。如果玩家把tower又放置到hud 背景上,那么就应该把这个选中的精灵和射程图片都清除掉。

  我们又一次做到了!现在,我们定义了一个hud层,上面放置了一些炮塔,我们可以从hud里面拖一塔出来,放置在地图中。接下来,我们会把第一部分和第二部分教程合起来,然后添加tower射击的功能。

  创建游戏HUD,并且使得在游戏层里面的移动与手势识别无关。

  显示了把炮塔从一个地方拖到另一个地方。
  还有一些内容请参考这个链接:tile map editor tool!

  仍然有待解决的问题:

  1.如何让炮塔开火并杀死creep。
  2.如何制作一波一波的creep,如何制作特殊creep。

ps:本人水平有限,翻译不准或有误的地方请您不吝指出,谢谢!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值