Flappy Bird 源码走读
参考:https://github.com/kirualex/SprityBird
该项目基于spritekit,代码的结构很清楚,感觉用来学习spritekit非常不错。
1、项目只有一个viewController,包含:
@property (weak,nonatomic) IBOutlet SKView * gameView; @property (weak,nonatomic) IBOutlet UIView * getReadyView; @property (weak,nonatomic) IBOutlet UIView * gameOverView; @property (weak,nonatomic) IBOutlet UIImageView * medalImageView; @property (weak,nonatomic) IBOutlet UILabel * currentScore; @property (weak,nonatomic) IBOutlet UILabel * bestScoreLabel;
初始时gameOverView均不可见。
2、在viewController的viewDidLoad中加载场景:
[self.gameView presentScene:scene];
3、这里做的比较好的是,利用协议完成viewController和Scene的交互,viewController处理游戏中反馈的事件,实现对上述游戏完成、游戏开始等顶层view的操作。
@protocol SceneDelegate <NSObject> - (void) eventStart; - (void) eventPlay; - (void) eventWasted; @end
4、在Scene的初始化中对场景中的障碍和鸟进行绘制:
- (void) startGame { // Reinit wasted = NO; [self removeAllChildren]; [self createBackground]; [self createFloor]; [self createScore]; [self createObstacles]; [self createBird]; // Floor needs to be in front of tubes floor.zPosition = bird.zPosition + 1; if([self.delegate respondsToSelector:@selector(eventStart)]){ [self.delegate eventStart]; } }
5、在项目里,只定义了两类node,一个是用来表述移动背景如地板、障碍、背景图等,另外一个就是绘制小鸟;
这里初始化的node对象包括鸟、障碍都会被保存在全局数据组里,在后续刷新。以障碍物的移动为例:
- (void) updateObstacles:(NSTimeInterval)currentTime { if(!bird.physicsBody){ return; } for(int i=0;i<nbObstacles;i++){ // Get pipes bby pairs SKSpriteNode * topPipe = (SKSpriteNode *) topPipes[i]; SKSpriteNode * bottomPipe = (SKSpriteNode *) bottomPipes[i]; // Check if pair has exited screen, and place them upfront again if (X(topPipe) < -WIDTH(topPipe)){ SKSpriteNode * mostRightPipe = (SKSpriteNode *) topPipes[(i+(nbObstacles-1))%nbObstacles]; [self place:bottomPipe and:topPipe atX:X(mostRightPipe)+WIDTH(topPipe)+OBSTACLE_INTERVAL_SPACE]; } // Move according to the scrolling speed topPipe.position = CGPointMake(X(topPipe) - FLOOR_SCROLLING_SPEED, Y(topPipe)); bottomPipe.position = CGPointMake(X(bottomPipe) - FLOOR_SCROLLING_SPEED, Y(bottomPipe)); } }
6、在scene中主要继承处理两个方法:
//该函数被Scene重绘每一帧调用 - (void)update:(NSTimeInterval)currentTime - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
7、通过sprite的物理引擎实现碰撞操作:
bottomPipe.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0,0, WIDTH(bottomPipe) , HEIGHT(bottomPipe))]
topPipe.physicsBody.categoryBitMask = blockBitMask;
topPipe.physicsBody.contactTestBitMask = birdBitMask;
- (void)didBeginContact:(SKPhysicsContact *)contact { if(wasted){ return; } wasted = true; [Score registerScore:self.score]; if([self.delegate respondsToSelector:@selector(eventWasted)]){ [self.delegate eventWasted]; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)