How to Clone Flappy Bird in Swift

Giordano Scalzo
Giordano ScalzoSoftware Craftsman at Effective Code Ltd
How To Clone 
FlappyBird 
in 
Swift
A little introduction
Who Am I? 
@giordanoscalzo 
https://github.com/gscalzo
Who Am I? 
@giordanoscalzo 
https://github.com/gscalzo 
A developer
Who Am I? 
@giordanoscalzo 
https://github.com/gscalzo 
An iOS developer
Who Am I? 
@giordanoscalzo 
https://github.com/gscalzo 
A Swift beginner
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in Swift
How to implement Hello World in Swift?
println("Hello, World!")
println("Hello, World!") 
Not exciting :-(
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in Swift
far to be perfect
but it was fun
instructions
git clone 
http://github.com/gscalzo/FlappySwift.git
./setup
./setup 1 
./setup 2 
./setup 3 
...
How to Clone Flappy Bird in Swift
walking skeleton 
./setup 1
How to Clone Flappy Bird in Swift
class GameScene: SKScene { 
private var screenNode: SKSpriteNode! 
override func didMoveToView(view: SKView) { 
// ... 
}
override func didMoveToView(view: SKView) { 
screenNode = SKSpriteNode(color: UIColor.clearColor(), size: self.size) 
addChild(screenNode) 
let backgroundNode = SKSpriteNode(imageNamed: "background") 
backgroundNode.anchorPoint = CGPointZero 
backgroundNode.position = CGPointZero 
screenNode.addChild(backgroundNode) 
let groundNode = SKSpriteNode(imageNamed: "ground") 
groundNode.anchorPoint = CGPointZero 
groundNode.position = CGPointZero 
screenNode.addChild(groundNode) 
}
How to Clone Flappy Bird in Swift
parallax layers 
./setup 2
override func didMoveToView(view: SKView) { 
//... 
Background(textureNamed: "background").addTo(screenNode).start() 
Ground(textureNamed: "ground").addTo(screenNode).start() 
}
protocol Startable { 
func start() -> Startable 
func stop() -> Startable 
}
class Background { 
private var parallaxNode: ParallaxNode! 
private let textureName: String 
init(textureNamed textureName: String) { 
} 
func addTo(parentNode: SKSpriteNode!) -> Background { 
return self 
} 
}
init(textureNamed textureName: String) { 
self.textureName = textureName 
} 
func addTo(parentNode: SKSpriteNode!) -> Background { 
let width = parentNode.size.width 
let height = parentNode.size.height 
parallaxNode = ParallaxNode(width: width, 
height: height, 
textureNamed: textureName).addTo(parentNode) 
return self 
}
extension Background : Startable { 
func start() -> Startable { 
parallaxNode.start(duration: 20.0) 
return self 
} 
func stop() -> Startable { 
parallaxNode.stop() 
return self 
} 
}
class Ground { 
private var parallaxNode: ParallaxNode! 
private let textureName: String 
init(textureNamed textureName: String) { 
} 
func addTo(parentNode: SKSpriteNode!) -> Ground { 
return self 
} 
}
init(textureNamed textureName: String) { 
self.textureName = textureName 
} 
func addTo(parentNode: SKSpriteNode!) -> Ground { 
let width = parentNode.size.width 
let height = CGFloat(60.0) 
parallaxNode = ParallaxNode(width: width, 
height: height, 
textureNamed: textureName).zPosition(5).addTo(parentNode) 
return self 
}
extension Ground : Startable { 
func start() -> Startable { 
parallaxNode.start(duration: 5.0) 
return self 
} 
func stop() -> Startable { 
parallaxNode.stop() 
return self 
} 
}
How to implement ParallaxNode?
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in Swift
class ParallaxNode { 
private let node: SKSpriteNode! 
init(width: CGFloat, height: CGFloat, textureNamed: String) { 
} 
private func createNode(textureNamed: String, x: CGFloat) -> SKNode { 
} 
func zPosition(zPosition: CGFloat) -> ParallaxNode { 
} 
func addTo(parentNode: SKSpriteNode) -> ParallaxNode { 
} 
func start(#duration: NSTimeInterval) { 
} 
func stop() { 
} 
}
init(width: CGFloat, height: CGFloat, textureNamed: String) { 
let size = CGSizeMake(2*width, height) 
node = SKSpriteNode(color: UIColor.whiteColor(), size: size) 
node.anchorPoint = CGPointZero 
node.position = CGPointZero 
node.addChild(createNode(textureNamed, x: 0)) 
node.addChild(createNode(textureNamed, x: width)) 
}
private func createNode(textureNamed: String, x: CGFloat) -> SKNode { 
let node = SKSpriteNode(imageNamed: textureNamed, normalMapped: true) 
node.anchorPoint = CGPointZero 
node.position = CGPoint(x: x, y: 0) 
return node 
}
func zPosition(zPosition: CGFloat) -> ParallaxNode { 
node.zPosition = zPosition 
return self 
} 
func addTo(parentNode: SKSpriteNode) -> ParallaxNode { 
parentNode.addChild(node) 
return self 
} 
func stop() { 
node.removeAllActions() 
}
func start(#duration: NSTimeInterval) { 
node.runAction(SKAction.repeatActionForever(SKAction.sequence( 
[ 
SKAction.moveToX(-node.size.width/2.0, duration: duration), 
SKAction.moveToX(0, duration: 0) 
] 
))) 
}
How to Clone Flappy Bird in Swift
Our hero 
./setup 3
override func didMoveToView(view: SKView) { 
physicsWorld.gravity = CGVector(dx: 0, dy: -3) 
//... 
bird = Bird(textureNames: ["bird1", "bird2"]).addTo(screenNode) 
bird.position = CGPointMake(30.0, 400.0) 
bird.start() 
}
class Bird { 
private let node: SKSpriteNode! 
private let textureNames: [String] 
var position : CGPoint { 
set { node.position = newValue } 
get { return node.position } 
} 
init(textureNames: [String]) { 
self.textureNames = textureNames 
node = createNode() 
} 
func addTo(scene: SKSpriteNode) -> Bird{ 
scene.addChild(node) 
return self 
} 
}
extension Bird { 
private func createNode() -> SKSpriteNode { 
let birdNode = SKSpriteNode(imageNamed: textureNames.first!) 
birdNode.setScale(1.8) 
birdNode.zPosition = 2.0 
birdNode.physicsBody = SKPhysicsBody(rectangleOfSize: birdNode.size) 
birdNode.physicsBody!.dynamic = true 
return birdNode 
} 
}
extension Bird : Startable { 
func start() -> Startable { 
animate() 
return self 
} 
func stop() -> Startable { 
node.physicsBody!.dynamic = false 
node.removeAllActions() 
return self 
} 
}
private func animate(){ 
let animationFrames = textureNames.map { texName in 
SKTexture(imageNamed: texName) 
} 
node.runAction( 
SKAction.repeatActionForever( 
SKAction.animateWithTextures(animationFrames, timePerFrame: 0.1) 
)) 
}
func update() { 
switch node.physicsBody!.velocity.dy { 
case let dy where dy > 30.0: 
node.zRotation = (3.14/6.0) 
case let dy where dy < -100.0: 
node.zRotation = -1*(3.14/4.0) 
default: 
node.zRotation = 0.0 
} 
}
Bird Physics 101
How to Clone Flappy Bird in Swift
Impulse
How to Clone Flappy Bird in Swift
func flap() { 
node.physicsBody!.velocity = CGVector(dx: 0, dy: 0) 
node.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 6)) 
}
How to Clone Flappy Bird in Swift
Pipes!
Pipes! 
./setup 4
How to Clone Flappy Bird in Swift
class GameScene: SKScene { 
override func didMoveToView(view: SKView) { 
//... 
Pipes(textureNames: ["pipeTop.png", "pipeBottom.png"]).addTo(screenNode).start() 
}
class Pipes { 
// class let createActionKey = "createActionKey" // class variables not yet supported 
private class var createActionKey : String { get {return "createActionKey"} } 
private var parentNode: SKSpriteNode! 
private let textureNames: [String] 
init(textureNames: [String]) { 
self.textureNames = textureNames 
} 
func addTo(parentNode: SKSpriteNode) -> Pipes { 
self.parentNode = parentNode 
return self 
} 
}
func start() -> Startable { 
let createAction = SKAction.repeatActionForever( 
SKAction.sequence( 
[ 
SKAction.runBlock { 
self.createNewPipe() 
}, 
SKAction.waitForDuration(3) 
] 
) ) 
parentNode.runAction(createAction, withKey: Pipes.createActionKey) 
return self 
}
func stop() -> Startable { 
parentNode.removeActionForKey(Pipes.createActionKey) 
let pipeNodes = parentNode.children.filter { (node: AnyObject?) -> Bool in 
(node as SKNode).name == PipePair.kind 
} 
for pipe in pipeNodes { 
pipe.removeAllActions() 
} 
return self 
}
private func createNewPipe() { 
PipePair(textures: textureNames, centerY: centerPipes()).addTo(parentNode).start() 
} 
private func centerPipes() -> CGFloat { 
return parentNode.size.height/2 - 100 + 20 * CGFloat(arc4random_uniform(10)) 
}
class PipePair { 
// class let kind = "PIPES" // class variables not yet supported 
class var kind : String { get {return "PIPES"} } 
private let gapSize: CGFloat = 50 
private var pipesNode: SKNode! 
private var finalOffset: CGFloat! 
private var startingOffset: CGFloat!
init(textures: [String], centerY: CGFloat){ 
pipesNode = SKNode() 
pipesNode.name = PipePair.kind 
let pipeTop = createPipe(imageNamed: textures[0]) 
let pipeTopPosition = CGPoint(x: 0, y: centerY + pipeTop.size.height/2 + gapSize) 
pipeTop.position = pipeTopPosition 
pipesNode.addChild(pipeTop) 
let pipeBottom = createPipe(imageNamed: textures[1]) 
let pipeBottomPosition = CGPoint(x: 0 , y: centerY - pipeBottom.size.height/2 - gapSize) 
pipeBottom.position = pipeBottomPosition 
pipesNode.addChild(pipeBottom) 
let gapNode = createGap(size: CGSize( 
width: pipeBottom.size.width, 
height: gapSize*2)) 
gapNode.position = CGPoint(x: 0, y: centerY) 
pipesNode.addChild(gapNode) 
finalOffset = -pipeBottom.size.width/2 
startingOffset = -finalOffset 
}
func start() { 
pipesNode.runAction(SKAction.sequence( 
[ 
SKAction.moveToX(finalOffset, duration: 6.0), 
SKAction.removeFromParent() 
] 
)) 
}
private func createPipe(#imageNamed: String) -> SKSpriteNode { 
let pipeNode = SKSpriteNode(imageNamed: imageNamed) 
return pipeNode 
} 
private func createGap(#size: CGSize) -> SKSpriteNode { 
let gapNode = SKSpriteNode(color: UIColor.clearColor(), 
size: size) 
return gapNode 
}
How to Clone Flappy Bird in Swift
Contact 
./setup 5
enum BodyType : UInt32 { 
case bird = 1 // (1 << 0) 
case ground = 2 // (1 << 1) 
case pipe = 4 // (1 << 2) 
case gap = 8 // (1 << 3) 
} 
class GameScene: SKScene {
extension Bird { 
private func createNode() -> SKSpriteNode { 
//... 
birdNode.physicsBody = SKPhysicsBody(rectangleOfSize: birdNode.size) 
birdNode.physicsBody?.dynamic = true 
birdNode.physicsBody?.categoryBitMask = BodyType.bird.toRaw() 
birdNode.physicsBody?.collisionBitMask = BodyType.bird.toRaw() 
birdNode.physicsBody?.contactTestBitMask = BodyType.world.toRaw() | 
BodyType.pipe.toRaw() | 
BodyType.gap.toRaw()
Or better, using a builder closure... 
extension Bird { 
private func createNode() -> SKSpriteNode { 
//... 
birdNode.physicsBody = SKPhysicsBody.rectSize(birdNode.size) { 
body in 
body.dynamic = true 
body.categoryBitMask = BodyType.bird.toRaw() 
body.collisionBitMask = BodyType.bird.toRaw() 
body.contactTestBitMask = BodyType.world.toRaw() | 
BodyType.pipe.toRaw() | 
BodyType.gap.toRaw() 
}
Handy builder closure extension 
extension SKPhysicsBody { 
typealias BodyBuilderClosure = (SKPhysicsBody) -> () 
class func rectSize(size: CGSize, 
builderClosure: BodyBuilderClosure) -> SKPhysicsBody { 
let body = SKPhysicsBody(rectangleOfSize: size) 
builderClosure(body) 
return body 
} 
}
class Ground { 
func addTo(parentNode: SKSpriteNode!) -> Ground { 
//... 
groundBody.physicsBody = SKPhysicsBody.rectSize(size) { body in 
body.dynamic = false 
body.affectedByGravity = false 
body.categoryBitMask = BodyType.ground.toRaw() 
body.collisionBitMask = BodyType.ground.toRaw() 
}
extension PipePair { 
private func createPipe(#imageNamed: String) -> SKSpriteNode { 
pipeNode.physicsBody = SKPhysicsBody.rectSize(pipeNode.size) { 
body in 
body.dynamic = false 
body.affectedByGravity = false 
body.categoryBitMask = BodyType.pipe.toRaw() 
body.collisionBitMask = BodyType.pipe.toRaw() 
} 
} 
private func createGap(#size: CGSize) -> SKSpriteNode { 
gapNode.physicsBody = SKPhysicsBody.rectSize(size) { 
body in 
body.dynamic = false 
body.affectedByGravity = false 
body.categoryBitMask = BodyType.gap.toRaw() 
body.collisionBitMask = BodyType.gap.toRaw() 
} 
} 
}
extension GameScene: SKPhysicsContactDelegate { 
func didBeginContact(contact: SKPhysicsContact!) { 
} 
func didEndContact(contact: SKPhysicsContact!) { 
} 
}
func didBeginContact(contact: SKPhysicsContact!) { 
let contactMask = contact.bodyA.categoryBitMask | 
contact.bodyB.categoryBitMask 
switch (contactMask) { 
case BodyType.pipe.toRaw() | BodyType.bird.toRaw(): 
log("Contact with a pipe") 
case BodyType.ground.toRaw() | BodyType.bird.toRaw(): 
log("Contact with ground") 
default: 
return 
} 
}
func didEndContact(contact: SKPhysicsContact!) { 
let contactMask = contact.bodyA.categoryBitMask | 
contact.bodyB.categoryBitMask 
switch (contactMask) { 
case BodyType.gap.toRaw() | BodyType.bird.toRaw(): 
log("Exit from gap") 
default: 
return 
} 
}
How to Clone Flappy Bird in Swift
Final touches 
./setup 6
class GameScene: SKScene { 
private var actors: [Startable]! 
private var score: Score!
override func didMoveToView(view: SKView) { 
//... 
let bg = Background(textureNamed: "background").addTo(screenNode) 
let gr = Ground(textureNamed: "ground").addTo(screenNode) 
bird = Bird(textureNames: ["bird1", "bird2"]).addTo(screenNode) 
bird.position = CGPointMake(30.0, 400.0) 
let pi = Pipes(textureNames: ["pipeTop.png", "pipeBottom.png"]).addTo(screenNode) 
actors = [bg, gr, pi, bird] 
score = Score().addTo(screenNode) 
for actor in actors { 
actor.start() 
} 
}
class Score { 
private var score: SKLabelNode! 
private var currentScore = 0 
func addTo(parentNode: SKSpriteNode) -> Score { 
score = SKLabelNode(text: "(currentScore)") 
score.fontName = "MarkerFelt-Wide" 
score.fontSize = 30 
score.position = CGPoint(x: parentNode.size.width/2, 
y: parentNode.size.height - 40) 
parentNode.addChild(score) 
return self 
} 
func increase() { 
currentScore += 1 
score.text = "(currentScore)" 
} 
}
func didBeginContact(contact: SKPhysicsContact!) { 
//... 
case BodyType.pipe.toRaw() | BodyType.bird.toRaw(): 
bird.pushDown() 
case BodyType.ground.toRaw() | BodyType.bird.toRaw(): 
for actor in actors { 
actor.stop() 
} 
let shakeAction = SKAction.shake(0.1, amplitudeX: 20) 
screenNode.runAction(shakeAction)
func didEndContact(contact: SKPhysicsContact!) { 
//... 
case BodyType.gap.toRaw() | BodyType.bird.toRaw(): 
score.increase()
extension Bird { 
func flap() { 
if !dying { 
node.physicsBody!.velocity = CGVector(dx: 0, dy: 0) 
node.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 6)) 
} 
} 
func pushDown() { 
dying = true 
node.physicsBody!.applyImpulse(CGVector(dx: 0, dy: -10)) 
}
How to Clone Flappy Bird in Swift
Into the Cave 
./setup 7
override func didMoveToView(view: SKView) { 
let textures = Textures.cave() 
let bg = Background(textureNamed: textures.background).addTo(screenNode) 
let te = Ground(textureNamed: textures.ground).addTo(screenNode) 
bird = Bird(textureNames: textures.bird).addTo(screenNode) 
let pi = Pipes(textureNames: textures.pipes).addTo(screenNode)
struct Textures { 
let background: String 
let ground: String 
let pipes: [String] 
let bird: [String] 
static func classic() -> Textures { 
return Textures( 
background: "background.png", 
ground: "ground.png", 
pipes: ["pipeTop.png", "pipeBottom.png"], 
bird: ["bird1", "bird2"]) 
} 
static func cave() -> Textures { 
return Textures( 
background: "cave_background.png", 
ground: "cave_ground.png", 
pipes: ["cave_pipeTop.png", "cave_pipeBottom.png"], 
bird: ["bird1", "bird2"]) 
} 
}
extension Bird { 
private func addLightEmitter() { 
let light = SKLightNode() 
light.categoryBitMask = BodyType.bird.toRaw() 
light.falloff = 1 
light.ambientColor = UIColor.whiteColor() 
light.lightColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5) 
light.shadowColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5) 
node.addChild(light) 
} 
}
extension PipePair { 
private func createPipe(#imageNamed: String) -> SKSpriteNode { 
let pipeNode = SKSpriteNode(imageNamed: imageNamed) 
pipeNode.shadowCastBitMask = BodyType.bird.toRaw() 
pipeNode.physicsBody = SKPhysicsBody.rectSize(pipeNode.size) {
private func createNode(textureNamed: String, x: CGFloat) -> SKNode { 
let node = SKSpriteNode(imageNamed: textureNamed, normalMapped: true) 
node.lightingBitMask = BodyType.bird.toRaw()
What if 
Michael Bay 
was the designer? 
./setup 8
How to Clone Flappy Bird in Swift
Explosions!
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { 
switch touches.count { 
case 1: bird.flap() 
default: shoot() 
} 
}
extension GameScene { 
private func shoot(#emitterName: String, finalYPosition: CGFloat) { 
let fireBoltEmmitter = SKEmitterNode.emitterNodeWithName(emitterName) 
fireBoltEmmitter.position = bird.position 
fireBoltEmmitter.physicsBody = SKPhysicsBody.rectSize(CGSize(width: 20, height: 20)) { 
body in 
body.dynamic = true 
body.categoryBitMask = BodyType.bomb.toRaw() 
body.collisionBitMask = BodyType.bomb.toRaw() 
body.contactTestBitMask = BodyType.pipe.toRaw() 
} 
screenNode.addChild(fireBoltEmmitter) 
fireBoltEmmitter.runAction(SKAction.sequence( 
[ 
SKAction.moveByX(500, y: 100, duration: 1), 
SKAction.removeFromParent() 
])) 
} 
private func shoot() { 
shoot(emitterName: "fireBolt", finalYPosition: 1000) 
}
How to Clone Flappy Bird in Swift
Game menu
Leaderboard
Swifterims!
Please: Fork and PRs 
github.com/gscalzo/FlappySwift
credits 
SKLightNode tutorial 
http://www.ymc.ch/en/playing-with-ios-8-sprite-kit-and-sklightnode 
Assets for Cave version 
http://www.free-pobo.com
Thank you!
Questions?
How to Clone Flappy Bird in Swift
1 of 109

Recommended

Better Software: introduction to good code by
Better Software: introduction to good codeBetter Software: introduction to good code
Better Software: introduction to good codeGiordano Scalzo
2.1K views133 slides
The Macronomicon by
The MacronomiconThe Macronomicon
The MacronomiconMike Fogus
7.3K views131 slides
Naïveté vs. Experience by
Naïveté vs. ExperienceNaïveté vs. Experience
Naïveté vs. ExperienceMike Fogus
7K views87 slides
Developing iOS apps with Swift by
Developing iOS apps with SwiftDeveloping iOS apps with Swift
Developing iOS apps with SwiftNew Generation Applications
7K views22 slides
JavaScript Survival Guide by
JavaScript Survival GuideJavaScript Survival Guide
JavaScript Survival GuideGiordano Scalzo
6K views143 slides
EcmaScript 6 by
EcmaScript 6 EcmaScript 6
EcmaScript 6 Manoj Kumar
1.7K views118 slides

More Related Content

What's hot

ECMAScript 6 by
ECMAScript 6ECMAScript 6
ECMAScript 6Piotr Lewandowski
1K views42 slides
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin by
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinFabio Collini
901 views42 slides
ES6 in Real Life by
ES6 in Real LifeES6 in Real Life
ES6 in Real LifeDomenic Denicola
5.5K views21 slides
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan by
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFabio Collini
469 views93 slides
Groovy by
GroovyGroovy
GroovyZen Urban
1.1K views35 slides
Groovy grails types, operators, objects by
Groovy grails types, operators, objectsGroovy grails types, operators, objects
Groovy grails types, operators, objectsHusain Dalal
458 views51 slides

What's hot(20)

Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin by Fabio Collini
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Fabio Collini901 views
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan by Fabio Collini
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
Fabio Collini469 views
Groovy by Zen Urban
GroovyGroovy
Groovy
Zen Urban1.1K views
Groovy grails types, operators, objects by Husain Dalal
Groovy grails types, operators, objectsGroovy grails types, operators, objects
Groovy grails types, operators, objects
Husain Dalal458 views
JavaScript - i och utanför webbläsaren (2010-03-03) by Anders Jönsson
JavaScript - i och utanför webbläsaren (2010-03-03)JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)
Anders Jönsson414 views
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy by Fabio Collini
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
Fabio Collini780 views
ES2015 (ES6) Overview by hesher
ES2015 (ES6) OverviewES2015 (ES6) Overview
ES2015 (ES6) Overview
hesher22.4K views
An Intro To ES6 by FITC
An Intro To ES6An Intro To ES6
An Intro To ES6
FITC1.6K views
Explaining ES6: JavaScript History and What is to Come by Cory Forsyth
Explaining ES6: JavaScript History and What is to ComeExplaining ES6: JavaScript History and What is to Come
Explaining ES6: JavaScript History and What is to Come
Cory Forsyth1.7K views
Building fast interpreters in Rust by Ingvar Stepanyan
Building fast interpreters in RustBuilding fast interpreters in Rust
Building fast interpreters in Rust
Ingvar Stepanyan4.7K views
Generics and Inference by Richard Fox
Generics and InferenceGenerics and Inference
Generics and Inference
Richard Fox173 views
A Few Interesting Things in Apple's Swift Programming Language by SmartLogic
A Few Interesting Things in Apple's Swift Programming LanguageA Few Interesting Things in Apple's Swift Programming Language
A Few Interesting Things in Apple's Swift Programming Language
SmartLogic5.2K views
Fertile Ground: The Roots of Clojure by Mike Fogus
Fertile Ground: The Roots of ClojureFertile Ground: The Roots of Clojure
Fertile Ground: The Roots of Clojure
Mike Fogus6.6K views

Viewers also liked

10 minutes of me: Giordano Scalzo's Visual Resume by
10 minutes of me: Giordano Scalzo's Visual Resume10 minutes of me: Giordano Scalzo's Visual Resume
10 minutes of me: Giordano Scalzo's Visual ResumeGiordano Scalzo
292.2K views56 slides
The Joy of ServerSide Swift Development by
The Joy  of ServerSide Swift DevelopmentThe Joy  of ServerSide Swift Development
The Joy of ServerSide Swift DevelopmentGiordano Scalzo
1.1K views190 slides
Carl Pullein Resume 2.0 by
Carl Pullein Resume 2.0Carl Pullein Resume 2.0
Carl Pullein Resume 2.0Fandcorp English Solutions
306K views25 slides
JavaScript Patterns by
JavaScript PatternsJavaScript Patterns
JavaScript PatternsGiordano Scalzo
3K views139 slides
Stunning Roses by
Stunning RosesStunning Roses
Stunning RosesThilini
2.4K views26 slides
(BAREFOOTJOURNAL.COM) The 15 Ultimate Places to Chill in 2015 by
(BAREFOOTJOURNAL.COM)  The 15 Ultimate Places to Chill in 2015(BAREFOOTJOURNAL.COM)  The 15 Ultimate Places to Chill in 2015
(BAREFOOTJOURNAL.COM) The 15 Ultimate Places to Chill in 2015barefootjournal
33.7K views21 slides

Viewers also liked(20)

10 minutes of me: Giordano Scalzo's Visual Resume by Giordano Scalzo
10 minutes of me: Giordano Scalzo's Visual Resume10 minutes of me: Giordano Scalzo's Visual Resume
10 minutes of me: Giordano Scalzo's Visual Resume
Giordano Scalzo292.2K views
The Joy of ServerSide Swift Development by Giordano Scalzo
The Joy  of ServerSide Swift DevelopmentThe Joy  of ServerSide Swift Development
The Joy of ServerSide Swift Development
Giordano Scalzo1.1K views
Stunning Roses by Thilini
Stunning RosesStunning Roses
Stunning Roses
Thilini2.4K views
(BAREFOOTJOURNAL.COM) The 15 Ultimate Places to Chill in 2015 by barefootjournal
(BAREFOOTJOURNAL.COM)  The 15 Ultimate Places to Chill in 2015(BAREFOOTJOURNAL.COM)  The 15 Ultimate Places to Chill in 2015
(BAREFOOTJOURNAL.COM) The 15 Ultimate Places to Chill in 2015
barefootjournal33.7K views
The Most Controversial Magazine Covers by guimera
The Most Controversial Magazine CoversThe Most Controversial Magazine Covers
The Most Controversial Magazine Covers
guimera 46K views
Ngam can ho voi nhung duong cong goi cam by Xu Pi
Ngam can ho voi nhung duong cong goi camNgam can ho voi nhung duong cong goi cam
Ngam can ho voi nhung duong cong goi cam
Xu Pi403 views
Best of the 2014 US Open by guimera
Best of the 2014 US OpenBest of the 2014 US Open
Best of the 2014 US Open
guimera 21.1K views
Rethinking Resumes by Karla Wiles
Rethinking ResumesRethinking Resumes
Rethinking Resumes
Karla Wiles256.5K views
Six Things You Need to Know About the Modern Call Center by Spoken Communications
Six Things You Need to Know About the Modern Call CenterSix Things You Need to Know About the Modern Call Center
Six Things You Need to Know About the Modern Call Center
Spoken Communications14.1K views
Beautiful Fuchsia Queen by Thilini
Beautiful Fuchsia QueenBeautiful Fuchsia Queen
Beautiful Fuchsia Queen
Thilini2.6K views
White Space Creativity - Frontend Conference CH 2014 by Denise Jacobs
White Space Creativity - Frontend Conference CH 2014White Space Creativity - Frontend Conference CH 2014
White Space Creativity - Frontend Conference CH 2014
Denise Jacobs46.7K views
My Visual Resume - Saad Ahmed Shaikh by Saad Ahmed Shaikh
My Visual Resume - Saad Ahmed ShaikhMy Visual Resume - Saad Ahmed Shaikh
My Visual Resume - Saad Ahmed Shaikh
Saad Ahmed Shaikh398.1K views
How I landed a job with Slideshare by Emiland
How I landed a job with SlideshareHow I landed a job with Slideshare
How I landed a job with Slideshare
Emiland 516.8K views

Similar to How to Clone Flappy Bird in Swift

JJUG CCC 2011 Spring by
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 SpringKiyotaka Oku
1.7K views85 slides
Greach, GroovyFx Workshop by
Greach, GroovyFx WorkshopGreach, GroovyFx Workshop
Greach, GroovyFx WorkshopDierk König
309 views37 slides
Griffon @ Svwjug by
Griffon @ SvwjugGriffon @ Svwjug
Griffon @ SvwjugAndres Almiray
854 views39 slides
JavaFX 2.0 With Alternative Languages - JavaOne 2011 by
JavaFX 2.0 With Alternative Languages - JavaOne 2011JavaFX 2.0 With Alternative Languages - JavaOne 2011
JavaFX 2.0 With Alternative Languages - JavaOne 2011Stephen Chin
1.7K views84 slides
HTML5 - Daha Flash bir web? by
HTML5 - Daha Flash bir web?HTML5 - Daha Flash bir web?
HTML5 - Daha Flash bir web?Ankara JUG
755 views68 slides
A More Flash Like Web? by
A More Flash Like Web?A More Flash Like Web?
A More Flash Like Web?Murat Can ALPAY
1.3K views67 slides

Similar to How to Clone Flappy Bird in Swift(20)

JJUG CCC 2011 Spring by Kiyotaka Oku
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
Kiyotaka Oku1.7K views
Greach, GroovyFx Workshop by Dierk König
Greach, GroovyFx WorkshopGreach, GroovyFx Workshop
Greach, GroovyFx Workshop
Dierk König309 views
JavaFX 2.0 With Alternative Languages - JavaOne 2011 by Stephen Chin
JavaFX 2.0 With Alternative Languages - JavaOne 2011JavaFX 2.0 With Alternative Languages - JavaOne 2011
JavaFX 2.0 With Alternative Languages - JavaOne 2011
Stephen Chin1.7K views
HTML5 - Daha Flash bir web? by Ankara JUG
HTML5 - Daha Flash bir web?HTML5 - Daha Flash bir web?
HTML5 - Daha Flash bir web?
Ankara JUG755 views
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte... by DroidConTLV
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
DroidConTLV463 views
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf by ssuserb6c2641
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfCompose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
ssuserb6c264137 views
Swift - One step forward from Obj-C by Nissan Tsafrir
Swift -  One step forward from Obj-CSwift -  One step forward from Obj-C
Swift - One step forward from Obj-C
Nissan Tsafrir2.3K views
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V... by Stephen Chin
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
Stephen Chin2.6K views
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・- by Tsuyoshi Yamamoto
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Tsuyoshi Yamamoto1.4K views
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン by Tsuyoshi Yamamoto
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
Tsuyoshi Yamamoto1.8K views
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V... by Stephen Chin
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
Stephen Chin3.7K views
Os Secoske by oscon2007
Os SecoskeOs Secoske
Os Secoske
oscon2007381 views
Swift 3 Programming for iOS : Protocol by Kwang Woo NAM
Swift 3 Programming for iOS : ProtocolSwift 3 Programming for iOS : Protocol
Swift 3 Programming for iOS : Protocol
Kwang Woo NAM286 views

More from Giordano Scalzo

The Joy Of Server Side Swift Development by
The Joy Of Server Side Swift DevelopmentThe Joy Of Server Side Swift Development
The Joy Of Server Side Swift DevelopmentGiordano Scalzo
1.2K views190 slides
A swift introduction to Swift by
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to SwiftGiordano Scalzo
104.8K views84 slides
Tame Accidental Complexity with Ruby and MongoMapper by
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperGiordano Scalzo
8.7K views61 slides
Code kata by
Code kataCode kata
Code kataGiordano Scalzo
3.3K views55 slides
Tdd iPhone For Dummies by
Tdd iPhone For DummiesTdd iPhone For Dummies
Tdd iPhone For DummiesGiordano Scalzo
1.8K views82 slides
Better Software Developers by
Better Software DevelopersBetter Software Developers
Better Software DevelopersGiordano Scalzo
1.8K views54 slides

More from Giordano Scalzo(10)

The Joy Of Server Side Swift Development by Giordano Scalzo
The Joy Of Server Side Swift DevelopmentThe Joy Of Server Side Swift Development
The Joy Of Server Side Swift Development
Giordano Scalzo1.2K views
A swift introduction to Swift by Giordano Scalzo
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to Swift
Giordano Scalzo104.8K views
Tame Accidental Complexity with Ruby and MongoMapper by Giordano Scalzo
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapper
Giordano Scalzo8.7K views
XpUg Coding Dojo: KataYahtzee in Ocp way by Giordano Scalzo
XpUg Coding Dojo: KataYahtzee in Ocp wayXpUg Coding Dojo: KataYahtzee in Ocp way
XpUg Coding Dojo: KataYahtzee in Ocp way
Giordano Scalzo5.5K views
Bdd: Tdd and beyond the infinite by Giordano Scalzo
Bdd: Tdd and beyond the infiniteBdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infinite
Giordano Scalzo32.8K views

How to Clone Flappy Bird in Swift

  • 1. How To Clone FlappyBird in Swift
  • 3. Who Am I? @giordanoscalzo https://github.com/gscalzo
  • 4. Who Am I? @giordanoscalzo https://github.com/gscalzo A developer
  • 5. Who Am I? @giordanoscalzo https://github.com/gscalzo An iOS developer
  • 6. Who Am I? @giordanoscalzo https://github.com/gscalzo A Swift beginner
  • 9. How to implement Hello World in Swift?
  • 14. far to be perfect
  • 15. but it was fun
  • 19. ./setup 1 ./setup 2 ./setup 3 ...
  • 23. class GameScene: SKScene { private var screenNode: SKSpriteNode! override func didMoveToView(view: SKView) { // ... }
  • 24. override func didMoveToView(view: SKView) { screenNode = SKSpriteNode(color: UIColor.clearColor(), size: self.size) addChild(screenNode) let backgroundNode = SKSpriteNode(imageNamed: "background") backgroundNode.anchorPoint = CGPointZero backgroundNode.position = CGPointZero screenNode.addChild(backgroundNode) let groundNode = SKSpriteNode(imageNamed: "ground") groundNode.anchorPoint = CGPointZero groundNode.position = CGPointZero screenNode.addChild(groundNode) }
  • 27. override func didMoveToView(view: SKView) { //... Background(textureNamed: "background").addTo(screenNode).start() Ground(textureNamed: "ground").addTo(screenNode).start() }
  • 28. protocol Startable { func start() -> Startable func stop() -> Startable }
  • 29. class Background { private var parallaxNode: ParallaxNode! private let textureName: String init(textureNamed textureName: String) { } func addTo(parentNode: SKSpriteNode!) -> Background { return self } }
  • 30. init(textureNamed textureName: String) { self.textureName = textureName } func addTo(parentNode: SKSpriteNode!) -> Background { let width = parentNode.size.width let height = parentNode.size.height parallaxNode = ParallaxNode(width: width, height: height, textureNamed: textureName).addTo(parentNode) return self }
  • 31. extension Background : Startable { func start() -> Startable { parallaxNode.start(duration: 20.0) return self } func stop() -> Startable { parallaxNode.stop() return self } }
  • 32. class Ground { private var parallaxNode: ParallaxNode! private let textureName: String init(textureNamed textureName: String) { } func addTo(parentNode: SKSpriteNode!) -> Ground { return self } }
  • 33. init(textureNamed textureName: String) { self.textureName = textureName } func addTo(parentNode: SKSpriteNode!) -> Ground { let width = parentNode.size.width let height = CGFloat(60.0) parallaxNode = ParallaxNode(width: width, height: height, textureNamed: textureName).zPosition(5).addTo(parentNode) return self }
  • 34. extension Ground : Startable { func start() -> Startable { parallaxNode.start(duration: 5.0) return self } func stop() -> Startable { parallaxNode.stop() return self } }
  • 35. How to implement ParallaxNode?
  • 39. class ParallaxNode { private let node: SKSpriteNode! init(width: CGFloat, height: CGFloat, textureNamed: String) { } private func createNode(textureNamed: String, x: CGFloat) -> SKNode { } func zPosition(zPosition: CGFloat) -> ParallaxNode { } func addTo(parentNode: SKSpriteNode) -> ParallaxNode { } func start(#duration: NSTimeInterval) { } func stop() { } }
  • 40. init(width: CGFloat, height: CGFloat, textureNamed: String) { let size = CGSizeMake(2*width, height) node = SKSpriteNode(color: UIColor.whiteColor(), size: size) node.anchorPoint = CGPointZero node.position = CGPointZero node.addChild(createNode(textureNamed, x: 0)) node.addChild(createNode(textureNamed, x: width)) }
  • 41. private func createNode(textureNamed: String, x: CGFloat) -> SKNode { let node = SKSpriteNode(imageNamed: textureNamed, normalMapped: true) node.anchorPoint = CGPointZero node.position = CGPoint(x: x, y: 0) return node }
  • 42. func zPosition(zPosition: CGFloat) -> ParallaxNode { node.zPosition = zPosition return self } func addTo(parentNode: SKSpriteNode) -> ParallaxNode { parentNode.addChild(node) return self } func stop() { node.removeAllActions() }
  • 43. func start(#duration: NSTimeInterval) { node.runAction(SKAction.repeatActionForever(SKAction.sequence( [ SKAction.moveToX(-node.size.width/2.0, duration: duration), SKAction.moveToX(0, duration: 0) ] ))) }
  • 46. override func didMoveToView(view: SKView) { physicsWorld.gravity = CGVector(dx: 0, dy: -3) //... bird = Bird(textureNames: ["bird1", "bird2"]).addTo(screenNode) bird.position = CGPointMake(30.0, 400.0) bird.start() }
  • 47. class Bird { private let node: SKSpriteNode! private let textureNames: [String] var position : CGPoint { set { node.position = newValue } get { return node.position } } init(textureNames: [String]) { self.textureNames = textureNames node = createNode() } func addTo(scene: SKSpriteNode) -> Bird{ scene.addChild(node) return self } }
  • 48. extension Bird { private func createNode() -> SKSpriteNode { let birdNode = SKSpriteNode(imageNamed: textureNames.first!) birdNode.setScale(1.8) birdNode.zPosition = 2.0 birdNode.physicsBody = SKPhysicsBody(rectangleOfSize: birdNode.size) birdNode.physicsBody!.dynamic = true return birdNode } }
  • 49. extension Bird : Startable { func start() -> Startable { animate() return self } func stop() -> Startable { node.physicsBody!.dynamic = false node.removeAllActions() return self } }
  • 50. private func animate(){ let animationFrames = textureNames.map { texName in SKTexture(imageNamed: texName) } node.runAction( SKAction.repeatActionForever( SKAction.animateWithTextures(animationFrames, timePerFrame: 0.1) )) }
  • 51. func update() { switch node.physicsBody!.velocity.dy { case let dy where dy > 30.0: node.zRotation = (3.14/6.0) case let dy where dy < -100.0: node.zRotation = -1*(3.14/4.0) default: node.zRotation = 0.0 } }
  • 56. func flap() { node.physicsBody!.velocity = CGVector(dx: 0, dy: 0) node.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 6)) }
  • 61. class GameScene: SKScene { override func didMoveToView(view: SKView) { //... Pipes(textureNames: ["pipeTop.png", "pipeBottom.png"]).addTo(screenNode).start() }
  • 62. class Pipes { // class let createActionKey = "createActionKey" // class variables not yet supported private class var createActionKey : String { get {return "createActionKey"} } private var parentNode: SKSpriteNode! private let textureNames: [String] init(textureNames: [String]) { self.textureNames = textureNames } func addTo(parentNode: SKSpriteNode) -> Pipes { self.parentNode = parentNode return self } }
  • 63. func start() -> Startable { let createAction = SKAction.repeatActionForever( SKAction.sequence( [ SKAction.runBlock { self.createNewPipe() }, SKAction.waitForDuration(3) ] ) ) parentNode.runAction(createAction, withKey: Pipes.createActionKey) return self }
  • 64. func stop() -> Startable { parentNode.removeActionForKey(Pipes.createActionKey) let pipeNodes = parentNode.children.filter { (node: AnyObject?) -> Bool in (node as SKNode).name == PipePair.kind } for pipe in pipeNodes { pipe.removeAllActions() } return self }
  • 65. private func createNewPipe() { PipePair(textures: textureNames, centerY: centerPipes()).addTo(parentNode).start() } private func centerPipes() -> CGFloat { return parentNode.size.height/2 - 100 + 20 * CGFloat(arc4random_uniform(10)) }
  • 66. class PipePair { // class let kind = "PIPES" // class variables not yet supported class var kind : String { get {return "PIPES"} } private let gapSize: CGFloat = 50 private var pipesNode: SKNode! private var finalOffset: CGFloat! private var startingOffset: CGFloat!
  • 67. init(textures: [String], centerY: CGFloat){ pipesNode = SKNode() pipesNode.name = PipePair.kind let pipeTop = createPipe(imageNamed: textures[0]) let pipeTopPosition = CGPoint(x: 0, y: centerY + pipeTop.size.height/2 + gapSize) pipeTop.position = pipeTopPosition pipesNode.addChild(pipeTop) let pipeBottom = createPipe(imageNamed: textures[1]) let pipeBottomPosition = CGPoint(x: 0 , y: centerY - pipeBottom.size.height/2 - gapSize) pipeBottom.position = pipeBottomPosition pipesNode.addChild(pipeBottom) let gapNode = createGap(size: CGSize( width: pipeBottom.size.width, height: gapSize*2)) gapNode.position = CGPoint(x: 0, y: centerY) pipesNode.addChild(gapNode) finalOffset = -pipeBottom.size.width/2 startingOffset = -finalOffset }
  • 68. func start() { pipesNode.runAction(SKAction.sequence( [ SKAction.moveToX(finalOffset, duration: 6.0), SKAction.removeFromParent() ] )) }
  • 69. private func createPipe(#imageNamed: String) -> SKSpriteNode { let pipeNode = SKSpriteNode(imageNamed: imageNamed) return pipeNode } private func createGap(#size: CGSize) -> SKSpriteNode { let gapNode = SKSpriteNode(color: UIColor.clearColor(), size: size) return gapNode }
  • 72. enum BodyType : UInt32 { case bird = 1 // (1 << 0) case ground = 2 // (1 << 1) case pipe = 4 // (1 << 2) case gap = 8 // (1 << 3) } class GameScene: SKScene {
  • 73. extension Bird { private func createNode() -> SKSpriteNode { //... birdNode.physicsBody = SKPhysicsBody(rectangleOfSize: birdNode.size) birdNode.physicsBody?.dynamic = true birdNode.physicsBody?.categoryBitMask = BodyType.bird.toRaw() birdNode.physicsBody?.collisionBitMask = BodyType.bird.toRaw() birdNode.physicsBody?.contactTestBitMask = BodyType.world.toRaw() | BodyType.pipe.toRaw() | BodyType.gap.toRaw()
  • 74. Or better, using a builder closure... extension Bird { private func createNode() -> SKSpriteNode { //... birdNode.physicsBody = SKPhysicsBody.rectSize(birdNode.size) { body in body.dynamic = true body.categoryBitMask = BodyType.bird.toRaw() body.collisionBitMask = BodyType.bird.toRaw() body.contactTestBitMask = BodyType.world.toRaw() | BodyType.pipe.toRaw() | BodyType.gap.toRaw() }
  • 75. Handy builder closure extension extension SKPhysicsBody { typealias BodyBuilderClosure = (SKPhysicsBody) -> () class func rectSize(size: CGSize, builderClosure: BodyBuilderClosure) -> SKPhysicsBody { let body = SKPhysicsBody(rectangleOfSize: size) builderClosure(body) return body } }
  • 76. class Ground { func addTo(parentNode: SKSpriteNode!) -> Ground { //... groundBody.physicsBody = SKPhysicsBody.rectSize(size) { body in body.dynamic = false body.affectedByGravity = false body.categoryBitMask = BodyType.ground.toRaw() body.collisionBitMask = BodyType.ground.toRaw() }
  • 77. extension PipePair { private func createPipe(#imageNamed: String) -> SKSpriteNode { pipeNode.physicsBody = SKPhysicsBody.rectSize(pipeNode.size) { body in body.dynamic = false body.affectedByGravity = false body.categoryBitMask = BodyType.pipe.toRaw() body.collisionBitMask = BodyType.pipe.toRaw() } } private func createGap(#size: CGSize) -> SKSpriteNode { gapNode.physicsBody = SKPhysicsBody.rectSize(size) { body in body.dynamic = false body.affectedByGravity = false body.categoryBitMask = BodyType.gap.toRaw() body.collisionBitMask = BodyType.gap.toRaw() } } }
  • 78. extension GameScene: SKPhysicsContactDelegate { func didBeginContact(contact: SKPhysicsContact!) { } func didEndContact(contact: SKPhysicsContact!) { } }
  • 79. func didBeginContact(contact: SKPhysicsContact!) { let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask switch (contactMask) { case BodyType.pipe.toRaw() | BodyType.bird.toRaw(): log("Contact with a pipe") case BodyType.ground.toRaw() | BodyType.bird.toRaw(): log("Contact with ground") default: return } }
  • 80. func didEndContact(contact: SKPhysicsContact!) { let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask switch (contactMask) { case BodyType.gap.toRaw() | BodyType.bird.toRaw(): log("Exit from gap") default: return } }
  • 83. class GameScene: SKScene { private var actors: [Startable]! private var score: Score!
  • 84. override func didMoveToView(view: SKView) { //... let bg = Background(textureNamed: "background").addTo(screenNode) let gr = Ground(textureNamed: "ground").addTo(screenNode) bird = Bird(textureNames: ["bird1", "bird2"]).addTo(screenNode) bird.position = CGPointMake(30.0, 400.0) let pi = Pipes(textureNames: ["pipeTop.png", "pipeBottom.png"]).addTo(screenNode) actors = [bg, gr, pi, bird] score = Score().addTo(screenNode) for actor in actors { actor.start() } }
  • 85. class Score { private var score: SKLabelNode! private var currentScore = 0 func addTo(parentNode: SKSpriteNode) -> Score { score = SKLabelNode(text: "(currentScore)") score.fontName = "MarkerFelt-Wide" score.fontSize = 30 score.position = CGPoint(x: parentNode.size.width/2, y: parentNode.size.height - 40) parentNode.addChild(score) return self } func increase() { currentScore += 1 score.text = "(currentScore)" } }
  • 86. func didBeginContact(contact: SKPhysicsContact!) { //... case BodyType.pipe.toRaw() | BodyType.bird.toRaw(): bird.pushDown() case BodyType.ground.toRaw() | BodyType.bird.toRaw(): for actor in actors { actor.stop() } let shakeAction = SKAction.shake(0.1, amplitudeX: 20) screenNode.runAction(shakeAction)
  • 87. func didEndContact(contact: SKPhysicsContact!) { //... case BodyType.gap.toRaw() | BodyType.bird.toRaw(): score.increase()
  • 88. extension Bird { func flap() { if !dying { node.physicsBody!.velocity = CGVector(dx: 0, dy: 0) node.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 6)) } } func pushDown() { dying = true node.physicsBody!.applyImpulse(CGVector(dx: 0, dy: -10)) }
  • 90. Into the Cave ./setup 7
  • 91. override func didMoveToView(view: SKView) { let textures = Textures.cave() let bg = Background(textureNamed: textures.background).addTo(screenNode) let te = Ground(textureNamed: textures.ground).addTo(screenNode) bird = Bird(textureNames: textures.bird).addTo(screenNode) let pi = Pipes(textureNames: textures.pipes).addTo(screenNode)
  • 92. struct Textures { let background: String let ground: String let pipes: [String] let bird: [String] static func classic() -> Textures { return Textures( background: "background.png", ground: "ground.png", pipes: ["pipeTop.png", "pipeBottom.png"], bird: ["bird1", "bird2"]) } static func cave() -> Textures { return Textures( background: "cave_background.png", ground: "cave_ground.png", pipes: ["cave_pipeTop.png", "cave_pipeBottom.png"], bird: ["bird1", "bird2"]) } }
  • 93. extension Bird { private func addLightEmitter() { let light = SKLightNode() light.categoryBitMask = BodyType.bird.toRaw() light.falloff = 1 light.ambientColor = UIColor.whiteColor() light.lightColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.5) light.shadowColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5) node.addChild(light) } }
  • 94. extension PipePair { private func createPipe(#imageNamed: String) -> SKSpriteNode { let pipeNode = SKSpriteNode(imageNamed: imageNamed) pipeNode.shadowCastBitMask = BodyType.bird.toRaw() pipeNode.physicsBody = SKPhysicsBody.rectSize(pipeNode.size) {
  • 95. private func createNode(textureNamed: String, x: CGFloat) -> SKNode { let node = SKSpriteNode(imageNamed: textureNamed, normalMapped: true) node.lightingBitMask = BodyType.bird.toRaw()
  • 96. What if Michael Bay was the designer? ./setup 8
  • 99. override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { switch touches.count { case 1: bird.flap() default: shoot() } }
  • 100. extension GameScene { private func shoot(#emitterName: String, finalYPosition: CGFloat) { let fireBoltEmmitter = SKEmitterNode.emitterNodeWithName(emitterName) fireBoltEmmitter.position = bird.position fireBoltEmmitter.physicsBody = SKPhysicsBody.rectSize(CGSize(width: 20, height: 20)) { body in body.dynamic = true body.categoryBitMask = BodyType.bomb.toRaw() body.collisionBitMask = BodyType.bomb.toRaw() body.contactTestBitMask = BodyType.pipe.toRaw() } screenNode.addChild(fireBoltEmmitter) fireBoltEmmitter.runAction(SKAction.sequence( [ SKAction.moveByX(500, y: 100, duration: 1), SKAction.removeFromParent() ])) } private func shoot() { shoot(emitterName: "fireBolt", finalYPosition: 1000) }
  • 105. Please: Fork and PRs github.com/gscalzo/FlappySwift
  • 106. credits SKLightNode tutorial http://www.ymc.ch/en/playing-with-ios-8-sprite-kit-and-sklightnode Assets for Cave version http://www.free-pobo.com