首页 > 代码库 > iOS_31_cocos2d_微信飞机
iOS_31_cocos2d_微信飞机
最终效果图:
纹理素材
场景
// // GameScene.m // 31_cocos2D入门 // // Created by beyond on 14-9-27. // Copyright (c) 2014年 com.beyond. All rights reserved. // 雷电,游戏场景 #import "GameScene.h" #import "Hero.h" // 背景音乐 //#import "SimpleAudioEngine.h" #import "OALSimpleAudio.h" // 子弹精灵 #import "Pellet.h" // 敌机 #import "Enemy.h" // 一次性初始化的子弹的最大数量 #define kPelletMaxCount 10 // 子弹两次出现之间的总的累计时间间隔 #define kPelletTotalIntervalTime 0.3 // 敌机的最大数量 #define kEnemyMaxCount 5 @interface GameScene() { // 因为一个spriteBatchNode对应一个纹理(图片),因此,从纹理相册裁剪出来的小精灵(帧),全部交给spriteBatchNode统一管理,场景只需要 与 spriteBatchNode打交道 CCSpriteBatchNode *_batch; // 背景2图片 始终在背景1图片的头顶上 CCSprite *_bg1, *_bg2; // 英雄,主角 Hero *_hero; // 标记 是否正在运行 BOOL _isGameRuning; // 重要~~~~子弹缓存 NSMutableArray *_pelletArr; // 成员变量 用于记住 累加的间隔时间,它当达到一定量时(如0.3秒),才发射一串子弹 // 子弹两次出现之间的总的间隔(累积)时间 CGFloat _pelletTotalIntervalTime; // 重要~~~~敌人缓存 NSMutableArray *_enemyArr; } @end @implementation GameScene #pragma mark - 覆盖父类方法 -(id)init { if (self=[super init]) { // 1.基本初始化 [self setupBasic]; // 2.初始化背景 // 初始化背景 ,并添加到batchNode中,统一管理,在时钟方法里,滚动背景 [self setupBg]; // 3.初始化英雄 // 初始化英雄 ,并添加到batchNode中,统一管理,在时钟方法里,碰撞检测 [self setupPlayer]; // 4.初始化子弹 // 初始化指定数量的子弹 ,并添加到batchNode中,统一管理,在时钟方法里,射出子弹 [self setupPelletArr]; // 5.初始化敌机 [self setupEnemyArr]; // 6.开始游戏 [self startGame]; } return self; } #pragma mark - 初始化 #pragma mark 1.基本初始化 - (void)setupBasic { // 1.加载纹理相册 到精灵帧缓存 [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"gameArts.plist"]; // 2.创建sprite批处理 _batch = [CCSpriteBatchNode batchNodeWithFile:@"gameArts.png"]; [self addChild:_batch]; // 3.添加一个按钮,暂停或继续游戏 [self setupPauseButton]; } // 4.添加一个按钮,暂停或继续游戏 - (void)setupPauseButton { // 调用父类封装的方法,绑定监听方法:gameControlBtnClicked [self addBtn:@"暂停/继续" position:ccp(0.5, 0.5) target:self sel:@selector(gameControlBtnClicked)]; } #pragma mark 2.初始化背景 - (void)setupBg { NSString *bgPicName = @"background_2.png"; _bg1 = [CCSprite spriteWithImageNamed:bgPicName]; _bg1.anchorPoint = CGPointZero; // 背景2图片 始终在背景1图片 的头顶上 _bg2 = [CCSprite spriteWithImageNamed:bgPicName]; _bg2.anchorPoint = CGPointZero; [_batch addChild:_bg1]; [_batch addChild:_bg2]; } #pragma mark 3.初始化玩家 - (void)setupPlayer { _hero = [Hero node]; // 因为是从同一个大纹理中,根据key裁剪出来的,所以可以加到同一个精灵batchNode,由它统一管理 [_batch addChild:_hero]; } #pragma mark 4.初始化子弹缓存 // 初始化指定数量的子弹 ,并添加到batchNode中,统一管理,在时钟方法里,射出子弹 - (void)setupPelletArr { _pelletArr = [NSMutableArray array]; // 初始化指定数量的子弹 ,并添加到batchNode中,统一管理,在时钟方法里,射出子弹 for (int i = 0; i<kPelletMaxCount; i++) { // 默认 就是先隐藏的 Pellet *b = [Pellet node]; // 类型为:大子弹 b.type = kPelletTypeBig; // 添加到数组中 [_pelletArr addObject:b]; // 添加到spriteBatchNode [_batch addChild:b]; } } #pragma mark 5.初始化敌机缓存 // 初始化指定数量的敌机 ,并添加到batchNode中,统一管理,在时钟方法里,安排敌机出场 - (void)setupEnemyArr { // 1.初始化 敌机缓存 _enemyArr = [[NSMutableArray alloc] init]; for (int i = 0; i<kEnemyMaxCount; i++) { // 默认 就是先隐藏的 Enemy *e = [Enemy node]; // 类型为:敌机_1 e.type = kEnemyType_1; // 添加到 敌机数组中 [_enemyArr addObject:e]; // 添加到spriteBatchNode [_batch addChild:e]; } } #pragma mark - 游戏控制按钮点击 - (void)gameControlBtnClicked { // 如果 正在游戏,就暂停;反之继续游戏 if (_isGameRuning) { // _isGameRuning = NO; [self pauseGame]; } else { // _isGameRuning = YES; [self startGame]; } } #pragma mark 开始 - (void)startGame { // 1.首先置标记为 1 _isGameRuning = YES; // 允许交互 self.userInteractionEnabled = YES; // 2.播放背景音乐 [[OALSimpleAudio sharedInstance] preloadBg:@"game_music.mp3"]; [[OALSimpleAudio sharedInstance] playBgWithLoop:YES]; // [[OALSimpleAudio sharedInstance]playBg:@"game_music.mp3" loop:YES]; // 3.初始化 子弹两次出现 之间需要的总的累计间隔时间,如 0.3秒 // 这样一设置之后,game一开始就有子弹射出去了 _pelletTotalIntervalTime = kPelletTotalIntervalTime; } #pragma mark 暂停 - (void)pauseGame { // 1.首先置标记为 0 _isGameRuning = NO; // 停止交互(触摸移动) self.userInteractionEnabled = NO; // 2.停止所有消息调度 // 同时,也要让spriteBatchNode中管理的所有成员,执行 unscheduleAllSelectors 方法 [_batch.children makeObjectsPerformSelector:@selector(unscheduleAllSelectors)]; // 3.暂停背景音乐 [[OALSimpleAudio sharedInstance]stopBg]; [[OALSimpleAudio sharedInstance]stopAllEffects]; } #pragma mark 结束 - (void)endGame { // 1.首先置标记为 0 _isGameRuning = NO; self.userInteractionEnabled = NO; // 2.停止所有消息调度 // 同时,也要让spriteBatchNode中管理的所有成员,执行 unscheduleAllSelectors 方法 [_batch.children makeObjectsPerformSelector:@selector(unscheduleAllSelectors)]; // 3.停止背景音乐 } #pragma mark - 时钟方法 - (void)update:(CCTime)delta { // 如果 游戏暂停中,则不更新任何状态 if (!_isGameRuning) { return; } // 1.背景动画(始终向下滚动) [self bgAnimation]; // 2.子弹动画(射出,运行轨迹) [self pelletAnimation:delta]; // 3.敌机出场作战 [self enemyAnimation:delta]; // 4.碰撞\边界检测 [self collsionAndBoundaryCheck]; } #pragma mark 1.滚动背景 // 1.背景动画(始终向下滚动) - (void)bgAnimation { // 2张背景图片,始终相差一张图片的高度(无论屏幕多高) CGFloat height = _bg1.contentSize.height; CGFloat bg1_Y = _bg1.position.y; // 向下走 bg1_Y--; // 临界点 // 向下走过程中,如果 1 超出屏幕了,将1 拉回去(2的y取决于1的y,因此,只要设置1的y) if (bg1_Y <= -height) { bg1_Y = 0; } _bg1.position = ccp(0, bg1_Y); // 2始终在1的头顶上 _bg2.position = ccp(0, bg1_Y + height); } #pragma mark 2.子弹动画 - (void)pelletAnimation:(CCTime)delta { // 先累加 delta时间,到一定量时,再发射子弹 _pelletTotalIntervalTime += delta; // 如果累计间隔时间达到了 预订的值:如0.3秒,可以发射子弹,位置是:英雄头顶 if (_pelletTotalIntervalTime >= kPelletTotalIntervalTime) { Pellet *pellet; for (pellet in _pelletArr) { // 如果数组中,还有 子弹不可见,说明在屏幕外;因此,可回收循环使用;只需重新调整位置 发射出去 if (pellet.visible == NO) { CGPoint from = ccp(_hero.position.x, CGRectGetMaxY(_hero.boundingBox)); [pellet emitFrom:from velocity:ccp(0, 200)]; break; } // 如果,数组中没有一个可以用的....那么遍历完后,只能创建新的了,(记得也要加到数组中去哦~) pellet = nil; } // 如果,数组中没有一个可以用的....那么遍历完后,只能创建新的发射了,(记得也要加到数组中去哦~) // 没有可循环利用的子弹 if (pellet == nil) { // 其实,内部已经封装了,默认创建出来的新子弹是不可见,只有在发射emit时,才可见 Pellet *p = [Pellet node]; // 大子弹 p.type = kPelletTypeBig; // 重要~~~一定要,记得,添加到数组中 [_pelletArr addObject:p]; // 添加到精灵batchNode [_batch addChild:p]; // 射出新子弹 CGPoint from = ccp(_hero.position.x, CGRectGetMaxY(_hero.boundingBox)); [p emitFrom:from velocity:ccp(0, 200)]; } // 用于记录的成员变量,清零 _pelletTotalIntervalTime = 0; } } #pragma mark 3.敌机出场作战 - (void)enemyAnimation:(CCTime)delta { Enemy *enemy; for (enemy in _enemyArr) { // 如果数组中,还有 敌机不可见,说明在屏幕外;因此,可回收循环使用;只需重新调整位置 参加战场 if (enemy.visible == NO) { // 具体如何 运动,内部自己决定 [enemy attendTheBattle]; // 一次时钟周期,送一个敌机上战场 break; } } } #pragma mark 碰撞\边界检测 - (void)collsionAndBoundaryCheck { // 子弹检测 Pellet *pellet; for (pellet in _pelletArr) { // 只有在屏幕范围内,可见的,才需要进行碰撞检测;如不可见,直接,分析下一个子弹 if (pellet.visible == NO) continue; // 1.子弹 与 屏幕 检测 // 2.子弹 与 敌机 检测 [self checkPellet:pellet]; }// 遍历子弹缓存数组 结束 } // 1.子弹 与 屏幕 检测 // 2.子弹 与 敌机 检测 - (void)checkPellet:(Pellet *)pellet { // 子弹边框与屏幕边框 进行交集检测 if (!CGRectIntersectsRect(self.boundingBox, pellet.boundingBox)) { // 1.离开屏幕了(再无交集),子弹消失 [pellet dismiss]; } else { // 2.子弹还在屏幕内,才要进行,与敌机的 碰撞检测 // 敌机检测 Enemy *enemy; for (enemy in _enemyArr) { // 如果不可见,不需要检测 // 只有在屏幕范围内,可见的,才需要进行碰撞检测;如不可见,直接,分析下一个 if (enemy.visible == NO) continue; // 1.敌机与屏幕rect检测 // 2.敌机与子弹检测 // 3.敌机与英雄检测 [self enemy:enemy checkWithPellet:pellet]; }// 遍历子弹缓存数组 结束 }// 子弹 和 敌机的 碰撞检测 } // 1.敌机与屏幕rect检测 // 2.敌机与子弹检测 // 3.敌机与英雄检测 - (void)enemy:(Enemy *)enemy checkWithPellet:(Pellet *)pellet { // 敌机 边框与屏幕rect边框 进行交集检测 if (!CGRectIntersectsRect(self.boundingBox, enemy.boundingBox)) { // 如果 没有交集,代表,敌机 逃离屏幕范围,故隐藏 敌机; [enemy hide]; } else if (CGRectIntersectsRect(pellet.boundingBox, enemy.boundingBox)) { // 只有在屏幕范围内的敌机,才需要 和 (在屏幕范围内的)子弹 进行交集检测 // 首先,隐藏子弹 [pellet dismiss]; // 然后,敌机中弹 [enemy gotHit]; } else { // 自定义方法,英雄与敌机 交集检测 [self enemyCheckWithHero:enemy]; } } // 自定义方法,英雄与敌机 交集检测 - (void)enemyCheckWithHero:(Enemy *)enemy { // 子弹 虽然 没有打中,但是 英雄与敌机 相撞,结果还是,game over if (CGRectContainsRect(_hero.boundingBox, enemy.boundingBox)) { [_hero dieWithEnemyTogether]; // game over,结束游戏 [self endGame]; } } #pragma mark - 触摸事件 // 必须有 touch began ,否则 不能响应 - (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event { } - (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event { // 1.当前点 CGPoint curPoint = [[CCDirector sharedDirector] convertToGL:[touch locationInView:touch.view]]; // 2.上一个点 CGPoint prePoint = [[CCDirector sharedDirector] convertToGL:[touch previousLocationInView:touch.view]]; // 3.设置飞机的位置 CGPoint newPosition = ccpAdd(_hero.position, ccpSub(curPoint, prePoint)); // 4.拖动过程中,边界检测,不能出屏幕 CGFloat _heroWidth = _hero.contentSize.width; CGFloat _heroHeight = _hero.contentSize.height; if (newPosition.x < _heroWidth*0.5) { return; } else if(newPosition.x > self.contentSize.width - _heroWidth*0.5){ return; }else if (newPosition.y < 0){ return; }else if(newPosition.y > self.contentSize.height - _heroHeight){ return; } _hero.position = newPosition; } @end<span style="font-family:Courier New;color:#393939;"><span style="font-size: 24px; line-height: 32px; background-color: rgb(245, 245, 245);"><strong> </strong></span></span>
子弹Pellet
// // Pellet.h // 31_cocos2D入门 // // Created by beyond on 14-9-28. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "CCSprite.h" typedef enum { // 小子弹 kPelletTypeSmall, // 大子弹 kPelletTypeBig }PelletType; @interface Pellet : CCSprite // 子弹类型 @property (nonatomic,assign) PelletType type; // 子弹速度 (Y方向,和X方向) @property (nonatomic, assign) CGPoint velocity; // 发射 emit,参数:射出的点 射出的速度 - (void)emitFrom:(CGPoint)from velocity:(CGPoint)velocity; // 消失/隐藏 - (void)dismiss; @end
// // Pellet.m // 31_cocos2D入门 // // Created by beyond on 14-9-28. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "Pellet.h" // 精灵帧 用到 #import "cocos2d.h" // 音效 //#import "SimpleAudioEngine.h" #import "OALSimpleAudio.h" @implementation Pellet #pragma mark - 父类方法 -(id)init { if (self=[super init]) { // 子弹创建时,默认隐藏,只有当调用发射emit方法时,才显示 self.visible = NO; } return self; } #pragma mark - 拦截setter - (void)setType:(PelletType)type { _type = type; // 更换子弹精灵的 图片 NSString *imgName = _type == kPelletTypeSmall ? @"bullet1.png" : @"bullet2.png"; CCSpriteFrame *frame = [CCSpriteFrame frameWithImageNamed:imgName]; [self setSpriteFrame:frame]; } #pragma mark - 供外界调用 // 发射 emit,参数:射出的点 射出的速度 - (void)emitFrom:(CGPoint)from velocity:(CGPoint)velocity{ self.visible = YES; // 设置位置 self.position = from; // 设置速度 self.velocity = velocity; // 音效播放 // [[SimpleAudioEngine sharedEngine] playEffect:@"Pellet.mp3"]; [[OALSimpleAudio sharedInstance]playEffect:@"bullet.mp3"]; } // 消失/隐藏 - (void)dismiss { self.visible = NO; [self unscheduleAllSelectors]; } #pragma mark - 时钟方法(飞行) - (void)update:(CCTime)delta { // 当子弹消失后,不再更新position... if (!self.visible) { return; } // deltaTime 时间内,移动(飞行)一段距离 // 路程s = 速度v * 时间t CGPoint s = ccpMult(self.velocity, delta); self.position = ccpAdd(self.position, s); } @end
英雄Hero
// // Hero.h // 31_cocos2D入门 // // Created by beyond on 14-9-27. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "CCSprite.h" @interface Hero : CCSprite // 简单处理,英 雄与敌机 相撞,结果还是,game over - (void)dieWithEnemyTogether; @end
// // Hero.m // 31_cocos2D入门 // // Created by beyond on 14-9-27. // Copyright (c) 2014年 com.beyond. All rights reserved. // 主角,代表玩家的 英雄 #import "Hero.h" #import "cocos2d.h" #import "CCDirector.h" #import "CCAnimation.h" @implementation Hero // 初始化玩家(的飞机) - (id)init { // 其实,磁盘里没有hero_fly_1.png 图片,它是到大图片的帧缓存中,根据key取出(裁剪)小图片 if (self = [super initWithImageNamed:@"hero_fly_1.png"]) { // 1.初始化位置,底部,中间 self.anchorPoint = ccp(0.5, 0); // 从导演那儿拿到viewSize就是屏幕的size CGSize fullScreenSize = [[CCDirector sharedDirector] viewSize]; self.position = ccp(fullScreenSize.width * 0.5, 0); // 2.创建 帧动画 需要的两个精灵帧 CCSpriteFrameCache *cache = [CCSpriteFrameCache sharedSpriteFrameCache]; CCSpriteFrame *frame1 = [cache spriteFrameByName:@"hero_fly_1.png"]; CCSpriteFrame *frame2 = [cache spriteFrameByName:@"hero_fly_2.png"]; NSArray *spriteFramesArr = @[frame1, frame2] ; // 3.为精灵添加动画 CCAnimation *animation = [CCAnimation animationWithSpriteFrames:spriteFramesArr delay:0.1]; CCActionAnimate *animate = [CCActionAnimate actionWithAnimation:animation]; // 4.播放帧动画 [self runAction:[CCActionRepeatForever actionWithAction:animate]]; } return self; } // 简单处理,英 雄与敌机 相撞,结果还是,game over - (void)dieWithEnemyTogether { // 停止之前的帧动画 [self stopAllActions]; // 直接播放 爆炸 帧动画 NSMutableArray *frames = [NSMutableArray array]; for (int i = 1; i<=4; i++) { NSString *name = [NSString stringWithFormat:@"hero_blowup_%d.png", i]; CCSpriteFrame *f = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:name]; [frames addObject:f]; } CCAnimation *animation = [CCAnimation animationWithSpriteFrames:frames delay:0.1]; [self runAction:[CCActionAnimate actionWithAnimation:animation]]; } @end
敌机Enemy(有待发散)
// // Enemy.h // 31_cocos2D入门 // // Created by beyond on 14-9-28. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "CCSprite.h" //对应plist文件中 5种图片 typedef enum { kEnemyType_1 = 1, kEnemyType_2 = 2, kEnemyType_3 = 3, kEnemyType_4 = 4, kEnemyType_5 = 5 } EnemyType; @interface Enemy : CCSprite @property (nonatomic, assign) EnemyType type; // 出现在游戏中,出场,加入战斗 - (void)attendTheBattle; - (void)hide; // 被击中 - (void)gotHit; @end
// // Enemy.m // 31_cocos2D入门 // // Created by beyond on 14-9-28. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "Enemy.h" #import "cocos2d.h" #import "OALSimpleAudio.h" #import "CCAnimation.h" #import "CCDirector.h" @interface Enemy() { // 最大的生命值 int _maxHp; // 生命值 int _hp; } @end @implementation Enemy #pragma mark - 父类方法 -(id)init { if (self=[super init]) { // 敌机创建时,默认隐藏,只有当调用出场,参加战斗 时,才显示 self.visible = NO; } return self; } #pragma mark - 拦截setter方法 - (void)setType:(EnemyType)type { _type = type; // 根据 敌人类型,设置 敌人显示的图片 [self setEnemyImage]; // 根据 敌人类型,设置 敌人最大生命值 switch (type) { case kEnemyType_1: _maxHp = 1; break; case kEnemyType_2: _maxHp = 2; break; case kEnemyType_3: _maxHp = 3; break; case kEnemyType_4: _maxHp = 4; break; case kEnemyType_5: _maxHp = 5; break; default: break; } } #pragma mark - 时钟方法 - (void)update:(CCTime)delta { // 同其他的子弹、英雄一样,只有在显示时,才需要更新位置 // 当敌人处于不可见时,说明被打死了,或逃出屏幕了,因此无需更新其位置 if (!self.visible) { return; } // 不断调整敌人的位置 (速度 可以和最大生命值一样,设置不同的...) self.position = ccpAdd(self.position, ccp( 0, -100 * delta)); } #pragma mark - 供外部调用 // 出现在游戏中,出场,加入战斗 - (void)attendTheBattle { // 出场时,满血 _hp = _maxHp; // 出场时,显示 self.visible = YES; // 出场时,位置,从屏幕顶部俯冲下来 self.anchorPoint = ccp(0.5, 0); CGSize winSize = [CCDirector sharedDirector].viewSize; // 重要~~~之所以要减一,是因为...出场时,要让它一只脚 踏进屏幕,与屏幕窗口的rect有交集 self.position = ccp(winSize.width * CCRANDOM_0_1(), winSize.height - 1); // 边界检测 } // 被击中 - (void)gotHit { _hp--; if (_hp == 0) { // 1.播放对应的被打中时 的音效 [self playHitSound]; // 2.播放对应的被爆炸动画 [self playExplodeAnimation]; } } // 消失 - (void)hide { self.visible = NO; [self unscheduleAllSelectors]; } #pragma mark - 抽取方法 // 根据 敌人类型,设置 敌人显示的图片 - (void)setEnemyImage { // 根据 敌人类型,设置 其图片 NSString *name = [NSString stringWithFormat:@"enemy%d_fly_1.png", _type]; // 从精灵帧缓存中,取出精灵帧,从而设置到精灵身上,显示 CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:name]; [self setSpriteFrame:frame]; } // 播放对应的被打中时 的音效 - (void)playHitSound { [[OALSimpleAudio sharedInstance] playEffect:[NSString stringWithFormat:@"enemy%d_down.mp3", _type]]; } // 播放爆炸动画,爆炸完,隐藏,最后重新设置显示图片,为参加下一次战斗作准备 - (void)playExplodeAnimation { NSMutableArray *frames = [NSMutableArray array]; // 这个爆炸效果的 4 应该动态变化 .... for (int i = 1; i<=4; i++) { NSString *name = [NSString stringWithFormat:@"enemy1_blowup_%d.png", i]; CCSpriteFrame *f = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:name]; [frames addObject:f]; } // 名词,动画 CCAnimation *animation = [CCAnimation animationWithSpriteFrames:frames delay:0.1]; // 动作_1 爆炸 CCActionAnimate *explosion = [CCActionAnimate actionWithAnimation:animation]; // 动作_2 不可见 CCActionHide *hide = [CCActionHide action]; // 动作_3 因为爆炸后,精灵的显示图片变了,所以要重新设置回来,以便下一次 参加战斗 CCActionCallFunc *func = [CCActionCallFunc actionWithTarget:self selector:@selector(setEnemyImage)]; // 动作_4 用序列包装 CCActionSequence *sequence = [CCActionSequence actions:explosion,hide,func, nil]; // 执行动作 [self runAction:sequence]; } @end
iOS_31_cocos2d_微信飞机
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。