Flappy Bird 源码走读

时间:2022-03-17 17:00:20

参考: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 + ; if([self.delegate respondsToSelector:@selector(eventStart)]){
[self.delegate eventStart];
}
}

5、在项目里,只定义了两类node,一个是用来表述移动背景如地板、障碍、背景图等,另外一个就是绘制小鸟;

这里初始化的node对象包括鸟、障碍都会被保存在全局数据组里,在后续刷新。以障碍物的移动为例:

- (void) updateObstacles:(NSTimeInterval)currentTime
{
if(!bird.physicsBody){
return;
} for(int i=;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-))%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];
}
}