13.14.7添加敌机

游戏当中怎么能少了千军万马的敌人呢?现在,我们来添加一些敌机,大量的敌机将从屏幕上方随机出现,并以随机的速度向下俯冲。这些敌机暂时不会发射子弹,之后读者可以自己添加该功能。具体步骤如下。

自定义一个FKSprite类,继承自CCSprite,因为创建的敌机玩家会发射子弹去消灭,为了增加游戏的趣味性和难度,需要加入敌机的生命值、血条和爆炸效果等特效。在FKSprite类中定义了相关变量用于存储数据。实现代码如下。

程序清单:codes/13/13.14/AirfightGame/AirfightGame/FKSprite.h

@interface FKSprite : CCSprite {

}

// 精灵的生命值

@property int lifeValue;

// 精灵的名称

@property NSString* name;

// 敌机的血条

@property CCProgressTimer* enemyPlaneHP;

// 血条的更新量

@property float HPInterval;

@end

打开HelloWorldLayer.m文件,添加两个变量,实现代码如下。

// 敌机数组

CCArray* enemyPlaneArray;

// 游戏帧计数器

NSInteger count;

在私有分类中添加两个和敌机相关的方法,实现代码如下。

// 更新敌机

-(void) updateEnemySprite:(ccTime)delta;

// 敌机离开屏幕删除

-(void) removeEnemySprite:(ccTime)delta;

updateEnemySprite:用于控制敌机的创建、俯冲。实现代码如下。

程序清单:codes/13/13.14/AirfightGame/AirfightGame/HelloWorldLayer.m

-(void) updateEnemySprite:(ccTime)delta{

// 控制count为100的倍数时添加一架敌机

if (count % 30 == 0)

{

// FKSprite精灵对象继承自CCSprite,增加了生命值

FKSprite* enemyPlaneSprite;

// 根据rand随机数添加不同的敌机

int rand = arc4random() % 2;

// 使用随机数来设置敌机的X坐标

int randX = arc4random() % (screenWidth - 40) + 20;

switch(rand)

{

case 0:

// 创建代表敌机

enemyPlaneSprite = [FKSprite spriteWithSpriteFrameName:@"e0.png"];

enemyPlaneSprite.position = ccp(randX,480+enemyPlaneSprite.contentSize.height);

enemyPlaneSprite.name = @"e0";

enemyPlaneSprite.lifeValue = 1;

break;

case 1:

// 创建代表敌机

enemyPlaneSprite = [FKSprite spriteWithSpriteFrameName:@"e2.png"];

enemyPlaneSprite.position = ccp(randX,480+enemyPlaneSprite.contentSize.height);

enemyPlaneSprite.name = @"e2";

enemyPlaneSprite.lifeValue = 1;

break;

}

// 获取随机时间(敌机俯冲的时间)

float durationTime = arc4random() %2 + 2;

// 敌机俯冲

id moveBy = [CCMoveBy actionWithDuration:durationTime

position:ccp(0,-enemyPlaneSprite.position.y-enemyPlaneSprite.contentSize.height)];

[enemyPlaneSprite runAction:moveBy];

// 将敌机精灵添加到敌机数组中

[enemyPlaneArray addObject:enemyPlaneSprite];

// 获得精灵表单

CCSpriteBatchNode *batchNode = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];

[batchNode addChild:enemyPlaneSprite z:4];

}else{

if (count % 200 == 0)

{

int randX = arc4random() % (screenWidth - 40) + 20;

// FKSprite精灵对象继承自CCSprite,增加了生命值

FKSprite* enemyPlaneSprite;

// 创建代表敌机

enemyPlaneSprite = [FKSprite spriteWithSpriteFrameName:@"e1.png"];

enemyPlaneSprite.position = ccp(randX,480+enemyPlaneSprite.contentSize.height);

enemyPlaneSprite.name = @"e1";

enemyPlaneSprite.lifeValue = 10;

// 获取随机时间(敌机掉落的时间)

float durationTime = arc4random() %2 + 2;

// 敌机俯冲

id moveBy = [CCMoveBy actionWithDuration:durationTime

position:ccp(0,-enemyPlaneSprite.position.y-enemyPlaneSprite.contentSize.height)];

[enemyPlaneSprite runAction:moveBy];

// 将敌机精灵添加到敌机数组中

[enemyPlaneArray addObject:enemyPlaneSprite];

// 获得精灵表单

CCSpriteBatchNode *batchNode = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];

[batchNode addChild:enemyPlaneSprite z:4];

// 创建一个进度条精灵

CCSprite* barSprite = [CCSprite spriteWithFile:@"planeHP.png"];

// 初始化一个CCProgressTimer对象

CCProgressTimer* enemyPlaneHP = [CCProgressTimer progressWithSprite:barSprite];

// setPercentage:0.0f,表示并未加载任何资源,表现在画面上就是什么也看不见

[enemyPlaneHP setPercentage:0.0f];

// 由于图片大小关系,把scale设置成0.15,即缩小一半

enemyPlaneHP.scale = 0.15;

enemyPlaneHP.midpoint = ccp(0,0.5);

enemyPlaneHP.barChangeRate = ccp(1, 0);

enemyPlaneHP.type = kCCProgressTimerTypeBar;

enemyPlaneHP.percentage = 100;

CGPoint pos = enemyPlaneSprite.position;

enemyPlaneHP.position = ccp(pos.x, pos.y+32);

[self addChild:enemyPlaneHP];

id moveBy2 = [CCMoveBy actionWithDuration:durationTime

position:ccp(0,-enemyPlaneSprite.position.y-enemyPlaneSprite.contentSize.height)];

[enemyPlaneHP runAction:moveBy2];

enemyPlaneSprite.enemyPlaneHP = enemyPlaneHP;

enemyPlaneSprite.HPInterval = 100.0 / (float)enemyPlaneSprite.lifeValue;

}

}

}

这段代码有点长,接下来为大家详细解释。

首先,游戏中出现的小敌机有3种,通过count变量来控制敌机出现的频率(count变量在update方法中自增)。

当count%30==0时,随机创建两种小飞机,对应e0.png和e2.png图片,设置生命值为1。接下来获取一个随机俯冲时间,根据该时间创建一个moveBy动作,让飞机执行moveBy动作俯冲。同时将敌机精灵添加到敌机数组和精灵表单当中。

当count%200==0时,创建一种小飞碟,对应e1.png图片,设置生命值为10。接下来获取一个随机俯冲时间,根据该时间创建一个moveBy动作,让飞机执行moveBy动作俯冲。同时将敌机精灵添加到敌机数组和精灵表单当中。之后使用前面用过的CCProgressTimer类创建了一个血条,游戏中血条会随着敌机被玩家飞机的子弹打中而减少,从而实现非常炫的射击效果。

removeEnemySprite的作用是当敌机已经移出屏幕外时删除敌机精灵。实现代码如下。

程序清单:codes/13/13.14/AirfightGame/ AirfightGame/HelloWorldLayer.m

-(void) removeEnemySprite:(ccTime)delta{

// 获得精灵表单

CCSpriteBatchNode *batchNode = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];

// 定义循环变量

CCSprite* enemyPlaneSprite;

// 遍历所有的敌机精灵

CCARRAY_FOREACH(enemyPlaneArray, enemyPlaneSprite){

// 如果敌机已经移出屏幕外,则删除敌机精灵

if (enemyPlaneSprite.position.y <= -enemyPlaneSprite.contentSize.height)

{

// 从精灵表单中删除该敌机精灵

[batchNode removeChild:enemyPlaneSprite cleanup:YES];

// 从敌机数组中删除该敌机精灵

[enemyPlaneArray removeObject:enemyPlaneSprite];

}

}

}

removeEnemySprite:比较简单,首先调用getChildByTag:方法获取精灵表单,然后遍历敌机数组,判断敌机的y轴若超出屏幕范围,则从精灵表单和敌机数组中删除敌机精灵。

找到onEnter方法,在⑤部分代码后初始化敌机数组,实现代码如下(程序清单同上)。

enemyPlaneArray = [[CCArray alloc] init];

修改update方法,实现代码如下(程序清单同上)。

-(void) update:(ccTime)delta{

count++;

[self updateBackground:delta];

[self updateEnemySprite:delta];

[self removeEnemySprite:delta];

}

再次编译并运行游戏,大量的敌机会随机出现,并向屏幕下方俯冲。模拟器显示效果如图13.63所示。

13.14.8玩家飞机添加子弹并射击

现在,大量的敌机向玩家飞机俯冲过来,不用多说,给玩家飞机添加子弹射击功能打爆它们。这里我们设计成子弹自动发射,并且子弹是无限的,这样玩家就只需要专心控制飞机的飞行就可以了。具体步骤如下。

打开HelloWorldLayer.m文件,添加变量,实现代码如下(程序清单同上)。

// 代表子弹精灵数组

CCArray* bulletArray;

在私有分类中添加3个和敌机相关的方法,实现代码如下(程序清单同上)。

// 更新子弹

-(void) updateShooting:(ccTime)delta;

// 子弹离开屏幕,删除

-(void) removeBulletSprite:(ccTime)delta;

// 碰撞检测

-(void) collisionDetection:(ccTime)delta;

updateShooting:方法用于不断发射子弹。实现代码如下(程序清单同上)。

-(void) updateShooting:(ccTime)delta{

// 获得精灵表单

CCSpriteBatchNode *batchNode = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];

// 飞机精灵坐标

CGPoint pos = planeSprite.position;

// 控制count为8的倍数时发射一颗子弹

if(count % 8 == 0)

{

// 创建代表子弹的精灵

CCSprite* bulletSprite = [CCSprite spriteWithSpriteFrameName:@"bullet.png"];

// 设置子弹坐标

CGPoint bulletPos = ccp(pos.x, pos.y +

planeSprite.contentSize.height/2 + bulletSprite.contentSize.height);

bulletSprite.position = bulletPos;

// 子弹移动时间为0.4秒,移动距离为屏幕高度-子弹的y轴

id moveBy = [CCMoveBy actionWithDuration:0.4f position:ccp(0, screenHeight-bulletPos.y)];

[bulletSprite runAction:moveBy];

// 将子弹精灵添加到精灵表单中

[batchNode addChild:bulletSprite z:4];

// 将子弹精灵添加到子弹精灵数组中

[bulletArray addObject:bulletSprite];

}

}

updateShooting:方法并不复杂,首先获取精灵表单,然后获取玩家飞机的坐标位置,当count%8==0时创建一颗子弹精灵,并执行moveBy动作达到发射子弹的效果,最后将子弹精灵添加到精灵表单和子弹精灵数组当中。

removeBulletSprite:方法的作用是当子弹精灵已经移出屏幕外时删除子弹精灵。实现代码如下(程序清单同上)。

-(void) removeBulletSprite:(ccTime)delta{

// 获得精灵表单

CCSpriteBatchNode *batchNode = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];

CCSprite* bulletSprite;

// 遍历所有的子弹

CCARRAY_FOREACH(bulletArray, bulletSprite){

// 如果子弹已经移出屏幕外,则删除子弹

if (bulletSprite.position.y >=screenHeight){

// 从精灵表单中删除该子弹精灵

[batchNode removeChild:bulletSprite cleanup:YES];

// 从子弹数组中删除该子弹精灵

[bulletArray removeObject:bulletSprite];

}

}

}

removeBulletSprite:方法也比较简单,首先调用getChildByTag:方法获取精灵表单,然后遍历子弹数组,判断子弹的y轴若超出屏幕范围,则从子弹表单和子弹数组中删除子弹精灵。

collisionDetection:是检查玩家飞机和敌机碰撞或者子弹和敌机碰撞的方法。实现代码如下(程序清单同上)。

-(void) collisionDetection:(ccTime)dt{

// 获得精灵表单

CCSpriteBatchNode *batchNode = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];

// 定义循环变量

FKSprite* enemyPlaneSprite;

CCSprite* bulletSprite;

// 遍历敌机数组

CCARRAY_FOREACH(enemyPlaneArray, enemyPlaneSprite){

// 玩家飞机和敌机发生碰撞

if(CGRectIntersectsRect(planeSprite.boundingBox,enemyPlaneSprite.boundingBox)){

// 播放爆炸动画

[self bombAnimate:@"blast" :enemyPlaneSprite.position ];

// 删除敌机精灵

[enemyPlaneArray removeObject:enemyPlaneSprite];

[batchNode removeChild:enemyPlaneSprite cleanup:YES];

[planeSprite stopAllActions];

// 删除玩家精灵

[batchNode removeChild:planeSprite cleanup:YES];

[self gameOver:@"游戏结束!"];

}

// 遍历子弹数组

CCARRAY_FOREACH(bulletArray, bulletSprite){

// 如果敌机与子弹发生碰撞

if(CGRectIntersectsRect(enemyPlaneSprite.boundingBox, bulletSprite.boundingBox)){

// 播放子弹音效

[[SimpleAudioEngine sharedEngine] playEffect:@"bullet.mp3"];

// 删除子弹精灵

[bulletArray removeObject:bulletSprite];

[batchNode removeChild:bulletSprite cleanup:YES];

// 敌机生命值减1

enemyPlaneSprite.lifeValue--;

// 血条减少

if (enemyPlaneSprite.enemyPlaneHP != nil) {

enemyPlaneSprite.enemyPlaneHP.percentage

= enemyPlaneSprite.HPInterval * enemyPlaneSprite.lifeValue;

}

// 判断敌机的生命值

if (enemyPlaneSprite.lifeValue <= 0) {

// 删除敌机精灵

[enemyPlaneArray removeObject:enemyPlaneSprite];

[batchNode removeChild:enemyPlaneSprite cleanup:YES];

// 播放爆炸动画

[self bombAnimate:@"blast" :enemyPlaneSprite.position ];

// 播放爆炸音效

[[SimpleAudioEngine sharedEngine] playEffect:@"b0.mp3"];

}

break;

}

}

}

}

collisionDetection:方法首先调用getChildByTag:方法获取精灵表单,然后循环遍历敌机数组。CGRectIntersectsRect(rect 1.feet 2)函数可以判断矩形结构是否交叉,两个矩形对象是否重叠,常用来检测两个图标是否发生碰撞。CCNode有一个属性boundingBox,会返回精灵的边界框。使用这个属性比自己计算要好,因为这样做更简单,同时也考虑了精灵的变形。通过CGRectIntersectsRect(rect 1.feet 2)函数判断玩家飞机和敌机是否发生碰撞,如果发生碰撞,则播放一段爆炸动画,然后从敌机数组和精灵表单中删除敌机精灵,再从精灵表单中删除玩家飞机精灵,最后调用gameOver:方法结束游戏。

如果玩家飞机和敌机没有发生碰撞,则不循环遍历子弹数组,判断子弹和敌机是否发生碰撞,如果发生碰撞,则播放子弹音效,删除子弹精灵,将敌机生命值减去1;如果敌机有血条,则更新血条(小飞碟生命值为10,有血条)。接下来判断敌机的生命值,如果敌机生命值为0,则从敌机数组和精灵表单中删除敌机精灵,播放爆炸动画和爆炸音效。

在私有分类中添加两个方法,实现代码如下(程序清单同上)。

// 播放爆炸动画

-(void) bombAnimate:(NSString*) name : (CGPoint) position;

// 游戏结束

-(void) gameOver:(NSString*) labelString;

bombAnimate: name:方法用于播放爆炸动画。实现代码如下(程序清单同上)。

-(void) bombAnimate:(NSString*) name : (CGPoint) position {

NSString* bombName = [NSString stringWithFormat:@"%@0.png",name];

float delay = 0.08f;

CCSpriteBatchNode *batchNode = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];

CCSprite* blastSprite = [CCSprite spriteWithSpriteFrameName:bombName];

blastSprite.position = position;

// 获得动画帧

CCAnimation* blastAnimation = [self getAnimationByName:name delay:delay animNum:4];

// 组合动作:1.播放动画 2.删除动画精灵对象

id action = [CCSequence actions: [CCAnimate actionWithAnimation:blastAnimation],

[CCCallBlock actionWithBlock:^() {

[batchNode removeChild:blastSprite cleanup:YES];

}],nil];

[blastSprite runAction:action];

[batchNode addChild:blastSprite z:4];

}

bombAnimate: name:方法接收传进来的爆炸效果名称,获取爆炸动画帧,先播放动画,动画播放完毕后删除动画。

gameOver:方法用于游戏结束时清除精灵对象并提示游戏信息。实现代码如下(程序清单同上)。

-(void) gameOver:(NSString*) labelString{

// 停止所有动作

[self unscheduleUpdate];

// 游戏结束

CCMenuItemFont* gameItem = [CCMenuItemFont itemWithString:labelString

target:self selector:@selector(onRestartGame:)];

gameItem.position=ccp(screenWidth/2, screenHeight/2);

CCMenu* menu = [CCMenu menuWithItems:gameItem, nil];

menu.position = CGPointZero;

[self addChild:menu];

}

找到onEnter方法,在⑤部分代码后初始化子弹数组,实现代码如下。

bulletArray = [[CCArray alloc] init];

修改update方法,实现代码如下(程序清单同上)。

-(void) update:(ccTime)delta{

count++;

[self updateBackground:delta];

[self updateEnemySprite:delta];

[self removeEnemySprite:delta];

[self updateShooting:delta];

[self removeBulletSprite:delta];

self collisionDetection:delta];

}

再次编译并运行游戏,大量的敌机会随机出现,并向屏幕下方俯冲,控制玩家飞机发射子弹,子弹击中敌机显示爆炸效果。模拟器显示效果如图13.64所示。

13.14.9添加背景音乐

有了音效,有了爆炸效果,没有背景音乐好像缺少点热血澎湃的游戏感觉,让我们马上加入背景音乐吧。

找到onEnter方法,在游戏的主循环代码之前添加背景音乐播放功能,实现代码如下(程序清单同上)。

[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"s3.wav" loop:YES];

[[SimpleAudioEngine sharedEngine] setBackgroundMusicVolume:0.5];

再次编译并运行游戏,开始游戏时就可以听见令人热血澎湃的背景音乐了。读者可以按照13.13.2节的内容为游戏增加声音设置功能选项,包括声音开关、声音大小调节等,这里不再赘述。

本文转自 fkJava李刚

51CTO博客,原文链接:http://blog.51cto.com/javaligang/1400891 ,如需转载请自行联系原作者

疯狂ios之疯狂打飞机游戏(3)相关推荐

  1. 疯狂ios讲义疯狂连载之游戏的状态数据模型

    对游戏玩家而言在游戏界面上看到的"元素"千差万别.变化多端但对游戏开发者而言游戏界面上的元素在底层都是一些数据不同的数据所绘制的图片有所不同.因此建立游戏的状态数据模型是实现游戏逻 ...

  2. 疯狂ios讲义疯狂连载之实现游戏视图控制器

    前面已经给出了游戏界面的布局文件,该布局文件需要使用一个视图控制器来负责显示.除此之外,视图控制器还需要为游戏界面的按钮事件提供事件监听器. 下面是该程序的视图控制器类的代码. 程序清单:codes/ ...

  3. 疯狂ios讲义疯狂连载之实现游戏逻辑(2)

    13.6.5 定义获取通道的工具方法 这里所谓的通道指的是一个方块上.下.左.右4个方向上的空白方块图13.10显示了一个方块四周的通道. 图13.10 方块四周的通道 下面是获取某个坐标点四周通道的 ...

  4. 疯狂ios讲义疯狂连载之显示动画

    UIImage专门用于显示各种位图该类支持表12.1所示的各种图片格式. 当系统内存紧张时UIImage会将图片数据从UIImage对象中清理出去来以节省系统内存这里的清理行为只是清理UIImage内 ...

  5. 疯狂ios讲义疯狂连载之加载界面的图片

    正如前面FKBaseBoard类的代码中所看到的,当程序需要创建N个FKPiece对象时,程序会直接调用getPlayImages()函数去获取图片,该函数将会随机从项目包下取得N张图片. 为了让ge ...

  6. 疯狂ios讲义疯狂连载之日期选择器(UIDatePicker)

    UIDatePicker是一个可以用来选择日期和时间的控件.除此之外,它也可作为倒计时控件. 日期选择器(UIDatePicker)继承了UIControl,因此UIDatePicker可以作为活动控 ...

  7. 疯狂ios讲义之疯狂连连看游戏简介

    <连连看>是一款广受玩家欢迎的小游戏,它具有玩法简单.耗时少等特征,尤其适合广大白领女性在办公室里休闲娱乐.图13.1显示了该游戏的游戏界面. 从图13.1可以看出,在<连连看> ...

  8. 疯狂iOS 讲义(上) Objective-C 2.0与iPhone/iPad应用开发基础(国内首部iOS 7相关著作,小伙伴们,学好iPhone/iPad应用开发,我们一起做土豪吧)

    疯狂iOS 讲义(上) Objective-C 2.0与iPhone/iPad应用开发基础(国内首部iOS 7相关著作,小伙伴们,学好iPhone/iPad应用开发,我们一起做土豪吧) 李刚 编 IS ...

  9. 疯狂iOS 讲义(上) Objective-C 2.0与iPhone/iPad应用开发基础

    疯狂iOS 讲义(上)Objective-C 2.0与iPhone/iPad应用开发基础(国内首部iOS 7相关著作,小伙伴们,学好iPhone/iPad应用开发,我们一起做土豪吧) 李刚编 ISBN ...

最新文章

  1. 算法与数据结构(选择排序)
  2. linux top 历史,阿里工程师眼中的linux top命令
  3. android视频教程
  4. AUTOSAR从入门到精通100讲(八十五)-AUTOSAR基础篇之BswM
  5. 如何在 Linux 终端中一次运行多个 Linux 命令(初学者教程)
  6. R语言数据框行转列实例
  7. Cannot delete or update a parent row: a foreign key constraint fails
  8. LCD12864显示屏原理及使用教程
  9. 带你啃透深度学习必学“圣经”花书!(附带论文代码精读讲解)
  10. ArcGis-学习笔记1-地图矢量化
  11. java security md5_java自带的加密--java.security.MessageDigest(md5加密)
  12. 楚狂人--驱动开发基础
  13. 【实例】VBA excel 隔行插入行与列
  14. mysql,php,redis,jq小记
  15. 漏洞平台之pikachu详细图文搭建教程
  16. 提高团队合作的六个方法
  17. mybatis中resultMap和resultType的详细用法
  18. 【稳定性day4】美团外卖高可用的演进之路 - 日活两千万的挑战
  19. iOS开发笔记--基于面向协议MVP模式下的软件设计
  20. 阿里云国际站购买服务器遇到实名失败是什么原因?

热门文章

  1. 学习世界模型,通向AI的下一步:Yann LeCun在IJCAI 2018上的演讲
  2. 关于现在人工智能预测的一些冷水
  3. AI博弈论:DeepMind让智能体在非对称博弈中找纳什均衡
  4. 深度学习不是AI的未来
  5. IT人的眼睛就是一把尺!
  6. 太慢不能忍!CPU又拿硬盘和网卡开刀了!
  7. 如果重新一次高考,你还会选择软件专业当程序员吗? | 每日趣闻
  8. [luogu 3773][CTSC 2017]吉夫特
  9. 【mysql学习笔记整理】
  10. Java设计模式之策略模式与状态模式