www.2527.com_澳门新葡8455手机版_新京葡娱乐场网址_
做最好的网站

www.2527.comiOS游戏开采尚未您想的那么难,实现微

2019-09-16 08:45 来源:未知

www.2527.com 1

关卡选择控制器(WNXSelectStageViewController)

关卡选择控制器采用UIScrollView实现,在scrollView放入24个WNXStageListView(当然这里也可以自己创建缓存池复用,个人觉得没必要),每个WNXStageListView都有对应的一个关卡信息模型stageModel,模型属性从工程->Resources->Plist->stages.plist文件中读取,根据model里的成员变量,加载关卡对应的信息,如关卡图片,是否解锁,玩家历史得分以及Rank标记等.

每个WNXStageListView,根据ID设置不同的Tag,并且提供单击手势,在stageView的点击事件中.调用导航控制器,Push到WNXPrepareViewController控制器,并将选择关卡的stageModel作为参数传过去,WNXPrepareViewController做出相应的展示即可.选择关卡效果如下图所示

www.2527.com 2选择关卡效果图

上一节,我们完成了建筑物的动态生成效果。在三种建筑物中,有一种建筑物,也就是商店,一旦它生成后,能产生一种特殊效果,那就是有一个钻石精灵会动态的漂浮在它下方,一旦用户点击后,玩家的钻石数量可以相应增加。本节的主要目的就是实现浮动精灵的动画特效,完成本节代码后,效果如下:

01.使用NSUserDefaults来保存数据

使用NSUserDefaults用于持久性的保存得分记录是一个明智的决定,我们不可能为了存一个数据而使用诸如Core Data或者Sqlite等数据库。

请在GameScene类中添加一个新方法,用于读取游戏记录得分:

func bestScore()->Int{ return NSUserDefaults.standardUserDefaults().integerForKey("BestScore")}

可以看到方法中为最高得分设置了名为"BestScore"的键。

有读取自然有写,请在bestScore()方法下方添加一个新方法:

func setBestScore(bestScore:Int){ NSUserDefaults.standardUserDefaults().setInteger(bestScore, forKey: "BestScore") NSUserDefaults.standardUserDefaults().synchronize()}

有段日子没有发布过任何文字和代码了,之前的文章下很多网友留言也没有回复,其实每条评论我都有认真看.只是最近整个人有点迷茫,望大家理解.其实我很期盼大家和我聊聊天,但不要总是聊技术...

动画精灵的本质是把一系列图片连续显示,进而展现出一种动画效果。我们的钻石精灵就是把上面图片中的五个图案在单位时间内多次连续显示,上面图片连续显示后就会在页面上展现出一种转动不停的特效。

系列:用Swift作个游戏作者:pmst(1345614869)微博:PPPPPPMST

小熊的新浪微博

我的新浪微博,欢迎关注

欢迎关注公众号,让我们一起学习,交流,成长:

思路:

分数结算控制器(WNXResultViewController)

当每个关卡游戏结束后,都会进入分数结算控制器,这里通过在WNXBaseGameViewController中封装了一个方法以保证每个关卡控制器都可以直接调用计算得分,当关卡游戏结束后,调用当前关卡的下面函数即可,这里小熊偷了个懒,只实现了相加的功能,不过相信通过参考相加的功能,大家实现相减的功能也是小csae啦~

- showResultControllerWithNewScroe:scroe unit:(NSString *)unil stage:(WNXStage *)stage isAddScore:isAddScroe;

说明下isAddScore的作用

  • 有些关卡是得分越高越好.这总关卡在显示结果的时候分数是从0一点点网上加的,这种情况isAddScore传入YES

  • 有些关卡是得分越少越好,这总卡在显示结果的时候分数是从大网小一点点减少的,这种情况isAddScore传入NO

当结算分数完成后,会出现以下几种情况,跟据不同的得分情况执行不同的逻辑即可,具体逻辑如下所示

www.2527.com 3得分不够,显示失败

  • 当前关卡无得分记录,并且得分大于F,保存玩家得分,正常显示得分结果,并且解锁下一关.

www.2527.com 4成功状态1

  • 当前关卡有记录,但是本次游戏得分没有超越历史记录,正常显示得分结果,不保存本次游戏得分.

www.2527.com 5成功状态2

  • 当前关卡有记录,并且本次游戏得分超越历史记录,显示超越历史得分动画,并且讲本次得分替换掉上一次得分.

www.2527.com 6成功状态3

接着我们要实现的是城市的资源生长逻辑。建造一个发电厂,游戏就可以增加人口数量,每隔一定时间,城市的钱币数就可以自动增加,如果建造了造钱厂,那么钱币增加的时间间隔就会减少,如果建筑了商城,那么当钱币数目超过200,并且经过一定的时间间隔后,商城旁边就会跳动出一个旋转的钻石,点击钻石,城市的钻石数目就可以增加。由此回到gamescenecomponent.vue中增加相应代码:

02.构建得分面板

得分面板如文中给出图片布局,工程量还是蛮大的,不过我会一步一步讲解,无需担心一口吃撑的情况。

首先请找到setupLabel方法,在它下方声明咱们的设置得分面板的函数,取名为setupScorecard():

func setupScorecard() { //1 if score > bestScore() { setBestScore } //...等下添加其他内容}
  • 首先调用bestScore()取到历史最高得分,与本回合得分比较,倘若这次“走狗屎运”得了高分,咱们就要更新历史最高纪录,也就是调用setBestScore方法。

接着,着手添加得分面板的精灵,内容有点多,请注意别码错:

func setupScorecard() { if score > bestScore() { setBestScore } // 1 得分面板背景 let scorecard = SKSpriteNode(imageNamed: "ScoreCard") scorecard.position = CGPoint(x: size.width * 0.5, y: size.height * 0.5) scorecard.name = "Tutorial" scorecard.zPosition = Layer.UI.rawValue worldNode.addChild(scorecard) // 2 本次得分 let lastScore = SKLabelNode(fontNamed: kFontName) lastScore.fontColor = SKColor(red: 101.0/255.0, green: 71.0/255.0, blue: 73.0/255.0, alpha: 1.0) lastScore.position = CGPoint(x: -scorecard.size.width * 0.25, y: -scorecard.size.height * 0.2) lastScore.text = "" scorecard.addChild(lastScore) // 3 最好成绩 let bestScoreLabel = SKLabelNode(fontNamed: kFontName) bestScoreLabel.fontColor = SKColor(red: 101.0/255.0, green: 71.0/255.0, blue: 73.0/255.0, alpha: 1.0) bestScoreLabel.position = CGPoint(x: scorecard.size.width * 0.25, y: -scorecard.size.height * 0.2) bestScoreLabel.text = "(self.bestScore" scorecard.addChild(bestScoreLabel) // 4 游戏结束 let gameOver = SKSpriteNode(imageNamed: "GameOver") gameOver.position = CGPoint(x: size.width/2, y: size.height/2   scorecard.size.height/2   kMargin   gameOver.size.height/2) gameOver.zPosition = Layer.UI.rawValue worldNode.addChild // 5 ok按钮背景以及ok标签 let okButton = SKSpriteNode(imageNamed: "Button") okButton.position = CGPoint(x: size.width * 0.25, y: size.height/2 - scorecard.size.height/2 - kMargin - okButton.size.height/2) okButton.zPosition = Layer.UI.rawValue worldNode.addChild let ok = SKSpriteNode(imageNamed: "OK") ok.position = CGPoint.zeroPoint ok.zPosition = Layer.UI.rawValue okButton.addChild // 6 share按钮背景以及share标签 let shareButton = SKSpriteNode(imageNamed: "Button") shareButton.position = CGPoint(x: size.width * 0.75, y: size.height/2 - scorecard.size.height/2 - kMargin - shareButton.size.height/2) shareButton.zPosition = Layer.UI.rawValue worldNode.addChild(shareButton) let share = SKSpriteNode(imageNamed: "Share") share.position = CGPoint.zeroPoint share.zPosition = Layer.UI.rawValue shareButton.addChild}

当你码完了这一段超长代码之后,你会松一口气,现在还有一步就能享受胜利的果实了!!想想我们时候什么需要显示这个得分面板。Player掉落失败的时候,对吗?请找到switchToShowScore()方法,在最下方调用setupScorecard(),点击运行,过几个障碍物,然后自由落体,看看是否良好地显示了得分面板。

Good Job!显示本次得分,历史最高纪录以及选项按钮——不过此时并没有什么卵用。

你有木有发现得分面板“毫无征兆”地就出现在了场景中央,来加个动画吧!!!

项目总结

项目写的比较匆忙,基本每天晚上抽空写点,写完也没有回头CodeReview,说实话,这是一个非常非常不好的习惯,大家一定要养成定期回头看看自己写过代码的习惯.随着越网后写,发现前面有很多地方可以修改,我吧有点懒,So你懂的...

感觉光靠文字来讲述一个项目实在是太困难.希望大家还是参考工程代码,当遇到无法看懂或者不理解的时候参考下我写的Blog应该会更好一些.这个游戏项目说实话还是比较简单的,相信大家仔细研究下都可以实现的.游戏还有24关,有兴趣的同学可以尝试自己将剩下的24关自己实现下~

有段日子没使用OC写项目了,如果有任何建议可在简书留言,或者私信,或者在微博留言都可以,我都会看的.

这个项目完事后,可能会很长一段时间,不再写这种大型的开源项目了,因为我个人准备开发一款游戏上架到AppStore,从设计到UI设计以及需求实现都是我一人完成,工作量比较大.PS(现在连做什么都不知道呢...).

以后我会分享一些有意思的小功能,小动画等给大家.希望朋友继续关注维尼的小熊.

diamondSprite函数用来创建一个钻石转动的动画精灵,他会把精灵特效对应的图片加载到页面上,我们要实现的精灵特效图片如下:

  • 当游戏结束时呈现上图的内容。

关卡控制器

24关,每关都有很多重复的功能,这里我们按照不同关卡的属性抽取出几种公共的父类,每个关卡根据自己的需求选择继承相应的控制器,并且在ViewDidLoad函数中初始化每个关卡不同的属性,具体分类效果如下图所示

www.2527.com 7逻辑图

WNXBaseGameViewController是所有关卡ViewController的基类控制器,提供每个游戏关卡的基本属性设置,并且每个关卡的初始化操作都封装在了这里,每个关卡只需要在自己的ViewDidLoad方法中调用buildStageInfo()函数,添加构建自己的UI即可,重写父类的方法,完成每关不同的操作.

公有属性

  • WNXGameGuideType guideType每关第一次进入关卡,本关游戏手势提示样式
  • WNXGameGuideTypeNone无提示
  • WNXGameGuideTypeOneFingerClick单个手指头点击
  • WNXGameGuideTypeReplaceClick左右按钮交替点击
  • WNXGameGuideTypeMultiPointClick多个手指同时点击

www.2527.com 8单个手指头点击效果www.2527.com 9左右按钮交替点击效果www.2527.com 10多个手指同时点击效果样式

  • WNXStage *stage每关关卡信息model

  • WNXScoreboardType每关计分板样式

  • WNXScoreboardTypeNone无计分板

  • WNXScoreboardTypeCountPTS

    www.2527.com 11WNXScoreboardTypeCountPTS

  • WNXScoreboardTypeTimeMS

    www.2527.com 12WNXScoreboardTypeTimeMS

  • WNXScoreboardTypeSecondAndMS

    www.2527.com 13WNXScoreboardTypeSecondAndMS

www.2527.com 14WNXScoreboardTypeCountPTS计分板样式www.2527.com 15WNXScoreboardTypeTimeMS计分板样式www.2527.com 16WNXScoreboardTypeSecondAndMS计分板样式

  • UIView *countScoreView计分板(考虑有多种样式,使用了UIView,每个关卡在用的时候根据自己类型进行强制转换)

  • WNXStateView *stateView关卡提示状态View

  • UIButton *playAgainButton 重新开始游戏按钮

  • UIButton *pauseButton暂停按钮

公有方法

- beginGame; // 开始游戏- endGame; // 结束游戏- beginRedayGoView; // 开始显示RedayGo动画- readyGoAnimationFinish; // RedayGo动画显示结束- pauseGame; // 暂停游戏- continueGame; // 继续游戏- playAgainGame; // 重新开始游戏- showGameFail; // 游戏失败(部分关卡有, 进入失败ViewController)// 显示关卡游戏结果- showResultControllerWithNewScroe:scroe // 玩家得分 unit:(NSString *)unil // 本关计分器显示单位 stage:(WNXStage *)stage // 关卡信息 isAddScore:isAddScroe; // 是否是添加分数(这里偷了个懒,只做了添加动画,应该有分数增长加动画或者减少动画)// 构建关卡信息- buildStageInfo;// 将广告,重新开始,暂停按钮放到最上层- bringPauseAndPlayAgainToFront;// 构建显示状态View- buildStageView;

WNXRYBViewController,继承至WNXBaseGameViewController,底部拥有三个按钮,并且默认有三条红黄蓝背景条,底部按钮默认Tag为0,1,2,游戏大部分关卡为这种样式

公有属性

@property (strong, nonatomic) UIImageView *redImageView;@property (strong, nonatomic) UIImageView *yellowImageView;@property (strong, nonatomic) UIImageView *blueImageView;@property (strong, nonatomic) UIButton *redButton;@property (strong, nonatomic) UIButton *yellowButton;@property (strong, nonatomic) UIButton *blueButton;@property (nonatomic, strong) NSMutableArray *buttons;@property (nonatomic, strong) NSArray *buttonImageNames;

公有方法

- setButtonsIsActivate:isActivate; // 设置全部按钮是否可以点击- setButtonImage:(UIImage *)image // 当底部按钮图片相同时,设置底部按钮图片 contenEdgeInsets:(UIEdgeInsets)insets; // 图片的contenEdgeInsets- removeAllImageView; // 有写关卡不需要红黄蓝背景图片时,删除三个UIImageView// 底部按钮Action- addButtonsActionWithTarget:target action:action forControlEvents:(UIControlEvents)forControlEvents;

WNXTwoButtonViewController,底部拥有俩个按钮关卡,并且默认带有背景ImageView.

公有属性

@property (nonatomic, strong) UIImageView *backgroundIV;@property (nonatomic, strong) UIButton *leftButton;@property (nonatomic, strong) UIButton *rightButton;

公有方法

// 统一设置按钮是否可以被点击,部分关卡按钮点击后,不允许再次点击- setButtonActivate:isActivate;

只带有背景图关卡,项目中有些关卡是采用陀螺仪和加速计的关卡.

关于每一关如何实现,我这里就不一一列举了,有点太多了,但是都并不复杂,写个2~3关基本就能掌握套路了,就个别关卡使用了加速计和陀螺仪,具体实现的代码我都在工程中写的很明白了,在Stage文件夹下,大家自行参考即可.

www.2527.com 17

课时7中实现了得分机制,当你越战越勇,得分也蹭蹭地往上加,不过马有失蹄,人有失足,总会不小心失败,这时候就要结算你的劳动成果了:通常都是告知游戏结束,得分几何,最好成绩等等信息。咱们游戏是这么设计的:

保存和读取玩家关卡记录(WNXStageInfoManager)

如何持久化存储玩家过关信息和每关的得分记录.本项目采用归档和解档的方案.拿到WNXStageInfoManager的单例对象,通过调用Save和Read方法保存或读取关卡信息,当游戏关卡进入结算得分控制器后,判断新记录是否需要保存,如果需要调用保存接口.具体实现代码请参照WNXStageInfoManager.m文件

// 单例方法  (instancetype)sharedStageInfoManager;// 保存关卡信息- saveStageInfo:(WNXStageInfo *)stageInfo;// 读取指定关卡编号的关卡信息- (WNXStageInfo *)stageInfoWithNumber:number;// 这个接口是当游戏无法过关时,在RootViewController点击手柄按钮,解锁下一关卡使用(**秘籍~慎用**)- unlockNextStage;

diamondSprite函数先创建一个动画精灵对象,它定义了一个变量叫framerate,这个值用来指定一秒内连续展示几幅图片,我们规定一秒内展示16幅,在我们上面的图片中,总共有5幅图案,一旦图案展示完了后,代码会重新从第一幅开始,再次循环展示。里面的gotoAndPlay调用就开启了图片连续显示过程。

  • 简单来说就是实例化几个特定纹理(你可以理解为照片image)的精灵,然后按照特定布局放置到屏幕中,是不是灰常简单呢?

失败(WNXFailViewController)

部分关卡会有在游戏中失败的情况,如下图

www.2527.com 18游戏失败

这里也是在WNXBaseGameViewController中封装了一个方法,当关卡失败后,直接调用showGameFail()方法,Push到失败控制器即可.

如果需要失败时执行一些操作,如停止计时,停止动画等,在当前关卡重写showGameFail()方法,在调用父类方法前调用需要执行的相应代码即可,如下

- showGameFail { // 需要在游戏失败时执行的相应代码 // do something [super showGameFail];}

玩家在点击右下角的Building按钮,打开建筑物选择面板时,程序必须根据玩家当前的人口数,能源数以及钱币数来决定到底哪种建筑物是允许玩家选择的。上面代码中的decideWhetherButtonDisableOrNot所实现的逻辑就是做相应的判断,它计算玩家当前的各种资源,然后跟建筑物需要的资源数相比对,如果满足条件,那么在控制面板的相应建筑物按钮上就会有一个Build按钮,如果资源不满足,那么相应建筑物上就没有Build按钮。

本文任务:

辅助工具:Photoshop CS6

www.2527.com 19

03.为得分面板添加动画

请回到原先的setupScorecard()方法,继续再下方添加动画代码:

//=== 添加一个常量 用于定义动画时间 ====let kAnimDelay = 0.3func setupScorecard() { //。。。。。。 //==== 以下是新添加的内容 ===== gameOver.setScale gameOver.alpha = 0 let group = SKAction.group([ SKAction.fadeInWithDuration(kAnimDelay), SKAction.scaleTo(1.0, duration: kAnimDelay) ]) group.timingMode = .EaseInEaseOut gameOver.runAction(SKAction.sequence([ SKAction.waitForDuration(kAnimDelay), group ])) scorecard.position = CGPoint(x: size.width * 0.5, y: -scorecard.size.height/2) let moveTo = SKAction.moveTo(CGPoint(x: size.width/2, y: size.height/2), duration: kAnimDelay) moveTo.timingMode = .EaseInEaseOut scorecard.runAction(SKAction.sequence([ SKAction.waitForDuration(kAnimDelay * 2), moveTo ])) okButton.alpha = 0 shareButton.alpha = 0 let fadeIn = SKAction.sequence([ SKAction.waitForDuration(kAnimDelay * 3), SKAction.fadeInWithDuration(kAnimDelay) ]) okButton.runAction shareButton.runAction let pops = SKAction.sequence([ SKAction.waitForDuration(kAnimDelay), popAction, SKAction.waitForDuration(kAnimDelay), popAction, SKAction.waitForDuration(kAnimDelay), popAction, SKAction.runBlock(switchToGameOver) ]) runAction}

点击运行,玩耍吧!

注意: 我发现了一个BUG,倘若游戏一开始就使得它下落触碰地面,弹出的得分面板share标签放置位置是错误的,因为它的背景以let shareButton = SKSpriteNode(imageNamed: "Button") 返回的是一个精灵size=0,让我百思不得其解。希望找到问题的朋友可以告知我。

暂停控制器(WNXPauseViewController)

每个游戏关卡都有暂停的功能,所以将暂停的功能封装到WNXBaseGameViewController中,并且提供两个接口供子控制器调用,分别为

  • pauseGame; 暂停游戏
  • continueGame; 继续游戏

在每个游戏关卡重写上面两个方法,当玩家点击暂停按钮时,回调用暂停方法,点击返回时,会调用继续方法,具体实现如下

// 玩家点击暂停按钮- pauseGame { // 关卡暂停,本关需要执行的相应操作,如暂停计时器,动画等. [super pauseGame];}- continueGame { [super continueGame]; // 继续游戏,继续执行暂停前的操作}

www.2527.com 20暂停控制器效果图

至此,整个游戏的设计就结束了,其实游戏本身还有很多需要改进的地方。例如例如不同的建筑物,它的建筑完成时间是不一样的,有些建筑物例如商城需要玩家等待很长时间,这时候我们可以提供一个功能,玩家只要缴纳一点钱,我们便能开个后门,减少建筑物建筑的时间。同时我们还可以增加更多种类,功能也独特的建筑物,只有付费玩家才能选择等等。

开发语言:Objective-C

文章公众号.jpg

启动页动画

启动页动画是目前App比较常见的功能(顺丰优选,顺手付,顺丰海淘等都有).其实这里有一种假象,在AppDelegate的didFinishLaunchingWithOptions()方法中,添加一个与启动图片完全一样的AnimVC,将AnimVC设置为keyWindow的rootViewController,在AnimVC的viewDidApper()方法中执行动画,当动画完成后通过Block切换keyWindow的rootViewController为首页VC就OK了.

- application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[UIApplication sharedApplication] setStatusBarHidden:YES]; [NSThread sleepForTimeInterval:1.0]; [self setKeyWindow]; return YES;}- setKeyWindow { __weak typeof 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];}

关于动画这里我就不讲什么了,有兴趣的朋友可以自己参考工程代码研究下.

www.2527.com 21启动页动画

最新的HTML5提供了相关机制,让我们能实现页面数据的局部保存,相应代码如下:

代码下载地址(如果觉得有帮助,请点击Star★)

代码下载地址,记得Star★和Follow

点击链接我的博客,欢迎关注

www.2527.com 22

项目说明:考虑到许多不会使用Cocos2D-X和Swift的朋友,此次项目采用Objective-C并且基于UIKit框架实现的.意思就是你会使用UIView,就可以尝试开发游戏了,嘿嘿!

商店下方的钻石图片在页面上回呈现出反复转动的动态效果,具体特效请参看视频:
更详细的讲解和代码调试演示过程,请点击链接

项目讲解: 把整个项目用文字带着大家过一遍有点不现实.这里我将项目的大体结构和一些主要逻辑,以及主要对象提供的接口功能下面列举出来.建议同学们先看代码,配合代码再来看这篇文章,顺着代码和文字搞懂项目主体逻辑.当需要学习具体功能如何实现时,在看.m文件下的实现代码学习如何实现功能,如果有哪些地方不清楚,在简书下面留言或者微博留言.

calculateBuildingsEffects函数的作用是根据当前建筑物的情况来决定城市系统资源的变化,如果当前建筑物有发电厂,那么就调用evaluatePowerSupply增加城市能源的数量,如果有造币厂,那么就调用evaluateCoinsGeneration,把钱币生成的时间间隔减短3个时间单位,如果有商城,那么就调用evaluateMerchant函数,它会判断当前钱币是否足够多,当钱币满足时,还需等待一段时间后,才会在商城附近调用popDiamond函数,实现钻石精灵旋转的特效。coinsTick负责每过一段时间后就给城市增加一个钱币,它会调用calculateBuildingsEffects函数来统计城市系统当前的资源数量。

学习建议:最好使用真机来进行运行调试,有些关卡需要使用加速计与陀螺仪等功能,模拟器是没有的.当遇到实在无法过去的关卡时,点击首页的有些手柄按钮,点击解锁下一关或者在代码启动时,手动写入关卡得分信息即可.

在autoSave中,代码利用HTML5提供的localStorage对象将相关信息存储起来,它保存了游戏当前的钻石数和钱币数,并调用JSON.stringify把buildingList中存储的建筑物信息全部转换成JSON格式的字符串后,存储在localStorage的city.buildinglist字段下。

编译环境:大于Xcode7.0

当我们在转动的钻石精灵上点击后,程序会让游戏的总钻石数加一,并且把钻石精灵从舞台容器,也就是stage中拿掉,这样钻石精灵就从页面上消失了。函数popDiamond被调用时,他就会调用diamondSprite函数创建一个转动的钻石精灵,然后设置精灵在页面上的坐标轴,同时把精灵对象加入舞台容器,这样转动着的钻石精灵就会出现在页面上。

原生项目是采用Cocos2D-X开发的,所以在对图片的动画处理时,有些地方会没有原生显得那么流畅(如切割图片,对图片的变形处理,图片快速替换等),并且在性能上来说,UIKit也不如Cocos2D-X流畅,毕竟术业有专攻.如果是要开发游戏来上架的话,最好采用专门的游戏引擎来搭建项目(Cocos-2D,Unity3D,Sprite Kit等).

这里写图片描述

音效和背景音乐

音效和背景音乐采用了AVFoundation框架封装了一个WNXSoundToolManager的单利对象,背景音乐采用AVAudioPlayer,背景音效采用AudioServicesPlaySystemSound.

提供以下方法和属性供全局调用或修改,通过修改bgMusicTypesoundType可以控制背景音乐和音效声音的大小,通过playSoundWithSoundName:方法根据音效名称设置播放不同的音效.

// 音效或背景音乐播放声音打大小枚举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;// 暂停背景音乐- pauseBgMusic;// 停止播放背景音乐- stopBgMusic;// 重新播放背景音乐- playBgMusicWihtPlayAgain:playAgain;// 播放音效:音效名称- playSoundWithSoundName:(NSString *)soundName;// 设置背景音乐音量:音量大小0~1- setBackgroundMusicVolume:volume;// 获取SoundManager单利对象  (instancetype)sharedSoundToolManager;@end

后续我们还有更多好玩的精彩游戏要设计,请继续关注后续的内容。

www.2527.com 23Hardest

在tick函数中会调用coinsTick,由于tick函数是每秒被调用40次,因此游戏在每一秒都会多次触发前面提到的各种资源计算函数。接下来我们就需要看看如何实现钻石转动的精灵特效,它的实现代码如下:

关卡准备开始控制器(WNXPrepareViewController)

每个关卡开始游戏前,都会以动画的形式出现本关游戏名称,过关规则,以及历史得分等一系列功能.都是由这个控制器完成的.通过选择关卡时传入的stageModel,展示model内对应的数据,当用户点击Play按钮时,使用WNXGameControllerViewManager单例对象,根据传入的stageModel,返回对应的关卡ViewController,然后Push到返回的ViewController游戏关卡即可.

www.2527.com 24准备开始控制器效果图

diamondSprite () {
        var container = new this.cjs.Container()
        var data = {
          framerate: 16,
          images: ['../../staticiamond-spritesheet.png'],
          frames: {width: 90, height: 90}
        }
        var spriteSheet = new this.cjs.SpriteSheet(data)
        var diamondSprite = new this.cjs.Sprite(spriteSheet)
        diamondSprite.gotoAndPlay(0)
        diamondSprite.scaleX = diamondSprite.scaleY = 0.5
        container.addChild(diamondSprite)
        container.on('click', function () {
          this.diamonds  = 1
          this.stage.removeChild(container)
        }.bind(this))

        return container
      },
      // 在页面上弹出一个钻石
      popDiamond (building) {
        var screenCoord = this.isoToScreenCoord(building.x, building.y)
        var globalScreenCoord = this.cityLayer.localToLocal(screenCoord.x, screenCoord.y, this.stage)
        var diamond = this.diamondSprite()
        diamond.x = globalScreenCoord.x
        diamond.y = globalScreenCoord.y
        this.stage.addChild(diamond)
      },

开发工具:Xcode7.1

export default {
    data () {
      return {
      ....
      // change here
        // 经过800时间单位能创造一个钻石
        // change here for test
        tickCountForMerchantDiamond: 800,
        // 200金币换一个钻石
        coinsNeededForDiamond:  200,
        // 每隔90个时间单位增加一个金币
        coinGenerationCountDown: 90,
        // 当前总共增加几个金币
        coinGenerationCount: 0
        }
    }
    ....
    tick () {
    ....
    this.coinsTick()
    ....
    }
    ....
    calculateBuildingsEffects () {
        // 根据当前建筑物,计算金币,钻石和人口数量
        this.powerSupplies = 10
        this.populations = 0
        this.coinGenerationCountDown = 90

        for (var i = 0, len = this.buildingList.length; i < len; i  ) {
          var b = this.buildingList[i]
          var data = Constant[b.name]
          // 计算总人口
          this.evaluatePopulation(data.needPopulations)

          if (b.isConstructionDone) {
            this.evaluatePowerSupply(data.power)
            if (b.name === 'CoinsGenerator') {
              this.evaluateCoinsGeneration()
            }

            if (b.name === 'Merchant') {
              this.evaluateMerchant(b)
            }
          }
        }
      },
      evaluatePopulation (value) {
        this.populations  = value
      },
      evaluatePowerSupply (value) {
        this.powerSupplies  = value
      },
      evaluateCoinsGeneration () {
        this.coinGenerationCountDown -= 3
      },
      evaluateMerchant (building) {
        var b = building
        if (!b.diamonTick) {
          b.diamonTick = 0
        }

        b.diamonTick  = 1
        if (b.diamonTick >= this.tickCountForMerchantDiamond) {
          if (this.coins >= this.coinsNeededForDiamond) {
            this.coins -= this.coinsNeededForDiamond
            this.popDiamond(b)
            b.diamondTick = 0
          }
        }
      },
      coinsTick () {
        this.coinGenerationCount  = 1
        if (this.coinGenerationCount >= this.coinGenerationCountDown) {
          this.coins  = 1
          this.coinGenerationCount = 0
        }

        this.calculateBuildingsEffects()
      },

首页(WNXRootViewController)

首页其实就是一张图片,通过判断当前设备屏幕尺寸,读取当前设备尺寸对应按钮的Plist文件,拿到首页6个按钮位置的Frame,在touchesBegan()方法中,通过CGRectContainsPoint方法判断当前点击位置时候在指定的Frame内,符合条件时做出对应 的操作,具体代码

// 加载当前设备对应首页按钮Frame- loadHomeButtonFrame { NSString *framePath = [[NSBundle mainBundle] pathForResource:@"home.plist" ofType:nil]; NSDictionary *frameDic = [NSDictionary dictionaryWithContentsOfFile:framePath]; NSDictionary *dict; if  { 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内- touchesBegan:(NSSet<UITouch *> *)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]]; }}
init () {
    ....
// change here
        if (localStorage['city.buildinglist']) {
          this.buildingList = JSON.parse(localStorage['city.buildinglist'])
        } else {
          this.buildingList = []
        }
        // *1 将字符强行转换成数字
        this.coins = localStorage['city.coins'] * 1 || 10
        this.diamonds = localStorage['city.diamonds'] * 1 || 0
        ....
}
....
tick () {
....
 // change here
  this.autoSave()
}
autoSave () {
        if (this.cjs.Ticker.getTicks() % 100 === 0) {
          localStorage['city.coins'] = this.coins
          localStorage['city.diamond'] = this.diamonds
          localStorage['city.buildinglist'] =       JSON.stringify(this.buildingList)
        }

这里写图片描述

在init函数中,也就是页面刚刚被浏览器加载时,他会被调用,在初始化时,代码会先从localStorage对象中读取city.buildinglist字段,获得存储的建筑物信息,把这些信息转换为二进制后重新存储在数组buildingList 变量中。然后分别读取city.coins 和 city.diamonds字段,获得上次页面关闭时游戏存储的钱币数和钻石数,并把他们恢复到本次游戏进程中来。在tick函数中会调用autoSave函数,后者会判断,每过100个时间单位后,才会把当前数据存储到localStorage对象中。完成这部分代码后,我们可以尝试着关闭或刷新当前页面,当下次再次打开页面时,我们可以看到,页面上的情形与上一次关闭时是一模一样的。

在实现上述效果前,我们需要对代码做些许改动。当玩家点击右下角的Building按钮时,弹出来的选择面板,必须根据用户当前的钱币数和人口数来决定哪一种建筑物是可以建造的,我们以前的代码并没有考虑到这一点,先打开buildingpanelcomponent.vue进行相应的代码修改:

www.2527.com 25

大家在接收微信红包时,点开红包后就会看到有一个空心铜钱转了好几圈后才出现红包里面的金额,上面实现的就是铜钱转圈的特效。

setupBuildingButton用来实现控制面板上的按钮,它先为每个建筑物设计两种按钮,当建筑物可以建造时,采用代码中button对应的按钮对象,当建筑物不可以建造时采用代码中的disableButton对象。上面的代码完成后,每次点击Building按钮,控制面板显示出来的建筑物总会根据当前的资源条件来表明建筑物是否可以被玩家选择:

这里写图片描述

至此,游戏的设计进入到尾声阶段。最后我们要实现的是游戏数据的本地存储。我们这个游戏是一个较为消耗时间的过程,如果玩家玩到一半暂时不想玩了,那么他可以把页面关闭,下次打开页面上,页面上显示的情况要和上次关闭时一模一样,这就要求我们的游戏在页面关闭时,把各种数据,例如当前的页面上已经有的建筑物,游戏的钱币数,人口值等相关信息存储到本地,当下次页面开启时,将存储的数据再次读入页面,代码根据存储的数据把页面上次关闭时的情况再次重现出来。

export default {
    data () {
      return {
      ....
      buttonsMap: {}
      }
    },
   ....
   decideWhetherButtonDisableOrNot () {
        // 判断当前是否有足够的钱币,电量和人口去建造建筑物
        for (var i = 0; i < 3; i  ) {
          var b = this.buildings[i]
          var hasEnoughPowerSupplies = Constant[b.name].needPopulations === 0 || (this.gameSceneComponent.powerSupplies - this.gameSceneComponent.populations >= Constant[b.name].needPopulations)

          var hasEnoughCoins = (this.gameSceneComponent.coins >= Constant[b.name].needCoins)
          var button = this.buttonsMap[b.name].enableButton
          var buttonDisabled = this.buttonsMap[b.name].disableButton

          if (hasEnoughPowerSupplies && hasEnoughCoins) {
            button.visible = true
            buttonDisabled.visible = false
          } else {
            button.visible = false
            buttonDisabled.visible = true
          }
        }
      },
      setupBuildingButton (i) {
      ....
     // change here
      var buttons = {}
      var _this = this
      // change here
      buttons['enableButton'] = button
      ....
      // change here
      buttons['disableButton'] = buttonDisabled
      this.buttonsMap[b.name] = buttons
      }
}
TAG标签:
版权声明:本文由澳门新葡8455手机版发布于www.2527.com,转载请注明出处:www.2527.comiOS游戏开采尚未您想的那么难,实现微