这一章我们讲一下特殊门的打开问题,游戏中有两中特殊门,在第二层中我们可以看到
左边的特殊门是需要小偷帮你打开的代码之前已经添加过了;右边的特殊门是根据在地图中多次用到大部分都是走过去直接就可以打开的,在第七层中是需要把四个守卫打败才能打开,在我们的二十一层中的两个门是把boss打败之后就能打开了。
那么接下来我们就开始添加右面特殊门的开启了,我们需要在if(other_tileGid)中添加:
NSString *value1 = [propsvalueForKey:@"door"];
int type1 = [value1intValue];
if (type1 ==4)
{
[self.curtitleMap.otherremoveTileAt:towerLoc];
}
if (type1 ==5)
{
if (self.curtitleMap.monsterInfor.mosterListCount <= 0)
{
[self.curtitleMap.otherremoveTileAt:towerLoc];
}
}
到这里我们这个特殊门就能打开了。
其实我们的开门就是达到一定的条件删除门的图块就行了。
下面我们来讲一下勇士的行走动画,首先我们要在Hero类中定义四个属性分别代表上下左右行走动画:
@property (nonatomic,retain)CCAnimation *upAction;
@property (nonatomic,retain)CCAnimation *downAction;
@property (nonatomic,retain)CCAnimation *rightAction;
@property (nonatomic,retain)CCAnimation *leftAction;
然后我们在Hero.m文件中添加一个勇士行走动画加载方法:
-(CCAnimation*)addHeroAnimate:(int)direction
{
CCTexture2D *heroTexture = [[CCTextureCachesharedTextureCache]addImage:@"hero.png"];
CCSpriteFrame *frame0,*frame1,*frame2,*frame3;
//第二个参数表示显示区域的x,y,width,height,根据direction来确定显示的y坐标
frame0 = [CCSpriteFrameframeWithTexture:heroTexturerect:CGRectMake(32*0,32*direction,32,32)];
frame1 = [CCSpriteFrameframeWithTexture:heroTexturerect:CGRectMake(32*1,32*direction,32,32)];
frame2 = [CCSpriteFrameframeWithTexture:heroTexturerect:CGRectMake(32*2,32*direction,32,32)];
frame3 = [CCSpriteFrameframeWithTexture:heroTexturerect:CGRectMake(32*3,32*direction,32,32)];
NSMutableArray *animFrames = [NSMutableArrayarray];
[animFrames addObject:frame0];
[animFrames addObject:frame1];
[animFrames addObject:frame2];
[animFrames addObject:frame3];
//循环动画序列
CCAnimation *animation = [CCAnimation animationWithFrames:animFrames delay:0.05f];
return animation;
}
并在初始化方法中调用这个方法加载勇士的行走动画
//加载勇士动画
self.upAction = [selfaddHeroAnimate:3];
self.downAction = [selfaddHeroAnimate:0];
self.rightAction = [selfaddHeroAnimate:2];
self.leftAction = [selfaddHeroAnimate:1];
接下来就是在Game01.m中添加播放动画了,在触摸响应事件中上下左右四个检测方法中添加其相应的代码:
CCAnimate *animate = [CCAnimateactionWithAnimation:_hero.upAction];
CCSequence *seq = [CCSequenceactions: animate,nil];
[_herorunAction:seq];
也可以添加到updateMove方法里。
还有我们游戏中有一些特殊的怪物,当勇士攻击它们的时候他们会释放一个“魔法”来减少勇士一些血量。这样我们就需要在计算勇士掉血量的时候添加一些判断了。
首先我们要在Game01中检测碰撞方法中if(enemy_tiledGid)添加如图所示代码:
还有在FightLayer.m中添加如图所示代码:
还有在MonsterInformation.m中更新损失血量的方法中添加如图所示代码:
这部分代码很简单,你可以自己修改掉血量,或者自己添加一些特殊的“魔法”。
我们游戏中还有一种有剧情的特殊怪物,一个是19层的boss(编号24),还有一个21层的boss(编号7),我们先说一下19层的boss,他是在打之前有一个剧情,打败之后还有一个剧情,21层的boss是在打败之后有一个剧情。检测碰撞方法中if(enemy_tiledGid)添加如图所示代码:
移除怪物的方法中添加如图所示代码:
特殊图块的隐藏问题
首先我们说一下18层的上楼楼梯隐藏问题,这里我是在楼梯上面覆盖一个路面图块,这样我们就看不见楼梯了,在这里隐藏楼梯就要通过覆盖在上面的路面图块来区分了。下面我们就开是添加这一部分的代码:
首先我们要在TitledMap类中添加一个属性(这个是一个开关变量)
@property (nonatomic,assign)bool hiddenFloor;
用于来判断楼梯是否需要隐藏。我们首先要在Game01.m碰撞检测方法中if(other_tileGid)方法中添加如下代码:
NSString *value2 = [propsvalueForKey:@"hidden"];
if (value2)
{
self.curtitleMap.hiddenFloor =YES;
}
当检测到当前楼层是隐藏楼层之后就开启隐藏开关,这部分检测必须要添加到楼梯检测之前。下面我们就要在if(upfloor_tileGid)中做出如下修改:
if (!self.curtitleMap.hiddenFloor)
{
self.curtitleMap.delivery =YES;
[selfupdateGameLayer:1];
}
这样在我们的上楼楼梯在开启隐藏开关的时候就不能上楼了。20层上楼楼梯也可以这样解决。
好了这里我们将其隐藏了下面我们就要开启它了,首先我们来看一下npc检测方法中case 3: (小偷)第一部分是打开二楼的门,第二部分是开启18层隐藏路面。这里我们有一个参数thiefID他是用来控制我们需要加载小偷的那次谈话 。在那里我们的勇士将遇到公主,和公主交谈之后就可以开启上楼楼梯了。下面我们就要把talkTonpc(交谈之后响应事件)方法中case 10:中的注释去掉:这样我们18层的问题就解决了。但是想让我们的小偷帮我们打通18层的隐藏路面,就得找到一把铁榔头就可以了。我们需要在道具检测if(item_tileGid)中的if(value5)中添加canopen = YES;就行了。
下面我讲一下我们的游戏你们需要自己添加的部分,一个是序章中仙子,玩过魔塔的同学都知道仙子一开始不会消失会移动到旁边的位置。还有关于24层魔塔开启的问题,16层有一个隐藏的神秘老人,上面覆盖了一块和其它不一样的墙。这个是开启二十四层的关键,遇到他之后会给你一个灵仗把这个灵仗给序章中的仙子就能开启24层,大家有兴趣的就自己添加一下24层吧,这样可以测试一下你是否已经掌握了这个游戏的制作方法,同时也会让你对cocos2d和Tiled地图编辑器更好的理解。同时你要是感觉那些方法你有更好的解决方法你也可以自己试着修改一下,或者找我交流一下也可以欢迎随时打扰。另外你也可以试着制作一下魔塔50层或者60层,只需要在我们这个魔塔20层的基础上再做一套50层或60层的地图并相应的修改一下游戏参数就行了。希望我的游戏教程能对你的学习有所帮助,谢谢大家的支持!
今天我们来讲一下我们的游戏存储问题,首先我们要说一下我们这个游戏我只是在游戏过程中可以进行存读数据,退出则没有进度。我没有真正的把它存到文件或者数据库中。要知道我们的游戏中有很多游戏数据需要存储,如:勇士的信息,还有地图信息,游戏中的一些开关变量等。
下面我们就来开始添加这一部分的内容吧,我们先说一下地图信息的存储问题,我们在游戏中对地图图块的操作是不可能改变原地图的,而且我们没有找到很好的方式对操作过的地图进行深复制,所以在这里我是把删除的图块坐标存储起来,读取的时候是重新初始化一套地图,然后把之前的删除的图块在新的地图上删除一遍就达到了我们的游戏地图存储效果。不管怎样我们先来看一下我们的代码吧:
我们要现在TiledMap这个类中添加几个属性:
@property (nonatomic,retain)NSMutableArray *itemArray;
@property (nonatomic,retain)NSMutableArray *enemyArray;
@property (nonatomic,retain)NSMutableArray *doorArray;
@property (nonatomic,retain)NSMutableArray *otherArray;
@property (nonatomic,retain)NSMutableArray *npcArray;
我们的游戏地图会变化的层都相应的添加一个用于存储数据的数组。
记得要在初始化方法中初始化这几个数组!!!
接着我们还要建一个坐标类RemovePoint代码:
RemovePoint.h代码:
#import<Foundation/Foundation.h>
#import"cocos2d.h"
@interface RemovePoint :NSObject
{
}
@property (nonatomic,assign)CGPoint point;
@end
RemovePoint.m代码:
#import"RemovePoint.h"
@implementation RemovePoint
@synthesize point;
-(id)init
{
if ((self = [superinit]))
{
}
returnself;
}
@end
下面我们就要添加存读游戏的方法了,这些方法是写在Herohp类中的,我们先来看一下存储游戏的方法:
-(void)onsaveGame
{
//判断是否有旧的进度
if (model.titleMapArray2.count >1)
{
//如果有重新初始化数组
[model.titleMapArray2removeAllObjects];
model.titleMapArray2 =nil;
model.titleMapArray2 = [[[NSMutableArrayalloc]init]autorelease];
}
int i = 0;
for (TitledMap *_titleMapinmodel.titleMapArray1)
{
TitledMap *titledMap = [TitledMap initWithAnalytic:i];
for (RemovePoint *point in _titleMap.itemArray)
{
[titledMap.itemArrayaddObject:point];
}
for (RemovePoint *point in _titleMap.enemyArray)
{
[titledMap.enemyArrayaddObject:point];
}
for (RemovePoint *point in _titleMap.doorArray)
{
[titledMap.doorArrayaddObject:point];
}
for (RemovePoint *point in _titleMap.npcArray)
{
[titledMap.npcArrayaddObject:point];
}
for (RemovePoint *point in _titleMap.otherArray)
{
[titledMap.otherArrayaddObject:point];
}
titledMap.delivery = _titleMap.delivery;
titledMap.hiddenFloor = _titleMap.hiddenFloor;
[model.titleMapArray2addObject:titledMap];
i++;
}
//Game01中数据存储
[hero.parentSaveModel];
//勇士类中数据存储
[heroSaveModel];
//Herohp中数据存储
[selfSaveModel];
}
这里我们的titleMapArray1是当前游戏进度数组,titleMapArray2是存储游戏进度数组,数组1是在游戏进行的过程中不断变化的,而我们的数组2只是在存读数据的时候用到,在我们这个方法里我们首先要判断数组2是否为空,如果不为空则重置数组,接着我们来看我们的for循环,我们要进行存储进度就必须重新初始化一组地图,然后把我们的数组1的数据进行复制并存储到数组2中。for循环下面还有三个调用方法,这三个方法对我们游戏中其它数据进行存储,这里我就不一一讲解了,你自己思考一下都有那些数据需要存储,然后想办法进行存读就行了。有了这个存储进度的方法我们就只需要将我们删除过的图块保存到其相应的数组中就行了。
下面我来简要说一下如何保存删除图块,保存删除图块主要代码:
//保存删除图块的方法
RemovePoint *mypoint = [[[RemovePointalloc]init]autorelease];
//删除图块的位置
mypoint.point =towerLoc;
//添加到相应的数组中
[self.curtitleMap.enemyArrayaddObject:mypoint];
在我们游戏中所有删除图块的地方都添加上这三行代码我们的任务就算完成了,记住删除的是哪一个图层的图块就添加到图层相对应的数组中。
还有一点就是这里我没有把它存到数据库中,或者写入文件中对数据进行保存,在程序退出的时候我们的数据就没有了。有兴趣的同学可以把这些数据存到数据库中或写入文件中试试。
下面在我们添加读取数据之前我们要在TitledMap中添加一个方法:
-(void)removeTileGIDAt
{
for (RemovePoint *pointinself.npcArray)
{
[self.npcremoveTileAt:point.point];
}
for (RemovePoint *pointinself.itemArray)
{
[self.itemremoveTileAt:point.point];
}
for (RemovePoint *pointinself.enemyArray)
{
[self.enemyremoveTileAt:point.point];
}
for (RemovePoint *pointinself.doorArray)
{
[self.doorremoveTileAt:point.point];
}
for (RemovePoint *pointinself.otherArray)
{
[self.otherremoveTileAt:point.point];
}
}
这个方法就是删除进度中已经删除的图块。
下面我们来看一下数据读取方法:
-(void)onreadGame
{
if (model.titleMapArray2.count >1)
{
if (model.titleMapArray1.count >1)
{
[model.titleMapArray1removeAllObjects];
model.titleMapArray1 = [[[NSMutableArrayalloc]init]autorelease];
}
int i = 0;
for (TitledMap *_titleMapinmodel.titleMapArray2)
{
TitledMap *titledMap = [TitledMap initWithAnalytic:i];
for (RemovePoint *point in _titleMap.itemArray)
{
[titledMap.itemArrayaddObject:point];
}
for (RemovePoint *point in _titleMap.enemyArray)
{
[titledMap.enemyArrayaddObject:point];
}
for (RemovePoint *point in _titleMap.doorArray)
{
[titledMap.doorArrayaddObject:point];
}
for (RemovePoint *point in _titleMap.npcArray)
{
[titledMap.npcArrayaddObject:point];
}
for (RemovePoint *point in _titleMap.otherArray)
{
[titledMap.otherArrayaddObject:point];
}
titledMap.delivery = _titleMap.delivery;
titledMap.hiddenFloor = _titleMap.hiddenFloor;
[titledMapremoveTileGIDAt];
[titledMapcreateMonsterInformation];
[model.titleMapArray1addObject:titledMap];
i++;
}
[hero.parentReadModel];
[heroReadModel];
[selfReadModel];
}
}
这里我们调用了删除图块的方法,并且我们在删除图块之后调用了创建怪物查看信息界面。还有我们要修改之前写的一些代码:
首先我们要把TitledMap.m中初始化方法中的[selfcreateMonsterInformation];
删除掉,并在GameModel.m加载地图方法中修改成如下代码:
if (self.titleMapArray1.count > 1)
{
[self.titleMapArray1removeAllObjects];
self.titleMapArray1 =nil;
self.titleMapArray1 = [[[NSMutableArrayalloc]init]autorelease];
}
for (int i =0; i <22; i++)
{
TitledMap *titledMap = [TitledMap initWithAnalytic:i];
[titledMapcreateMonsterInformation];
[self.titleMapArray1addObject:titledMap];
}
这里我们在初始化地图的时候先判断数组1是否为空,如果不为空则重新初始化数组。
下面我们再来看一下我们的重新开始游戏的方法:
-(void)onrestartGame
{
[modelinitWithMap];
[hero.parentResetModel];
[heroResetModel];
[selfResetModel];
}
好了我们的魔塔教程终于结束了!谢谢大家的支持,如果有什么问题可以加我qq:864635084