如何使用cocos2d来制作一个塔防游戏:第一部分
如何使用教程截图:
我们做到了!在第一篇教程中,我们已经花时间讨论了最终要完成一个什么样的作品。现在,让我们开始写代码吧。对任何塔防游戏来说,第一步就是创建“爬行怪(Creeps)”。这些怪物会入侵你的塔防世界,你需要把它们击退。因此,我们将在这个教程里学些什么东西呢?因为,这只是教程的第一部分,所以不可能全部涉及到,下面是我们将要实现的功能列表:
怎么制作waypoint,其实就是敌人沿着固定路径的点.
如何加载一个tile map,并能从中加载对象,而不是硬编码那些对象。
怎么创建爬行怪(Creep)/坏家伙(Bad Guy))/敌人(Enemy)对象。
怎样使敌人沿着我们预先设定好的路径行走。
怎样平滑地滚动iphone屏幕。
如果没有上述这些功能特性,这个游戏就不能称之为一个合格的塔防游戏。首先,我将向大家展示,如何创建一个enemy,并让它沿着预先设定好的路径行走,具体方式就是沿着一系列的waypoint前进。到这个系列教程结束的时候,你将拥有制作塔防游戏的全部知识了,尽情发挥,去创造更加好玩的TD吧!这里有本教程的完整源代码。
一个“waypoint”在易做图里面被定义为“一组坐标集合,它标识了物理空间的一个点”。我喜欢这个定义,所以我就使用它啦。我们可以把一个舞台想像成一个物理空间,而waypoint的位置就是舞台上面的x,y值。
我们会在舞台上创建一系列的waypoint,然后让敌人沿着这些waypoint移动,直到行进至终点!听起来很复杂?其实并不是很复杂。现在,为了让我们的塔防游戏起点更高一些,我们将使用Tiled地图来做舞台,你可以从 http://www.mapeditor.org下载tile 地图编辑器。当然,我们也需要下载cocos2d,从 http://www.cocos2d-iphone.org/可以下载。这两个工具都会帮助我们完成一些让人感到很自豪的事,并且可以充分利用iphone的一些特性。
好,你之前已经下载本教程源代码了,你可能看到我们写了一大堆的类。大部分都比较清楚明了,下面是它们的列表以及功能说明:
TowerDefenseTutorialAppDelegate – 创建窗口,加载CCDirector,同时加载第一个Scene。
RootViewController – 从UIViewController继承而来,我们可以使用它方便地改变视图的朝向(orientation)
GameConfig – 目前只定义了和视图朝向相关的一些变量。
TutorialScene – 我们主要的视图对象,负责加载地图并且设置creep的位置
DataModel –一个简单的数据接口,存储了游戏的主要数据,方便查找用。
Creep – 游戏中的坏蛋,刚开始有两个,随着游戏的进行,会越来越多。
Waypoint –可以和tile map编辑器发生联系的类,跟预定义路径有关。
Wave – 控制某一个时刻Creep出现的顺序的类。
看起来好像有好多类,但是,1,2,3都是cocos2d模板自带的类,而Waypoint和Wave目前为止,它们的功能也非常简单。实际上,Waypoint只是从CCNode继承而来,它们只需要x,y值就行了,从tile map中读取。如果你们不相信我的话,可以打开Waypoint类的头文件和实现文件看一看,下面是它们的定义:
Waypoint.h:
#import "cocos2d.h"@inte易做图ce WayPoint : CCNode {}@end
Waypoint.m:
#import "WayPoint.h"@implementation WayPoint- (id) init{ if ((self = [super init])) { } return self;}@end
对于DataModel类,只要是对NSMutableArray熟悉的人都会觉得很简单,让我们直接看看代码吧:
#import "cocos2d.h"@inte易做图ce DataModel : NSObject { CCLayer *_gameLayer; NSMutableArray *_targets; NSMutableArray *_waypoints; NSMutableArray *_waves; UIPanGestureRecognizer *_gestureRecognizer;}@property (nonatomic, retain) CCLayer *_gameLayer;@property (nonatomic, retain) NSMutableArray * _targets;@property (nonatomic, retain) NSMutableArray * _waypoints;@property (nonatomic, retain) NSMutableArray * _waves;@property (nonatomic, retain) UIPanGestureRecognizer *_gestureRecognizer;;+ (DataModel*)getModel;@end 所以,这里大部分代码都是很直白的。DataModel是一个单例的类,符合NSCoding协议。我们这样做有两个原因:其一,我们这样做的目的是用来保存之后游戏的状态,其二,我们把它做成单例是因为整个游戏中,我们只想让一个DataModel对象存在。我们可以从任何类中访问DataModel,只需要包含相应的头文件,然后调用下面的方法就行了:
DataModel *m = [DataModel getModel]; 下面是单例的具体实现:
+(DataModel*)getModel{ if (!_sharedContext) { _sharedContext = [[self alloc] init]; } return _sharedContext;} 我们也保存了游戏里面所有的主要角色--“targets”是我们的缓慢爬行的敌人,“waypoints”是敌人要沿着走的路径点,而“waves“则存储wave类,wave类包含了已经出了多少个敌人了,出现敌人的速度是多少等等。
那么 UIPanGestureRecognizer和CCLayer对象呢?呃,CCLayer是指向game layer的一个引用,所有的游戏逻辑都在这个层里面发生。这里保存一个引用的话,你在其它类中可以非常方便地访问到主GameScene。而 UIPanGestureRecognizer类是用来实现平滑地滚动iphone屏幕用的。因为塔防游戏不能局限于480×320的范围,经常需要滑动地图。有了这个类,我们就可以定义任何大小的地图了。
现在,我们已经消除了对上面给出的这么多类的恐惧了。那么具体代码看起来怎么样呢。首先,让我们来看看”坏人“吧!我们已经知道”Wave“和”DataModel“类是干嘛用的了,这两个类对大家来说应该不会陌生了。先看看Creep的代码:
#import "cocos2d.h"#import "DataModel.h"#import "WayPoint.h"@inte易做图ce Creep : CCSprite { int _curHp; int _moveDuration; int _curWaypoint;}@property (nonatomic, assign) int hp;@property (nonatomic, assign) int moveDuration;@property (nonatomic, assign) int curWaypoint;- (Creep *) initWithCreep:(Creep *) copyFrom;- (WayPoint *)getCurrentWaypoint;- (WayPoint *)getNextWaypoint;@end@inte易做图ce FastRedCreep : Creep {}+(id)creep;@end@inte易做图ce StrongGreenCreep : Creep {}+(id)creep;@end 我们创建了一个creep类,里面定义了生命值,移动速度和当前处于地图上的哪个点。这里包含了我们目前为止需要了解的全部信息。我们还定义了其他两种类型的creep,因为,哪个塔防游戏没有不同类型的敌人呢?有一个快速移动的红色creep和一个行动缓慢,但是生命值很多的creep---我们还可以添加更多其它类型的creep类型,但是,这里为了简单,我们只实现这3种。
现在,因为我们已经看到头文件了,我想你肯定想知道实现文件是什么样的。但是,也是考虑简单的因素,目前我只向你展示那些对我们来说比较重要的内容。首先,让我们看看,具体creep类是怎么实现的:
@implementation FastRedCreep+ (id)creep { FastRedCreep *creep = nil; if ((creep = [[[super alloc] initWithFile:@"Enemy1.png"] autorelease])) { creep.hp = 10; creep.moveDuration = 4; creep.curWaypoint = 0; } return creep;}
这就是我们怎么实现creep的-我们只定义了一个类方法,可以用 “[FastRedCreep creep]”的方式来调用,调有之后会返回一个creep对象,然后我们就可以把它加到scene里面去,并让它工作了。因为,Creep是从CCSprite派生出来的,所以我们可以自动获得CCSprite的所有好处。当然,你也可以从CCNode派生,然后里面包含一个CCSprite的引用。具体是从CCSprite派生还是CCNode,这两者都各有利弊。(我本人喜欢从CCNode继承,因为符合”优先使用组合而不是继承“的面向对象原则,但是,有时候,为了使之能加到CCSpriteBatchNode里面去,而选择继承CCSprite,反正各有好处,大家自己去权衡)。
接下来,在Creep类中,我们要用到DataModel类和WayPoint类,先看下面代码:
- (WayPoint *)getCurrentWaypoint{ DataModel *m = [DataModel getModel]; WayPoint *waypoint = (WayPoint *) [m._waypoints objectAtIndex:self.curWaypoint]; return waypoint;}- (WayPoint *)getNextWaypoint{ DataModel *m = [DataModel getModel]; int lastWaypoint = m._waypoints.count; self.curWaypoint++; if (self.curWaypoint > lastWaypoint) self.curWaypoint = lastWaypoint - 1; WayPoint *waypoint = (WayPoint *) [m._waypoints objectAtIndex:self.curWaypoint]; return waypoint;}
这里定义了creep的获得当前位置点的方法,还有得到下一个行进点的方法。你可以看到两处同样的 “WayPoint *waypoint = (WayPoint *) [m._waypoints objectAtIndex:self
补充:综合编程 , 其他综合 ,