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];
    }
}
原文地址:https://www.cnblogs.com/Fredric-2013/p/5442063.html