【吼吼睡cocos2d学习笔记】第四章 - 第一个游戏

【吼吼睡cocos2d学习笔记】第四章 - 第一个游戏,第1张

概述来让我们开始第一个游戏的制作。 这个过程可能有点艰辛,但是只要坚持下来,第一个游戏往往能给我们带来巨大的收益(当然这个收益不是经济上的:-P) 先上截图: iPad中: 游戏构思 角色 在屏幕的上方,有一定数量的敌人(蜘蛛),屏幕下方有一只玩家控制的熊猫。 游戏流程 每间隔一段时间,会有一只蜘蛛爬下来袭击熊猫,熊猫通过移动来躲避攻击。随着游戏的进行,蜘蛛下降的速度会越来越快,出动的频率会越来越高。

来让我们开始第一个游戏的制作。

这个过程可能有点艰辛,但是只要坚持下来,第一个游戏往往能给我们带来巨大的收益(当然这个收益不是经济上的:-P)

先上截图:


iPad中:


游戏构思

角色

在屏幕的上方,有一定数量的敌人(蜘蛛),屏幕下方有一只玩家控制的熊猫。

游戏流程

每间隔一段时间,会有一只蜘蛛爬下来袭击熊猫,熊猫通过移动来躲避攻击。随着游戏的进行,蜘蛛下降的速度会越来越快,出动的频率会越来越高。

胜负判定

熊猫躲避过一定数目的蜘蛛以后获胜,在此之前玩家用完所有生命则失败。

游戏展示动画,其实是有音效的,可能屏幕录像的软件不能捕捉来自模拟器的声音:展示动画

分析

我们按照事务流的方式来对整个游戏进行简单分析:

1.启动游戏,加载主页面。本示例不做菜单,不做配置,直接进入游戏场景。

2.将游戏置为READY状态;初始化各种数据;根据屏幕的宽度计算蜘蛛的个数,初始化蜘蛛精灵;初始化熊猫精灵

3.当玩家触摸屏幕以后,游戏开始,游戏状态设置为PLAYING,启动以下计时器:

3a.播放蜘蛛帧动画的计时器

3b.搜索下一个出动的蜘蛛计时器

3c.碰撞检测的计时器

4.玩家用手指控制熊猫在屏幕下方移动,这里要注意的是,接收触摸事件的是熊猫,如果触摸点不在熊猫上,它是不能移动的。因此需要给熊猫精灵添加一个targetedtouchDelegate。同时要防止熊猫划出屏幕边界。

5.定时检测下一个出动的蜘蛛,找到以后,让蜘蛛下移的屏幕低部,然后复位,如果熊猫躲避过来此次攻击,加分。当出动的蜘蛛次数超过一定量的时候(本游戏中是8次),加快游戏速度,加快蜘蛛出动频率。同时判断是否已经满足胜利条件。

6.当碰撞检测计时器检测到碰撞后,停止出动蜘蛛计时器、碰撞检测计时器、动画播放计时器,生命减一,判断是否还有剩余生命。如果没有,Game Over,游戏状态设置为END;如果还有生命,游戏状态设置为DEAD。

7.当玩家触摸屏幕的时候:

7.a.如果游戏状态是READY,开始游戏。

7.b 如果游戏状态是DEAD,复位蜘蛛和熊猫,启动各个计时器。

7.c 如果游戏状态为其他状态,无视。

8.对熊猫精灵的事件分析:

当熊猫精灵接收到触摸事件以后,判断是否命中到了精灵范围内,如果是,吃掉该事件,否则让该事件继续下发给其他对象。

9.注意,为了无缝的向iPad设备上移植,需要注意:计量避免出现假设性代码,所有的尺寸都根据屏幕尺寸计算。

开工

XCode->New Project->IOS->cocos2d-name it->finish!

删掉默认的helloWorld层,按照下图,创建Group:


Sprites:盛放精灵类

Layers:盛放所有层

Scenes:盛放所有的场景

RootVIEwController.m

本游戏适合在竖屏模式下进行,因此需要做以下修改:

#elif GAME_autoROTATION == kGameautorotationUIVIEwControllerreturn ( UIInterfaceOrIEntationIsLandscape( interfaceOrIEntation ) );
修改为

#elif GAME_autoROTATION == kGameautorotationUIVIEwController		return ( UIInterfaceOrIEntationIsLandscape( interfaceOrIEntation ) );


AppDelegate.m

因为删除了HelloWorld层,现在需要启动我们自己添加的GameScene场景,因此需要修改AppDelegate.m的相关代码:

找到applicationDIDFinishLaunching方法中[CCDirector sharedDirector] runWithScene的代码,此代码的作用是让【导演】运行第一个游戏场景,将之修改成:

[[CCDirector sharedDirector] runWithScene: [GameScene scene]];

GameScene使我们自己设计的场景类,在Scenes文件组中,scene是该类的初始化方法,负责返回一个GameScene对象。


做完这些以后我们来实现主场景类: GameScene

首先在.h文件中添加静态scene方法的声明:

+(CCScene *)scene;

在 .m文件中实现该方法,同时加上对资源的释放:

-(voID)dealloc{    [super dealloc];}+(CCScene *)scene{    CCScene *sc = [CCScene node];    [sc addChild:[GameLayer node]];    return  sc;}

scene方法中构造了一个CCScene对象,并将GameLayer层作为子节点加入其中。

其实在IOS开发中,scene对象中的代码量往往非常少,代码大部分出现在层和精灵中。


在看关键的GameLayer以前,我们先来看一下熊猫精灵(PandaSprite)类

这个类继承自CCSprite,同时实现了CCTargetedtouchDelegate协议。这是.m中的代码

////  GameLayer.h//  CH04////  Created by 李庆辉 on 11-12-8.//  QQ:66927785//  Blog:http://blog.csdn.net/redparty//  copyright 2011年 __MS__. All rights reserved.//#import "PandaSprite.h"@implementation PandaSprite@synthesize curLayer;//释放delegate-(voID)onExit{    [[CCtouchdispatcher shareddispatcher] removeDelegate:self];    [super onExit];}//当被node的时候,触发该事件,注册targetedDelegate-(voID)onEnter{    [[CCtouchdispatcher shareddispatcher] addTargetedDelegate:self priority:0 swallows@R_419_5033@:YES];    [super onEnter];}//获得自身的rect,用来进行命中判定-(CGRect)rect{    return CGRectMake(-rect_.size.wIDth * 0.5,-rect_.size.height * 0.5,rect_.size.wIDth,rect_.size.height);}//当touch开始的时候,判定是否命中了自身,如果是,吃掉该事件,反之忽略该事件-(BOol)cctouchBegan:(UItouch *)touch withEvent:(UIEvent *)event{        if (CGRectContainsPoint([self rect],[self converttouchToNodeSpaceAR:touch])) {        return YES;    }    return NO;}//根据玩家的触摸,变换主角的位置。-(voID)cctouchmoved:(UItouch *)touch withEvent:(UIEvent *)event{        //获得GameLayer中的gameStatus的值,如果不是PLAYING,则忽略当前触摸。    if ([curLayer getGameStatus] != @"PLAYING") {        return;    }    CGSize sizeOfWin = [[CCDirector sharedDirector] winSize];        //获得自己的尺寸的一半,用来对左右两边缘的位置进行校正    CGSize halfOfMyself;    halfOfMyself = CGSizeMake([self contentSize].wIDth * 0.5,[self contentSize].height  * 0.5);    //根据自身的大小确定自己在x轴方向上的最小值和最大值    CGfloat minX = halfOfMyself.wIDth;    CGfloat maxX = sizeOfWin.wIDth -  halfOfMyself.wIDth;        CGPoint posOftouch = [touch locationInVIEw:touch.vIEw];        CGPoint posForGL = [[CCDirector sharedDirector] convertToGL:posOftouch];        //对越界情况进行校正    if (posForGL.x < minX) {        posForGL.x = minX;    }    if (posForGL.x > maxX) {        posForGL.x = maxX;    }        //坐标系转换    posForGL.y = [self contentSize].height * 0.5;    self.position = posForGL;}@end

代码中已经注视的非常清楚了,这里不再赘述。需要说明的是,在CCtouchmoved方法中,有如下代码:

//获得GameLayer中的gameStatus的值,如果不是PLAYING,则忽略当前触摸。    if ([curLayer getGameStatus] != @"PLAYING") {        return;    }

curLayer是在.h中声明的ID类型的对象:

////  GameLayer.h//  CH04////  Created by 李庆辉 on 11-12-8.//  QQ:66927785//  Blog:http://blog.csdn.net/redparty//  copyright 2011年 __MS__. All rights reserved.//#import <Foundation/Foundation.h>#import "cocos2d.h"@interface PandaSprite : CCSprite<CCTargetedtouchDelegate> {    ID curLayer;}@property (nonatomic,retain)ID curLayer;@end

ID是Objective-C中所有节点的父类,相当于c#中的Object类。该对象将来会传入一个GameLayer的对象。之所以这样做是因为在熊猫精灵并非在任何时候都被允许移动的,只有在游戏状态为PLAYING的时候才响应该事件。具体可以参看GameLayer.m中的getGameStatus方法。这是一种在精灵和层之间传递数据的方式。


好,现在看是来看重量级的GameLayer类

先看.h文件

////  GameLayer.h//  CH04////  Created by 李庆辉 on 11-12-8.//  QQ:66927785//  Blog:http://blog.csdn.net/redparty//  copyright 2011年 __MS__. All rights reserved.//#import <Foundation/Foundation.h>#import "cocos2d.h"#import "PandaSprite.h"#import "SimpleAudioEngine.h"@interface GameLayer : cclayer {    //窗口尺寸    CGSize sizeOfWin ;    //蜘蛛的尺寸    CGSize sizeOfSpIDer;    //熊猫的尺寸    CGSize sizeOfPanda;    //盛放蜘蛛精灵的数组    CCArray *spIDers;        //盛放熊猫生命的数组    CCArray *livesPandas;    //主角精灵    PandaSprite *panda;    //蜘蛛的个数    int spIDerNumber;    //控制游戏速度的一个因子,会被用在计算蜘蛛的下降速度和下降频率上    CGfloat speed;    //当前的游戏状态,分为:READY,PLAYING,DEAD,END,OVER    Nsstring *gameStatus;    //显示分数的label    cclabelTTF *lblscoreShow;    //显示游戏信息的label    cclabelTTF *lblinfo;    //动画播放分数的label    cclabelBMFont *lblscoreAnimate;    //没有什么具体含义,仅仅被用来控制分数计算的频率    int numSpIDersMoved;        int livesCount;        //游戏得分    int score;    }//播放蜘蛛动画-(voID)playSpIDerAnimate;//重置蜘蛛们的位置-(voID)resetSpIDer;//寻找下一个行动的蜘蛛-(voID)checkSpIDer:(ccTime)dt;//将checkSpIDer寻找到的蜘蛛下坠并复位-(voID)downSpIDer:(CCSprite *)spIDer;//作为downSpIDer中action的回调函数,负责让到达屏幕底部的蜘蛛复位-(voID)makeSpIDerBack:(CCSprite *)spIDer;//碰撞检测-(voID)checkCollision;//停止所有发生在蜘蛛和主角上的动作。-(voID)stopAllAction;//返回gameStatus的值,这个值会在PandaSprite中用到-(Nsstring *)getGameStatus;//该方法根据speed来改变蜘蛛出动的频率。-(voID)changeCheckTimeout;//创建死亡label-(voID)createLables;//创建蜘蛛数组-(voID)createSpIDerArray;//创建显示生命的熊猫-(voID)createlivesPandas;//创建主角-(voID)createPanda;//初始化游戏-(voID)initGame;//用Action来显示实时分数-(voID)showAnimatescore;//当胜利的时候-(voID)whenWin;//当碰撞的时候-(voID)whenCollision;//根据当前剩余的生命显示对应个数的熊猫-(voID)showlives;//开始游戏相关的计时器-(voID)startSchedule;//停止游戏相关的计时器-(voID)stopSchedule;@end

有点多,但是每一行我的加上了注释,每个方法的实现都在.m中:

////  GameLayer.h//  CH04////  Created by 李庆辉 on 11-12-8.//  QQ:66927785//  Blog:http://blog.csdn.net/redparty//  copyright 2011年 __MS__. All rights reserved.//#import "GameLayer.h"@implementation GameLayer#define SPRITETAG 100#define LABLE_TAG 150#define score_HEIGHT 30#define  SCALE_SPIDER 0.5#define SCALE_PANDA 0.8#define FADE_score 45#define SPEED 2#define DTSPEED 250#define MAXliVES 5#define SCALE_liVESPANDA 0.25static int framIndex;//释放层用到的非autoRelease资源-(voID)dealloc{    [super dealloc];    [spIDers release];    spIDers = nil;}-(ID)init{    if (self = [super init]) {        [self initGame];       }    return  self;}-(voID)initGame{    //获得屏幕尺寸    sizeOfWin = [[CCDirector sharedDirector] winSize];    //创建label(分数、生命、死亡信息、win)    [self  createLables];        //创建蜘蛛数组    [self createSpIDerArray];        //创建主角    [self createPanda];        //创建显示生命用的熊猫    [self createlivesPandas];        //重置蜘蛛位置    [self resetSpIDer];    //预加载音效文件,如果不予加载的话,第一次播放此音效的时候会卡至少一秒钟。    [[SimpleAudioEngine sharedEngine] preloadEffect:@"bomb.caf"];    numSpIDersMoved = 1;    score = 0;    livesCount = MAXliVES;    framIndex = 1;    speed = SPEED;    gameStatus = @"READY";    [self setIstouchEnabled:YES];}-(voID)createPanda{    //添加主角    CCSpriteFrameCache *frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];    [frameCache addSpriteFramesWithfile:@"sprite.pList"];    panda = [PandaSprite node];    [panda setdisplayFrame:[frameCache spriteFrameByname:@"panda.png"]];        sizeOfPanda = [panda contentSize];    sizeOfPanda.wIDth *= SCALE_PANDA;    sizeOfPanda.height *= SCALE_PANDA;    [self addChild:panda z:3];        //将本层传入panda对象中,实现层和精灵的信息传递    panda.curLayer = self;}-(voID)createlivesPandas{    CCSpriteFrameCache *frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];    [frameCache addSpriteFramesWithfile:@"sprite.pList"];        CCSprite *tmpPanda = [CCSprite spriteWithSpriteFrame:[frameCache spriteFrameByname:@"zz1.png"]];        //获得生命区熊猫的尺寸    CGSize sizeOflivesPanda = [tmpPanda contentSize];    sizeOflivesPanda.wIDth *= SCALE_liVESPANDA;    sizeOflivesPanda.height *= SCALE_liVESPANDA;        livesPandas = [[CCArray alloc] initWithCapacity:MAXliVES];    for (int i = 0; i < MAXliVES; i++) {        CCSprite * tmpPanda = [CCSprite spriteWithSpriteFrame:[frameCache spriteFrameByname:@"panda.png"]];        [livesPandas addobject:tmpPanda];        tmpPanda.scale = SCALE_liVESPANDA;        tmpPanda.position = CGPointMake((i+1)*sizeOflivesPanda.wIDth,sizeOfWin.height - sizeOflivesPanda.height - 5 );        [self addChild:tmpPanda];    }}-(voID)createSpIDerArray{    //创建蜘蛛精灵表的帧缓存,并加载蜘蛛精灵动作的pList文件    CCSpriteFrameCache *frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];    [frameCache addSpriteFramesWithfile:@"sprite.pList"];        //生成临时蜘蛛,获取缩放以后蜘蛛的尺寸。    CCSprite* spIDer = [CCSprite spriteWithSpriteFrame:[frameCache spriteFrameByname:@"zz1.png"]];    spIDer.scale = SCALE_SPIDER;    sizeOfSpIDer = [spIDer contentSize];    sizeOfSpIDer.wIDth *= SCALE_SPIDER;    sizeOfSpIDer.height *= SCALE_SPIDER;        //根据蜘蛛的尺寸计算可以放置的蜘蛛的个数,根据个数初始化蜘蛛数组    spIDerNumber = sizeOfWin.wIDth/sizeOfSpIDer.wIDth;    spIDers = [[CCArray alloc] initWithCapacity:spIDerNumber];    for (int i = 0; i < spIDerNumber; i++) {        CCSprite *tmpSpIDer = [CCSprite spriteWithSpriteFrame:[frameCache spriteFrameByname:@"zz1.png"]];        [spIDers addobject:tmpSpIDer];        tmpSpIDer.scale = SCALE_SPIDER;        [self addChild:tmpSpIDer z:0 tag:SPRITETAG + i];    }}-(voID)createLables{    //信息    lblinfo = [cclabelTTF labelWithString:@""  Fontname:@"Arial" FontSize:22];    lblinfo.position = CGPointMake(sizeOfWin.wIDth * 0.5,sizeOfWin.height * 0.5);    [self addChild:lblinfo z:100 tag:LABLE_TAG];    [lblinfo setVisible:NO];    [lblinfo setopacity:125];        //分数    cclabelTTF *lblscore = [cclabelTTF labelWithString:@"分数:" Fontname:@"Arial" FontSize:14];    lblscore.anchorPoint = CGPointMake(1,1);    lblscore.position =  CGPointMake(sizeOfWin.wIDth - 60 - [lblscore contentSize].wIDth/2,sizeOfWin.height - 10);    [self addChild:lblscore];        lblscoreShow = [cclabelTTF labelWithString:@"0000000" Fontname:@"Arial" FontSize:14];    lblscoreShow.anchorPoint = CGPointMake(1,1);    lblscoreShow.position =  CGPointMake(sizeOfWin.wIDth - [lblscore contentSize].wIDth/2,sizeOfWin.height - 10);    [self addChild:lblscoreShow];        //实时显示当前得分的标签,用到了BMFont,使用HIEro制作    lblscoreAnimate = [cclabelBMFont labelWithString:@"" fntfile:@"myFont.fnt"];    lblscoreAnimate.scale = 0;    [lblscoreAnimate setopacity:FADE_score];    lblscoreAnimate.position = CGPointMake(sizeOfWin.wIDth * 0.5,sizeOfWin.height * 0.5);    [self addChild:lblscoreAnimate];        //生命}-(voID)resetSpIDer{    //将蜘蛛们复位    CGSize halfSize = CGSizeMake(sizeOfSpIDer.wIDth * 0.5,sizeOfSpIDer.height * 0.5);    CGfloat leftmargin = (sizeOfWin.wIDth - sizeOfSpIDer.wIDth * spIDerNumber) * 0.5;    for (int i = 0; i < spIDerNumber; i++) {       CCSprite *spIDer = (CCSprite *)[self getChildByTag:SPRITETAG + i];        spIDer.position = CGPointMake((i+1)*sizeOfSpIDer.wIDth - halfSize.wIDth+leftmargin,sizeOfWin.height -  halfSize.height - score_HEIGHT);        [spIDer stopAllActions];    }        //将熊猫复位    panda.position = CGPointMake(sizeOfWin.wIDth * 0.5,sizeOfPanda.height * 0.5);}-(voID)playSpIDerAnimate{    CCSpriteFrameCache *frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];    [frameCache addSpriteFramesWithfile:@"sprite.pList"];    if (++framIndex >2) {        framIndex = 1;    }    for (int i = 0; i<spIDerNumber; i++) {        CCSprite *tmpspIDer = [spIDers objectAtIndex:i];        if ([tmpspIDer numberOfRunningActions] == 0) {            //为了让蜘蛛的动画产生不一致,避免所有的蜘蛛播放相同的纹理,将i也加入了计算中,最终得到的是一个介于1-2的整数            [tmpspIDer setdisplayFrame:[frameCache spriteFrameByname:[Nsstring stringWithFormat:@"zz%d.png",(framIndex + i)%2 +1]]];        }    }    }-(voID)changeCheckTimeout{    [self unschedule:@selector(checkSpIDer:)];    [self schedule:@selector(checkSpIDer:) interval:0.25 * speed];}//寻找下一个出动的蜘蛛-(voID)checkSpIDer:(ccTime)dt{    for (int i = 0; i<20; i++) {        int checkIndex = CCRANDOM_0_1() * spIDerNumber;        CCSprite *spIDer = [spIDers objectAtIndex:checkIndex];        //如果找到了一个本身没有动作的蜘蛛,说明该蜘蛛还没有出动,出动之。        if ([spIDer numberOfRunningActions] == 0)  {                        //出动蜘蛛            [self downSpIDer:spIDer];                                                                                    break;        }    }}-(voID)whenWin{    [lblinfo setString:@"You Win!"];    [lblinfo setVisible:YES];    [self stopAllAction];    [self unschedule:@selector(checkSpIDer:)];    [self unschedule:@selector(checkCollision)];    gameStatus = @"END";}//计算分数,用动画的方式现在在屏幕中间,同时累加到分数变量,显示在右上角。-(voID)showAnimatescore{    //根据当前speed计算当前得分,原则上是:速度越快,单位得分越高    int scoreBySpeed = ((SPEED+0.1)-speed) * DTSPEED;    score += scoreBySpeed;    [lblscoreAnimate setString:[Nsstring stringWithFormat:@"%d",scoreBySpeed]];    [lblscoreShow setString:[Nsstring stringWithFormat:@"%07d",score]];    //播放动画前,将label透明度调大,尺寸缩小到0    lblscoreAnimate.scale = 0;    [lblscoreAnimate setopacity:FADE_score];    //创建一个放大动作和一个隐出动作    CCAction *acS = [CCScaleto actionWithDuration:0.2 scale:3];    CCAction *acE= [CCFadeto actionWithDuration:0.2 opacity:0];        //用CCSpawn的方式同步执行两个动作    [lblscoreAnimate runAction:[CCSpawn actions:acS,acE,nil]];}//出动蜘蛛-(voID)downSpIDer:(CCSprite *)spIDer{    //蜘蛛移动的目标位置    CGPoint targetPos = CGPointMake(spIDer.position.x,[spIDer contentSize].height * 0.5);        CCAction *ac = [CCMoveto actionWithDuration:speed position:targetPos];    //当蜘蛛执行玩ac动作以后,回来执行callBack指向的回调函数:makeSpIDerBack    CCCallFuncN *callBack = [CCCallFuncN actionWithTarget:self selector:@selector(makeSpIDerBack:) ];        //用CCSequence的方式执行ac和callBack    [spIDer runAction:[CCSequence actions:ac,callBack,nil]];}//回调函数,让蜘蛛复位-(voID)makeSpIDerBack:(CCSprite *)spIDer{    CGPoint backPos = CGPointMake(spIDer.position.x,sizeOfWin.height - sizeOfSpIDer.height * 0.5 - score_HEIGHT);    CCAction *back = [CCMoveto actionWithDuration:1 position:backPos];    [spIDer runAction:back];        //播放加分动画,加分    [self showAnimatescore];        //每有一只蜘蛛被躲避开,就加快游戏速度,同时进行获胜判定    numSpIDersMoved++;    if (numSpIDersMoved %5 == 0) {        //在本游戏中,如果速度快到0.7,认为玩家获胜。        if (speed < 0.7) {            [self whenWin];            return;        }        speed -= 0.02;        [self changeCheckTimeout];        numSpIDersMoved = 1;    }    }//当用户点击屏幕的时候,根据不同的情景改变游戏状态。-(voID)cc@R_419_5033@Ended:(NSSet *)@R_419_5033@ withEvent:(UIEvent *)event{    //如果当前是死亡状态,继续游戏    if (gameStatus == @"DEAD") {        [panda stopAllActions];        [panda setVisible:YES];        [lblinfo setVisible:NO];        [self resetSpIDer];        gameStatus = @"PLAYING";        [self changeCheckTimeout];        [self startSchedule];    }        //如果当前是READY状态,开始游戏    if (gameStatus == @"READY") {        gameStatus = @"PLAYING";        [self changeCheckTimeout];        [self startSchedule];    }}//开始游戏相关的计时器-(voID)startSchedule{    //让蜘蛛动起来    [self unschedule:@selector(playSpIDerAnimate)];    [self schedule:@selector(playSpIDerAnimate) interval:0.4];        //启动碰撞检测    [self unschedule:@selector(checkCollision)];    [self schedule:@selector(checkCollision) interval:0.02];}//停止游戏相关的计时器-(voID)stopSchedule{     [self unschedule:@selector(playSpIDerAnimate)];    [self unschedule:@selector(checkCollision)];}//碰撞检测-(voID)checkCollision{    //计算熊猫和蜘蛛的最大相距半径,大于此值认为发生了碰撞。    float maxdistance = sizeOfSpIDer.wIDth * 0.45 +sizeOfPanda.wIDth * 0.45;        //依次判定每一个蜘蛛是否与熊猫发生了碰撞    for (int i = 0; i < spIDerNumber; i++) {        CCSprite *spIDer;        spIDer = [spIDers objectAtIndex:i];        //忽略没有出动的蜘蛛        if ([spIDer numberOfRunningActions] == 0) {            continue;        }        //得到当前蜘蛛和熊猫的距离        float actualdistance = ccpdistance(spIDer.position,panda.position);                if (actualdistance < maxdistance) {                       [self whenCollision];            break;                                }    }}//当碰撞发生的时候,进行处理-(voID)whenCollision{    //播放音频    [[SimpleAudioEngine sharedEngine] playEffect:@"bomb.caf"];        [self unschedule:@selector(checkSpIDer:)];    [self stopSchedule];    [self stopAllAction];    gameStatus = @"DEAD";                //blink the spIDer    CCAction *blink = [CCBlink actionWithDuration:0.5 blinks:3];    [panda runAction:blink];    livesCount--;    [lblinfo setVisible:YES];    if (livesCount == 0) {        [lblinfo setString:@"GAME OVER!"];        gameStatus = @"OVER";        return;    }    [self showlives];    [lblinfo setString:@"你挂了!点击屏幕重新来过!"];    }-(voID)showlives{    for (int i = livesCount; i<MAXliVES; i++) {        CCSprite *tmpSprite = [livesPandas objectAtIndex:i];        [tmpSprite setVisible:NO];    }}//停止所有蜘蛛和主角的动作-(voID)stopAllAction{    for (int i = 0; i < spIDerNumber; i++) {        CCSprite *spIDer = [spIDers objectAtIndex:i];        [spIDer stopAllActions];    }    [panda stopAllActions];}//该方法用在向panda精灵中传递游戏状态,实现:只有在PLAYING的时候才可以移动主角。-(Nsstring *)getGameStatus{    return gameStatus;}@end

我想注释已经足够清楚了,有序考虑到了向iPad平台的兼容,所以有大量的代码用来计算尺寸和位置。千万不要认为这是在浪费时间,记住一句话:

程序员应该尽量少写基于假设的代码

比如spIDer.positon = CGPointMake(160,32)。

你写这行代码的本意可能是想将熊猫精灵放在屏幕的底部的中间,听起来似乎不错,因为当前你做的是iphone的开发,熊猫的高度是64px。但是这都基于两个假设:

假设一:屏幕宽度是320px,显然并非所有的IOS设备都是这样。

假设二:熊猫高度是64px。

事实上,我们很容易在游戏进行到一定的程度以后,要添加新的需求,比如移植到iPad上,比如说你想增加一个关卡,这次主角是一个蚂蚁或者一只大象。那么这些基于假设的代码就会成为让你加班的原因。也很有可能会耽误你和女儿的周末晚餐⋯⋯

资源

本例中,用到了以下资源:

bomb.caf:主角死亡时候播放的音效。

sprite.pList & sprite.png:精灵贴图列表,使用Zwoptex文件制作,这个工具使用起来非常简单,大家可以Google之。

myFont.fnt & myFont.png:自定义字体类表和图像,使用hIEro制作。游戏开发必备。


说得再多,不如自己动手写一遍。

奉上源码:cocos2d-蜘蛛人源码

回见。

总结

以上是内存溢出为你收集整理的【吼吼睡cocos2d学习笔记】第四章 - 第一个游戏全部内容,希望文章能够帮你解决【吼吼睡cocos2d学习笔记】第四章 - 第一个游戏所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1092858.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-28
下一篇 2022-05-28

发表评论

登录后才能评论

评论列表(0条)

保存