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

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

这个过程可能有点艰辛,但是只要坚持下来,第一个游戏往往能给我们带来巨大的收益(当然这个收益不是经济上的:-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 == kGameAutorotationUIViewController
return ( 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 swallowsTouches: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.25

static 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)ccTouchesEnded:(NSSet *)touches 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-蜘蛛人源码

回见。




 

 

 

 

转载于:https://www.cnblogs.com/pengyingh/articles/2393191.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/548583.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何从iPhone / iPod Touch / iPad连接

如何从iPhone / iPod Touch / iPad连接 本文将介绍从iPhone / iPod Touch / iPad等设备连接到已启用L2TP over IPsec功能的PacketiX 服务器的方法。 操作前需要按照“在服务器上启用IPsec功能”的提示&#xff0c;在服务器端进行设置。如何增加一个连接设置 首先&#xff0c;你…

POJ 3250 Bad Hair Day【单调队列】

Description Some of Farmer JohnsNcows (1 ≤N≤ 80,000) are having a bad hair day! Since each cow is self-conscious about her messy hairstyle, FJ wants to count the number of other cows that can see the top of other cows heads. Each cowihas a specified heig…

2012年3月编程语言排行榜:JavaScript超越Perl和Python

2012年3月编程语言排行榜&#xff1a;JavaScript超越Perl和Python TIOBE 于昨日公布了2012年3月编程语言指数排行榜。排名前三的语言是&#xff1a;Java、C 和 C#。 在本月排行榜中&#xff0c;JavaScript上升至第8位。由于网站越来越依赖JavaScript&#xff0c;因此JS的流行程…

ASP.NET MVC 多语言开发简单案例

首先&#xff0c;新建一个空的MVC项目&#xff0c;增加一个名叫Language的文件夹用于存放资源文件&#xff0c;如下图所示&#xff1a; 在Language文件夹中&#xff0c;增加两个资源文件&#xff0c;分别用于存储中文和英文&#xff0c;如下图所示&#xff1a; 如何添加资源文件…

C#中使用NPIO实现导入导出Excel简单操作

本文介绍了在Winform中使用NPOI(PIO项目下的.Net组件)来操作Excel文件&#xff0c;而无需安装Office。 要实现的效果是这样的&#xff1a; 1、导出Excel&#xff1a;根据指定datatable&#xff0c;弹出导出窗口&#xff0c;用户自定义路径、导出名&#xff0c;然后导出。 2、…

3招seo技巧让你把关键词做进百度前三

这个技巧也没有什么&#xff0c;说白了就是个百度点击原理&#xff01;当然&#xff0c;这个标题也就是一个标题党&#xff0c;但是的确能让你的排名靠前&#xff0c;因为我之前测试过&#xff0c;我做过一个词&#xff0c;有10个人&#xff0c;点击一下&#xff0c;隔天就第一…

ASP.NET MVC 中删除无用视图引擎

默认情况下&#xff0c;ASP.NET MVC 同时支持 WebForm 和 Razor 引擎&#xff0c;而我们通常在同一个项目中只用到了一种视图引擎&#xff0c;如Razor&#xff0c;那么我们就移除没有使用的视图引擎&#xff0c;提高View视图的检索效率。在没有删除WebForm引擎之前&#xff0c;…

VRRP在生产环境中的应用

该案例是笔者做过的一个网络改造项目&#xff0c;同时为了保密只介绍VRRP方面的配置以及要注意的事项。 该方案时某医院核心网络&#xff0c;通过两台S8610走VRRP(和网关相连)。网络拓扑如下 S8610_1配置 en config hostname S8610_1 enable sec ******* line vty 0 4 pass ***…

隐藏ASP.NET MVC版本

默认情况下&#xff0c;ASP.NET MVC网站会把版本号提供给浏览器&#xff0c;如下图所示&#xff1a; 只需在Global.asax中添加一句代码即可&#xff1a; MvcHandler.DisableMvcResponseHeader true; 如下图所示&#xff1a; protected void Application_Start(){MvcHandler.…

ASP.NET MVC 使用Log4Net记录系统运行中问题

log4net是.Net下一个非常优秀的开源日志记录组件。log4net记录日志的功能非常强大。它可以将日志分不同的等级&#xff0c;以不同的格式&#xff0c;输出到不同的媒介。 在NuGet程序包中下载log4Net组件&#xff0c;如下图所示&#xff1a; 打开Web.config文件&#xff0c;在&…

ASP.NET MVC 使用Log4Net在不同目录中记录不同类型的日志

在上一篇文章中&#xff0c;实现了利用Log4Net记录系统中出现的问题。如果同时还需要记录用户操作的监控日志&#xff0c;就需要一些修改 修改Web.Cofig配置文件 ,增加一个logger节点和appender节点&#xff0c;用于监控操作日志配置&#xff0c;并且设置不同的日志文件路径&am…

简明python教程 --C++程序员的视角(七):异常

当你的程序中出现某些 异常的 状况的时候&#xff0c;异常就发生了。例如&#xff0c;当你想要读某个文件的时候&#xff0c;而那个文件不存在。或者在程序运行的时候&#xff0c;你不小心把它删除了。上述这些情况可以使用异常来处理。 假如你的程序中有一些无效的语句&#x…

【流媒體】Android 实时视频采集—Camera预览采集

【流媒體】Android 实时视频采集—Cameara预览采集 SkySeraph Mar 26th 2012 SZ Email&#xff1a;skyseraph00163.com 更多精彩请直接访问SkySeraph个人站点&#xff1a;www.skyseraph.com 流媒体系列&#xff1a; http://skyseraph.com/2012/04/11/Media/流媒体专题/ 1 概…

C#中使用SqlBulk百万级数据秒级插入

本文转自这篇文章&#xff0c;提供了一种较快的数据插入的思路&#xff0c;转过来做个记录。 #region static void Insert() {Console.WriteLine("使用Bulk插入的实现方式");Stopwatch sw new Stopwatch();DataTable dt GetTableSchema(); using (SqlConnection …

利用JQuery插件CleverTabs实现多页签打开效果

在VS中&#xff0c;我们能打开多页签&#xff0c;并在不同的页签之间进行浏览和操作&#xff0c;这一功能通过JQuery插件CleverTabs也能实现此效果。CleverTabs下载请点击这里&#xff1a;JQuery CleverTabs 本文采用ASP.NET MVC技术实现效果&#xff1a;要在布局页中点击不同的…

精通ASP.NET MVC ——路由

本文章将关注定义路由&#xff0c;并使用它们去处理URL&#xff0c;使用户能够到达控制器和动作。 文章非常长&#xff0c;可以对路由机制有较初步的了解。首先创建示例项目&#xff0c;项目名为UrlAndRoutes&#xff0c;如下图所示&#xff1a; 然后是创建示例控制器和示例视图…

ASP.NET Web API简单学习

Web API 简介 Web API可以返回 json、xml类型的数据&#xff0c;对于数据的增删改查&#xff0c;提供了对应的资源操作&#xff0c;按照要求的类型进行处理&#xff0c;主要包括&#xff1a;Get&#xff08;查&#xff09;&#xff0c;Post&#xff08;增&#xff09;&#xff…

利用NCO 3.0 调用SAP中的函数

简介 .Net Connector 3.0&#xff0c;简称 Nco3.0。Nco3.0 是 SAP 针对 .net 平台提供的编程接口&#xff0c;由 Nco1.0/Nco2.0 演变而来。如果使用 .net 平台编程&#xff0c;推荐使用 NCo3.0。3.0 版与之前1.0/2.0 版本比较&#xff0c;不管是 API 还是架构&#xff0c;都重新…

char与varchar区别(MYISAM)

Char属于固定长度的字符类型&#xff0c;而varchar属于可变长的字符类型。 下表将各种字符串值保存到char(4)和varchar&#xff08;4&#xff09;列后的结果&#xff0c;说明了char和varchar之间的差别 值 Char(4) 存储需求 Varchar (4) 存储需求 ‘’ ‘ ’ 4字符节…

Win10系统局域网中的其他电脑能Ping通但是无法访问本地IIS发布的网站

局域网中的其他电脑无法访问本地IIS发布的网站&#xff0c;但是能ping通&#xff0c;基本是由于没开放端口的原因。 打开防火墙 —— 高级设置&#xff1a; 点击“入站规则”&#xff0c;然后是“新建规则”&#xff1a; 然后选择“端口” &#xff1a; 选择“TCP”&#xff0c…