在博主还是学生的时候,flappyBird这款游戏非常火爆,最后等到Android版的出来之后,也是很痴迷的玩了一把。可是,博主游戏天赋一直平平,几度玩得想摔手机。本文主要介绍如何开发iOS平台的flappyBird,游戏中使用了原本软件的图片资源,仅作学习交流使用。博主实现的flappyBird游戏包含游戏等级设定,排行榜,音效等功能。
技术点
flappyBird是单机游戏,主要涉及界面逻辑、图片资源、游戏动画、得分排行。
为了实现这几个功能,需要使用以下几个技术框架:
- AVFoundation
- 归档
- 模态视图
- NSTimer
- 视图控件,包括UIImageView、UILabel、UITableView等
实现过程
1、创建工程
1)打开Xcode,点击新建工程,选择Single View Application模板
2)填写工程信息
2、移除Main.storyboard文件
上图是flappyBird的文件目录,因为Xcode6使用模板创建工程时会自动生成Main.storyboard文件,而工程中本人使用代码布局,所以可以移除Main.storyboard文件。具体操作方法可以参看本人另一篇文章:
《iOS学习之移除Main.storyboard》
3、游戏界面布局
整体效果图如下
需要说明的是,Game Over这个界面,首先需要隐藏或者等到游戏结束才创建。本人是选择在游戏判定结束时才创建并显示。
4、游戏运行
这款游戏的两个关键点:
- 使用定时器驱动游戏界面运行,即游戏界面中的柱子高低变化与柱子的消失与产生。
- 游戏结束的判定,这里涉及两个问题,一是碰撞检测,二是计分统计。
具体实现部分代码
1、计分统计
1 2 3 4 5 6
| -(void)columnLabelClick { if (topPipeFrame.origin.x == (100 + 30 - 70)) { columnNumber++; columnLabel.text = [NSString stringWithFormat:@"%zi",columnNumber]; } }
|
2、绘制柱子
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
| -(void)pipe { //通道高度 NSInteger tunnelHeight = 0; //根据游戏难度设定通道高度 if([[DataTool stringForKey:kRateKey] isEqualToString:@"ordinary"]) { tunnelHeight = 100; }else if([[DataTool stringForKey:kRateKey] isEqualToString:@"general"]) { tunnelHeight = 90; }else if([[DataTool stringForKey:kRateKey] isEqualToString:@"difficult"]) { tunnelHeight = 80; }else if([[DataTool stringForKey:kRateKey] isEqualToString:@"hard"]) { tunnelHeight = 75; } else if([[DataTool stringForKey:kRateKey] isEqualToString:@"crazy"]) { tunnelHeight = 70; } //柱子图像 NSInteger tall = arc4random() % 200 + 40; topPipe = [[UIImageView alloc]initWithFrame:CGRectMake(320, -20, 70, tall)]; topPipe.image = [UIImage imageNamed:@"pipe"]; [self.view addSubview:topPipe];
bottomPipe = [[UIImageView alloc]initWithFrame:CGRectMake(320, tall + tunnelHeight, 70, 400)]; bottomPipe.image = [UIImage imageNamed:@"pipe"]; [self.view addSubview:bottomPipe]; //把底部图片视图放在柱子视图上面 [self.view insertSubview:roadView aboveSubview:bottomPipe]; }
|
3、使用定时器,驱动游戏界面运行,并进行碰撞检测
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
| //添加定时器 timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(onTimer) userInfo:nil repeats:YES]; //定时器操作 -(void)onTimer { //底部动画移动 CGRect frame = roadView.frame; if (frame.origin.x == -15) { frame.origin.x = 0; } frame.origin.x--; roadView.frame = frame; //上升 if (isTap == NO) { CGRect frame = birdsView.frame; frame.origin.y -= 3; number += 3; birdsView.frame = frame; if (number >= 60) { isTap = YES; } } //下降 if(isTap == YES && birdsView.frame.origin.y < 370){ CGRect frame = birdsView.frame; frame.origin.y++; number -= 2; birdsView.frame = frame; number = 0; } //柱子移动 topPipeFrame = topPipe.frame; CGRect bottomPipeFrame = bottomPipe.frame; topPipeFrame.origin.x--; bottomPipeFrame.origin.x--; topPipe.frame = topPipeFrame; bottomPipe.frame = bottomPipeFrame; if (topPipeFrame.origin.x < -70) { [self pipe]; } //碰撞检测(交集) bool topRet = CGRectIntersectsRect(birdsView.frame, topPipe.frame); bool bottomRet = CGRectIntersectsRect(birdsView.frame, bottomPipe.frame); if (topRet == true || bottomRet == true) { [self.soundTool playSoundByFileName:@"punch"]; [self onStop]; } if (topPipeFrame.origin.x == (100 + 30 - 70)) { [self.soundTool playSoundByFileName:@"pipe"]; [self columnLabelClick]; } }
|
4、更新分数,更新最佳分数与排行榜分数,并使用归档将数据持久化
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
| -(void)updateScore { //更新最佳成绩 if (columnNumber > [DataTool integerForKey:kBestScoreKey]) { [DataTool setInteger:columnNumber forKey:kBestScoreKey]; } //更新本局分数 [DataTool setInteger:columnNumber forKey:kCurrentScoreKey]; //更新排行榜 NSArray *ranks = (NSArray *)[DataTool objectForKey:kRankKey]; NSMutableArray *newRanksM = [NSMutableArray array]; NSInteger count = ranks.count; BOOL isUpdate = NO; for (NSInteger i = 0; i < count; i++) { NSString *scoreStr = ranks[i]; NSInteger score = [scoreStr integerValue]; if (score < columnNumber && isUpdate == NO) { scoreStr = [NSString stringWithFormat:@"%zi", columnNumber]; [newRanksM addObject:scoreStr]; isUpdate = YES; i--; } else { scoreStr = [NSString stringWithFormat:@"%zi", score]; [newRanksM addObject:scoreStr]; } } if (newRanksM.count > count) { [newRanksM removeLastObject]; } [DataTool setObject:newRanksM forKey:kRankKey]; }
|
5、绘制GameOver提示显示
1 2 3 4 5 6
| -(void)pullGameOver { //游戏结束操作界面 gameOver = [[GameOverView alloc] initWithFrame:CGRectMake(20, 160, 280, 300)]; gameOver.delegate = self; [self.view addSubview:gameOver]; }
|
6、游戏停止操作
1 2 3 4 5 6 7 8
| -(void)onStop { //更新分数 [self updateScore]; //停止定时器 [timer setFireDate:[NSDate distantFuture]]; //弹出游戏结束操作界面 [self pullGameOver]; }
|
小结
这款游戏的实现还是很简单的,主要使用UIImageView自带的动画实现方式,即可实现bird的动画效果。使用NSTimer即可实现游戏场景的柱子移动,至于柱子的高度,则可以使用随机数方式在一定范围内实现高低变化。最后可以使用CGRectIntersectsRect来实现边界碰撞检测来判定游戏是否结束。
以上是博主开发iOS版flappyBird的简要过程介绍,其中只包含了关键点的代码实现,具体完整游戏源代码地址:https://github.com/CharsDavy/flappyBird