Okay guys, today we’re going to cover SpriteKit, and we’re using FlappyBird as an example.

First we make the Game. Game Technology: Sprite Kit. Everything looks different OMG.

In SpriteKit, it’s like making a different app. Literally, everything you learned hitherto is worthless, except for Swift syntax. There are no UIViews, no nothing.

You have your AppDelegate.swift, which you (as always) shouldn’t touch. You have your GameScene.sks, which is *LIKE* a storyboard for SpriteKit, but we’re going to avoid it for the same reason that we avoid regular Storyboards. You have your GameScene.swift, which is where we’re going to design most of our game. And then you have GameViewController which, basically, takes your SpriteKit stuff and displays it in a view controller for you.

Let’s run the app. Cool. Hey, did you noticed something called nodes? Yes, these are objects of class SKNode. SKNode’s are basically anything interactable in your game. It could be an actual sprite. It could be an invisible wall. It could be the sky in the background. It could be your player. It’s anything. 

A derivative of SKNode is SKSpriteNode, which is basically an SKnode with a sprite attached to it.

SKLabelNodes are SKNodes with text. SKFieldNodes are SKNodes that represent intractable physics. So, this is like a gravity well, or an electric field, or something like that—a node which, in a local space, changes the physics.

So, in our game, what are our SKNodes? The bird, yes. Each of the pipes. The floor! The moving sprites that represent the floor, and the sky. The score label. Nice. These are all different nodes.

Let’s start with the skyline. Go straight to our GameScene.swift. Remove everything inside our functions. Let’s add, in our didMoveToView, the following:

    let numSkyNodes: CGFloat = 4
    let skyHeight: CGFloat = 65
    
    override func didMoveToView(view: SKView) {
        /* Setup your scene here */
        for i in 1...Int(self.numSkyNodes) {
            var sky = SKSpriteNode(imageNamed: "sky")
            sky.position = CGPointMake(CGFloat(i) * sky.size.width, self.skyHeight)
            sky.name = "sky"
            self.addChild(sky)
        }
    }

Whoa okay that doesn’t look like it’s 65 pixels off the ground. Why? Where does position refer to? The  center, in this case! We get to set the ANCHOR POINT, which is where the position refers to. It’s a point on the BOUNDS. Oh, also, guess what! Instead of top-left, it’s bottom right in SpriteKit. Good luck fuckers. So, if we set anchor point to CGPointZero, it sets it to the bottom left. Okay cool. So add that. We’ll probably need like, 4 or 5 skynodes.

Looks good. Now, let’s make it side scroll endlessly. Can anyone suggest how we do this?

Move them all to the left. When one goes fully off the screen to the left, move it to the right. Nice! We want to apply this same motion to all of our sky node copies. So, we can essentially use a nice function called enumerateChildNodesWIthName in order to do a *MAP* over all of the sky nodes. We do this in that update method.

    override func update(currentTime: CFTimeInterval) {
        self.enumerateChildNodesWithName("sky", usingBlock: { (node, _) -> Void in
            let sky = node as SKSpriteNode
            sky.position = CGPointMake(sky.position.x - 1, sky.position.y)
            
            if (sky.position.x <= -sky.size.width) {
                sky.position = CGPointMake(sky.position.x + sky.size.width * self.numSkyNodes,
                                           sky.position.y)
            }
        })
    }

Nice! That’s all. Now, how about the floor that goes under it? Well, it’s painfully similar code. We just use different constants

    let numLandNodes: CGFloat = 4
    let landHeight: CGFloat = -15
	let landScrollSpeed: CGFloat = 2

Cool! Let’s also set our back b9.’iiii
o

o.ground color while we’re here.

self.backgroundColor = SKColor(red: 81.0/255.0, green: 192.0/255.0, blue: 201.0/255.0, alpha: 1.0)

Now we’re looking like a real game! Now, let’s add a bird and make it flap! Now, we can use the update function to change the sprite of the bird. But, we want to show you guys everything we can. There are these things called SKActions, which are, basically, animations for SKNodes. The update is just a general function which is called all the time. But SKActions are animations that happen directly on SKNodes. Theoretically, we could have all given the infinite side scrolling sprites SKActions. But again, we want to show you everything. :)

So, let’s make a bird 

    let birdFlapRate = 0.2

        let birdTextures = [SKTexture(imageNamed: "bird-01"),
                            SKTexture(imageNamed: "bird-02")]
        let bird = SKSpriteNode(texture: birdTextures[0])
		bird.name = "bird"
        bird.position = CGPointMake(0.35 * self.frame.size.width,
                                    0.6 * self.frame.size.height)
        let flap = SKAction.animateWithTextures(birdTextures, timePerFrame: self.birdFlapRate)
        let flapForever = SKAction.repeatActionForever(flap)
        bird.runAction(flapForever)
        self.addChild(bird)

Well he looks underwhelming. Add

bird.setScale(2)

Cool! This makes the whole thing blurry though. Mostly because it will default try to mush the colors together when we raise the scale. Definitely not what we want, since Flappy Bird is *supposed* to look all pixelated. So, let’s apply a FILTERING to the textures before we use them. That way, we essentially force the sprite to keep its pixelated look.

        for texture in birdTextures {
            texture.filteringMode = .Nearest
        }

Looking good! Now, let’s add physics!
At the top, we say:
self.physicsWorld.gravity = CGVectorMake(0, -10)

For our bird, we say:

        bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.size.height / 2.0)
        bird.physicsBody?.dynamic = true
        bird.physicsBody?.allowsRotation = false

Okay. So gravity affects our bird now. We want him to bounce on the floor. So, how can we do that? We could modify the floor sprites. Or we could make an invisible node to just handle it for us. The latter is easier.

        let ground = SKNode()
        ground.position = CGPointMake(0, skyHeight/2)
        ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, skyHeight))
        ground.physicsBody?.dynamic = false
        self.addChild(ground)

Cool. Let’s add touches.

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        let bird = self.childNodeWithName("bird")
        bird?.physicsBody?.velocity = CGVectorMake(0, 0)
        bird?.physicsBody?.applyImpulse(CGVectorMake(0, 40))
    }

Nothing too complicated so far. This is all really reasonable. Now, I would imagine that you guys can figure out how to make pipes if you sat and thought about it. It’s not introducing any new features about SpriteKit, so we won’t bother with talking about the pipes. Instead, we’re just going to copy a function that does it for us. You’re welcome to read through the function and figure out how it all works. But, it doesn’t add any new concepts.

Let also rotate the bird. In our update function: 

let bird = self.childNodeWithName("bird")
bird?.zRotation = bird!.physicsBody!.velocity.dy * 0.0015

Now we’re looking like a reasonable game! Now, let’s wrap it up with some collision checking. We have physics, but do we *really* know when a collision occurs? No. Well, SpriteKit has a built in a way of handling that. The physics body actually is the guy you talk to for that. First you have to make yourself the contact delegate. 

self.physicsWorld.contactDelegate = self

    let birdCategory: UInt32 = 1 << 0
    let worldCategory: UInt32 = 1 << 1
    let pipeCategory: UInt32 = 1 << 2

        bird.physicsBody?.categoryBitMask = self.birdCategory
        bird.physicsBody?.collisionBitMask = self.worldCategory | self.pipeCategory
        bird.physicsBody?.contactTestBitMask = self.worldCategory | self.pipeCategory

        ground.physicsBody?.categoryBitMask = self.worldCategory

        pipes.physicsBody?.categoryBitMask = self.pipeCategory

    func didBeginContact(contact: SKPhysicsContact) {
        if (contact.bodyA.categoryBitMask & (self.worldCategory | self.pipeCategory) != 0 ||
            contact.bodyB.categoryBitMask & (self.worldCategory | self.pipeCategory) != 0) {
            self.removeAllChildren()
            self.removeAllActions()
            self.setupScene()
        }
    }

And now you’re all done! You have a rudimentary Flappy Bird.

Leave early.