有大师曾经说过,游戏永远没有真正意义上的完结,因为总是可以不断地完善它,给它赋予更多的玩法和元素,使之更充实和完美。本章我们的目的是学习使用Cocos2D制作一个简单的游戏,而制作游戏追求完美的心境是永恒不变的。接下来将进一步完善此游戏。2.6.1 添加计分和玩家生命值为了增加游戏的趣味性,游戏还需要加上计分规则以及玩家生命值的设定。简单起见,我们设置每击中一个敌人计100分,玩家共有3条生命,如果玩家分数超过1000分,即击中10个敌人就可以胜利过关;反之,如果失去3条生命,则Game Over!打开HelloWorldLayer.h文件,在头文件中添加两个实例变量,如代码清单2-28所示。代码清单2-28 在头文件中添加两个实例变量
CCLabelTTF *_lifeLabel; CCLabelTTF *_scoreLabel;在init方法中return self;语句前、if条件语句的最后添加代码清单2-29所示代码。代码清单2-29 在方法中return self;语句前、if条件语句的最后添加代码
//12.init player lives & score CCLabelTTF *lifeIndicator = [CCLabelTTF labelWithString:@"生命值:" fontName:@"Arial" fontSize:20]; lifeIndicator.anchorPoint = ccp(0.0,0.5); lifeIndicator.position = ccp(20,winSize.height - 20); [selfaddChild:lifeIndicator z:10]; _lifeLabel = [CCLabelTTF labelWithString:@"3" fontName:@"Arial" fontSize:20]; _lifeLabel.position = ccpAdd(lifeIndicator.position, ccp(lifeIndicator.contentSize.width+10,0)); [self addChild:_lifeLabel z:10]; CCLabelTTF *scoreIndicator = [CCLabelTTF labelWithString:@"分数:" fontName:@"Arial" fontSize:20]; scoreIndicator.anchorPoint = ccp(0.0,0.5f); scoreIndicator.position = ccp(winSize.width - 100,winSize.height - 20); [selfaddChild:scoreIndicator z:10]; _scoreLabel = [CCLabelTTF labelWithString:@"00" fontName:@"Arial" fontSize:20]; _scoreLabel.position = ccpAdd(scoreIndicator.position, ccp(scoreIndicator.contentSize.width+ 10,0)); [self addChild:_scoreLabel z:10];这段代码的主要作用是在游戏界面中添加计分和玩家生命值的显示。注意 坐标计算技巧:这里通过修改label的anchorPoint定位label,同时使用ccpAdd宏计算两个点的和。编译并运行代码,屏幕输出如图2-9所示。
按照先前设定的规则,击中一个敌机加100分,被敌机撞到,生命值减去1。 步骤1 打开HelloWorldLayer.h,向其中添加两个实例变量,如代码清单2-30所示。 代码清单2-30 添加两个实例变量 int _totalLives; int _totalScore;步骤2 在init方法中return self;语句前、if条件语句的最后添加代码进行初始化,如代码清单2-31所示。代码清单2-31 在方法中return self;语句前、if条件语句的最后添加代码
//13.init lives & score variable _totalLives = 3; _totalScore = 0;步骤3 定义一个updateHUD的方法,根据_totalLives和_totalScore这两个变量实时更新相应的label方法,该方法的实现如代码清单2-32所示。代码清单2-32 实时更新相应的label方法
-(void) updateHUD:(ccTime)dt{ [_lifeLabelsetString:[NSStringstringWithFormat:@"-",_totalLives]]; [_scoreLabelsetString:[NSStringstringWithFormat:@"d",_totalScore]]; }注意 游戏开发初学者对HUD可能有点陌生,这里有必要做一下解释。HUD是Head-Up Display的简写,是游戏开发中一个非常重要的概念。Mini地图、分数、血条、时间进度条和魔兽世界里的技能条等,这些都是HUD,主要用来给玩家提供一些辅助信息,让玩家可更加关心游戏玩法,而不用去管底层的数据。步骤4 更新游戏主循环,添加updateHUD方法调用。如代码清单2-33所示。代码清单2-33 游戏主循环
-(void) update:(ccTime)dt{ [selfupdatePlayerPosition:dt]; [selfupdatePlayerShooting:dt]; [selfcollisionDetection:dt]; [selfupdateHUD:dt]; }步骤5 在碰撞检测的逻辑里添加相应的计分和损失生命值的逻辑计算,如代码清单2-34所示。
代码清单2-34 添加计分和损失生命值的逻辑计算
-(void) collisionDetection:(ccTime)dt{ CCSprite *enemy; CGRectbulletRect = [self rectOfSprite:_bulletSprite]; CCARRAY_FOREACH(_enemySprites, enemy) { if (enemy.visible) { //1.bullet & enemy collision detection CGRectenemyRect = [self rectOfSprite:enemy]; if (_bulletSprite.visible&&CGRectIntersectsRect(enemyRect, bulletRect)) { enemy.visible = NO; _bulletSprite.visible = NO; _totalScore += 100; [_bulletSpritestopAllActions]; [enemystopAllActions]; CCLOG(@"collision bullet"); break; } //2.enemy & player collision detection CCSprite *playerSprite = (CCSprite*)[self getChildByTag:kTagPalyer]; CGRectplayRect = [self rectOfSprite:playerSprite]; if (playerSprite.visible&& playerSprite.numberOfRunningActions == 0 &&CGRectIntersectsRect(enemyRect, playRect)) { enemy.visible = NO; _totalLives -= 1; id blink = [CCBlink actionWithDuration:2.0 blinks:4]; [playerSpritestopAllActions]; [playerSpriterunAction:blink]; CCLOG(@"collision player"); break; } } } }2.6.2 添加游戏胜利和结束画面玩家达到1000分或者生命值变为0时,需要呈现游戏胜利或者失败的画面。为了简单,我们设置游戏结束时(不管是胜利还是失败)只在中间显示一个label,2秒后重新开始游戏。步骤1 打开HelloWorldLayer.h,添加一个新的实例变量,如代码清单2-35所示。代码清单2-35 添加一个新的实例变量
CCLabelTTF *_gameEndLabel;步骤2 添加一个私有方法,如代码清单2-36所示。代码清单2-36 添加一个私有方法
-(void) onRestartGame{ [[CCDirector sharedDirector] replaceScene:[HelloWorldLayer scene]]; }步骤3 修改碰撞处理的代码,如代码清单2-37所示。代码清单2-37 修改碰撞处理
-(void) collisionDetection:(ccTime)dt{ CCSprite *enemy; CGRectbulletRect = [self rectOfSprite:_bulletSprite]; CCARRAY_FOREACH(_enemySprites, enemy) { if (enemy.visible) { //1.bullet & enemy collision detection CGRectenemyRect = [self rectOfSprite:enemy]; if (_bulletSprite.visible&&CGRectIntersectsRect(enemyRect, bulletRect)) { enemy.visible = NO; _bulletSprite.visible = NO; _totalScore += 100; if (_totalScore>= 1000) { [_gameEndLabelsetString:@"游戏胜利!"]; _gameEndLabel.visible = YES; idscaleTo = [CCScaleTo actionWithDuration:1.0 scale:1.2f]; [_gameEndLabelrunAction:scaleTo]; [selfunscheduleUpdate]; [self performSelector:@selector(onRestartGame) withObject:nil afterDelay:2.0f]; } [_bulletSpritestopAllActions]; [enemystopAllActions]; CCLOG(@"collision bullet"); break; } //2.enemy & player collision detection CCSprite *playerSprite = (CCSprite*)[self getChildByTag:kTagPalyer]; CGRectplayRect = [self rectOfSprite:playerSprite]; if (playerSprite.visible&& playerSprite.numberOfRunningActions == 0 &&CGRectIntersectsRect(enemyRect, playRect)) { enemy.visible = NO; _totalLives -= 1; if (_totalLives<= 0) { [_gameEndLabelsetString:@"游戏失败!"]; _gameEndLabel.visible = YES; idscaleTo = [CCScaleTo actionWithDuration:1.0 scale:1.2f]; [_gameEndLabelrunAction:scaleTo]; [selfunscheduleUpdate]; [self performSelector:@selector(onRestartGame) withObject:nil afterDelay:3.0f]; } id blink = [CCBlink actionWithDuration:2.0 blinks:4]; [playerSpritestopAllActions]; [playerSpriterunAction:blink]; CCLOG(@"collision player"); break; } } } }代码比较简单,就是当条件满足时更改label显示的内容,然后让label运行一个action,以放大的形式呈现;最后停止游戏主循环,隔2秒调用重启游戏的方法。注意 这里需要注意[self unscheduleUpdate]方法,就是终止update方法,即终止游戏主循环。编译并运行,大家玩一玩这个游戏去吧!当然,还可以给这个游戏添加更多的乐趣,比如每过一关,敌机的数量越来越多,过关的分数要求越来越高等。本章介绍就到此为止,后面章节我们还会不断地完善此游戏,使之更加丰富多彩。大家在学习的过程中,也可以充分发挥想象力和创造力,不断地给这个游戏注入新的活力。
相关资源:cocos2d-x权威指南完整版