和大家聊聊天
有段日子没有发布过任何文字和代码了,之前的文章下很多网友留言也没有回复,其实每条评论我都有认真看.只是最近整个人有点迷茫,望大家理解.其实我很期盼大家和我聊聊天,但不要总是聊技术…
关于项目(代码下载地址在文章最下面点击GitHub链接)
项目说明:考虑到许多不会使用Cocos2D-X和Swift的朋友,此次项目采用Objective-C并且基于UIKit框架实现的.意思就是你会使用UIView,就可以尝试开发游戏了,嘿嘿!
原生项目是采用Cocos2D-X开发的,所以在对图片的动画处理时,有些地方会没有原生显得那么流畅(如切割图片,对图片的变形处理,图片快速替换等),并且在性能上来说,UIKit也不如Cocos2D-X流畅,毕竟术业有专攻.如果是要开发游戏来上架的话,最好采用专门的游戏引擎来搭建项目(Cocos-2D,Unity3D,Sprite Kit等).
开发语言:Objective-C
开发工具:Xcode7.1
编译环境:大于Xcode7.0
辅助工具:Photoshop CS6
项目讲解: 把整个项目用文字带着大家过一遍有点不现实.这里我将项目的大体结构和一些主要逻辑,以及主要对象提供的接口功能下面列举出来.建议同学们先看代码,配合代码再来看这篇文章,顺着代码和文字搞懂项目主体逻辑.当需要学习具体功能如何实现时,在看.m
文件下的实现代码学习如何实现功能,如果有哪些地方不清楚,在简书下面留言或者微博留言.
学习建议:最好使用真机来进行运行调试,有些关卡需要使用加速计与陀螺仪等功能,模拟器是没有的.当遇到实在无法过去的关卡时,点击首页的有些手柄按钮,点击解锁下一关或者在代码启动时,手动写入关卡得分信息即可.
主体架构
音效和背景音乐
音效和背景音乐采用了AVFoundation
框架封装了一个WNXSoundToolManager
的单利对象,背景音乐采用AVAudioPlayer
,背景音效采用AudioServicesPlaySystemSound
.
提供以下方法和属性供全局调用或修改,通过修改bgMusicType
和soundType
可以控制背景音乐和音效声音的大小,通过playSoundWithSoundName:
方法根据音效名称设置播放不同的音效.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// 音效或背景音乐播放声音打大小枚举 typedef NS_ENUM(NSInteger, SoundPlayType) { SoundPlayTypeHight = 0, SoundPlayTypeMiddle, SoundPlayTypeLow, SoundPlayTypeMute }; @interface WNXSoundToolManager : NSObject // 背景音乐声音大小Type @property (nonatomic, assign) SoundPlayType bgMusicType; // 音效声音大小Type @property (nonatomic, assign) SoundPlayType soundType; // 暂停背景音乐 - (void)pauseBgMusic; // 停止播放背景音乐 - (void)stopBgMusic; // 重新播放背景音乐 - (void)playBgMusicWihtPlayAgain:(BOOL)playAgain; // 播放音效:音效名称 - (void)playSoundWithSoundName:(NSString *)soundName; // 设置背景音乐音量:音量大小0~1 - (void)setBackgroundMusicVolume:(float)volume; // 获取SoundManager单利对象 + (instancetype)sharedSoundToolManager; @end |
保存和读取玩家关卡记录(WNXStageInfoManager)
如何持久化存储玩家过关信息和每关的得分记录.本项目采用归档和解档的方案.
拿到WNXStageInfoManager
的单例对象,通过调用Save和Read方法保存或读取关卡信息,当游戏关卡进入结算得分控制器后,判断新记录是否需要保存,如果需要调用保存接口.具体实现代码请参照WNXStageInfoManager.m
文件
1 2 3 4 5 6 7 8 9 10 |
// 单例方法 + (instancetype)sharedStageInfoManager; // 保存关卡信息 - (BOOL)saveStageInfo:(WNXStageInfo *)stageInfo; // 读取指定关卡编号的关卡信息 - (WNXStageInfo *)stageInfoWithNumber:(int)number; // 这个接口是当游戏无法过关时,在RootViewController点击手柄按钮,解锁下一关卡使用(**秘籍~慎用**) - (BOOL)unlockNextStage; |
启动页动画
启动页动画是目前App比较常见的功能(顺丰优选,顺手付,顺丰海淘等都有).其实这里有一种假象,在AppDelegate的didFinishLaunchingWithOptions()
方法中,添加一个与启动图片完全一样的AnimVC,将AnimVC设置为keyWindow的rootViewController,在AnimVC的viewDidApper()
方法中执行动画,当动画完成后通过Block切换keyWindow的rootViewController为首页VC就OK了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[UIApplication sharedApplication] setStatusBarHidden:YES]; [NSThread sleepForTimeInterval:1.0]; [self setKeyWindow]; return YES; } - (void)setKeyWindow { __weak typeof(self) weakSelf = self; WNXLaunchAnimationViewController *launchAnimationVC = [[WNXLaunchAnimationViewController alloc] init]; launchAnimationVC.animationFinish = ^{ UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; WNXBaseNavigationController *rootNav = (WNXBaseNavigationController *)[sb instantiateViewControllerWithIdentifier:@"RootNavigationController"]; weakSelf.window.rootViewController = rootNav; }; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window.rootViewController = launchAnimationVC; [self.window makeKeyAndVisible]; } |
关于动画这里我就不讲什么了,有兴趣的朋友可以自己参考工程代码研究下.
首页(WNXRootViewController)
首页其实就是一张图片,通过判断当前设备屏幕尺寸,读取当前设备尺寸对应按钮的Plist文件,拿到首页6个按钮位置的Frame,在touchesBegan()
方法中,通过CGRectContainsPoint
方法判断当前点击位置时候在指定的Frame内,符合条件时做出对应 的操作,具体代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
// 加载当前设备对应首页按钮Frame - (void)loadHomeButtonFrame { NSString *framePath = [[NSBundle mainBundle] pathForResource:@"home.plist" ofType:nil]; NSDictionary *frameDic = [NSDictionary dictionaryWithContentsOfFile:framePath]; NSDictionary *dict; if (iPhone5) { dict = frameDic[@"iphone5"]; } else { dict = frameDic[@"iphone4"]; } _settingFrame = CGRectFromString(dict[@"btn_setting_frame"]); _languageFrame = CGRectFromString(dict[@"btn_language_frame"]); _moreFrame = CGRectFromString(dict[@"btn_more_frame"]); _rankFrame = CGRectFromString(dict[@"btn_rank_frame"]); _playFrame = CGRectFromString(dict[@"btn_play_frame"]); _getFrame = CGRectFromString(dict[@"btn_get_frame"]); } // 判断点击点是否在对应的Frame内 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint touchPoint = [touch locationInView:touch.view]; [[WNXSoundToolManager sharedSoundToolManager] playSoundWithSoundName: kSoundCliclName]; if (CGRectContainsPoint(_settingFrame, touchPoint)) { [self performSegueWithIdentifier:@"Setting" sender:nil]; } else if (CGRectContainsPoint(_languageFrame, touchPoint)) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:kBlogURL]]; } else if (CGRectContainsPoint(_moreFrame, touchPoint)) { [self performSegueWithIdentifier:@"Rare" sender:nil]; } else if (CGRectContainsPoint(_rankFrame, touchPoint)) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:kWeiBoURL]]; } else if (CGRectContainsPoint(_playFrame, touchPoint)) { [self performSegueWithIdentifier:@"PlayGame" sender:nil]; } else if (CGRectContainsPoint(_getFrame, touchPoint)) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:kGithubUrl]]; } } |
关卡选择控制器(WNXSelectStageViewController)
关卡选择控制器采用UIScrollView实现,在scrollView放入24个WNXStageListView
(当然这里也可以自己创建缓存池复用,个人觉得没必要),每个WNXStageListView
都有对应的一个关卡信息模型stageModel
,模型属性从工程->Resources->Plist->stages.plist文件中读取,根据model
里的成员变量,加载关卡对应的信息,如关卡图片,是否解锁,玩家历史得分以及Rank标记等.
每个WNXStageListView
,根据ID设置不同的Tag,并且提供单击手势,在stageView
的点击事件中.调用导航控制器,Push到WNXPrepareViewController
控制器,并将选择关卡的stageModel作为参数传过去,WNXPrepareViewController
做出相应的展示即可.
选择关卡效果如下图所示
选择关卡效果图
关卡准备开始控制器(WNXPrepareViewController)
每个关卡开始游戏前,都会以动画的形式出现本关游戏名称,过关规则,以及历史得分等一系列功能.都是由这个控制器完成的.通过选择关卡时传入的stageModel
,展示model
内对应的数据,当用户点击Play
按钮时,使用WNXGameControllerViewManager
单例对象,根据传入的stageModel
,返回对应的关卡ViewController,然后Push到返回的ViewController游戏关卡即可.
关卡控制器
24关,每关都有很多重复的功能,这里我们按照不同关卡的属性抽取出几种公共的父类,每个关卡根据自己的需求选择继承相应的控制器,并且在ViewDidLoad
函数中初始化每个关卡不同的属性,具体分类效果如下图所示
WNXBaseGameViewController –> UIViewController
WNXBaseGameViewController是所有关卡ViewController的基类控制器,提供每个游戏关卡的基本属性设置,并且每个关卡的初始化操作都封装在了这里,每个关卡只需要在自己的ViewDidLoad方法中调用buildStageInfo()
函数,添加构建自己的UI即可,重写父类的方法,完成每关不同的操作.
公有属性
WNXGameGuideType guideType
每关第一次进入关卡,本关游戏手势提示样式WNXGameGuideTypeNone
无提示WNXGameGuideTypeOneFingerClick
单个手指头点击WNXGameGuideTypeReplaceClick
左右按钮交替点击WNXGameGuideTypeMultiPointClick
多个手指同时点击
单个手指头点击效果
WNXStage *stage
每关关卡信息model(model详情)WNXScoreboardType
每关计分板样式WNXScoreboardTypeNon