SlideShare una empresa de Scribd logo
1 de 109
Descargar para leer sin conexión
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 implement Hello World in Swift?
println("Hello, World!")
println("Hello, World!") 
Not exciting :-(
far to be perfect
but it was fun
instructions
git clone 
http://github.com/gscalzo/FlappySwift.git
./setup
./setup 1 
./setup 2 
./setup 3 
...
walking skeleton 
./setup 1
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) 
}
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?
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) 
] 
))) 
}
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
Impulse
func flap() { 
node.physicsBody!.velocity = CGVector(dx: 0, dy: 0) 
node.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 6)) 
}
Pipes!
Pipes! 
./setup 4
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 
}
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 
} 
}
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)) 
}
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
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) 
}
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

Más contenido relacionado

La actualidad más candente

Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
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
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
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
 
Groovy grails types, operators, objects
Groovy grails types, operators, objectsGroovy grails types, operators, objects
Groovy grails types, operators, objectsHusain Dalal
 
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)
JavaScript - i och utanför webbläsaren (2010-03-03)Anders Jönsson
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
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 italyFabio Collini
 
EcmaScript 6 - The future is here
EcmaScript 6 - The future is hereEcmaScript 6 - The future is here
EcmaScript 6 - The future is hereSebastiano Armeli
 
ES2015 (ES6) Overview
ES2015 (ES6) OverviewES2015 (ES6) Overview
ES2015 (ES6) Overviewhesher
 
An Intro To ES6
An Intro To ES6An Intro To ES6
An Intro To ES6FITC
 
Explaining ES6: JavaScript History and What is to Come
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 ComeCory Forsyth
 
Building fast interpreters in Rust
Building fast interpreters in RustBuilding fast interpreters in Rust
Building fast interpreters in RustIngvar Stepanyan
 
Generics and Inference
Generics and InferenceGenerics and Inference
Generics and InferenceRichard Fox
 
A Few Interesting Things in Apple's Swift Programming Language
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 LanguageSmartLogic
 
Fertile Ground: The Roots of Clojure
Fertile Ground: The Roots of ClojureFertile Ground: The Roots of Clojure
Fertile Ground: The Roots of ClojureMike Fogus
 

La actualidad más candente (20)

ECMAScript 6
ECMAScript 6ECMAScript 6
ECMAScript 6
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
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
 
ES6 in Real Life
ES6 in Real LifeES6 in Real Life
ES6 in Real Life
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
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
 
Groovy
GroovyGroovy
Groovy
 
Groovy grails types, operators, objects
Groovy grails types, operators, objectsGroovy grails types, operators, objects
Groovy grails types, operators, objects
 
Pooya Khaloo Presentation on IWMC 2015
Pooya Khaloo Presentation on IWMC 2015Pooya Khaloo Presentation on IWMC 2015
Pooya Khaloo Presentation on IWMC 2015
 
はじめてのGroovy
はじめてのGroovyはじめてのGroovy
はじめてのGroovy
 
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)
JavaScript - i och utanför webbläsaren (2010-03-03)
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
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
 
EcmaScript 6 - The future is here
EcmaScript 6 - The future is hereEcmaScript 6 - The future is here
EcmaScript 6 - The future is here
 
ES2015 (ES6) Overview
ES2015 (ES6) OverviewES2015 (ES6) Overview
ES2015 (ES6) Overview
 
An Intro To ES6
An Intro To ES6An Intro To ES6
An Intro To ES6
 
Explaining ES6: JavaScript History and What is to Come
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
 
Building fast interpreters in Rust
Building fast interpreters in RustBuilding fast interpreters in Rust
Building fast interpreters in Rust
 
Generics and Inference
Generics and InferenceGenerics and Inference
Generics and Inference
 
A Few Interesting Things in Apple's Swift Programming Language
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
 
SDC - Einführung in Scala
SDC - Einführung in ScalaSDC - Einführung in Scala
SDC - Einführung in Scala
 
Say It With Javascript
Say It With JavascriptSay It With Javascript
Say It With Javascript
 
Fertile Ground: The Roots of Clojure
Fertile Ground: The Roots of ClojureFertile Ground: The Roots of Clojure
Fertile Ground: The Roots of Clojure
 

Destacado

10 minutes of me: Giordano Scalzo's Visual Resume
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
 
The Joy of ServerSide Swift Development
The Joy  of ServerSide Swift DevelopmentThe Joy  of ServerSide Swift Development
The Joy of ServerSide Swift DevelopmentGiordano Scalzo
 
Stunning Roses
Stunning RosesStunning Roses
Stunning RosesThilini
 
(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
(BAREFOOTJOURNAL.COM) The 15 Ultimate Places to Chill in 2015barefootjournal
 
Gold Medal Teamwork with Mary Whipple
Gold Medal Teamwork with Mary WhippleGold Medal Teamwork with Mary Whipple
Gold Medal Teamwork with Mary WhippleSpoken Communications
 
The Most Controversial Magazine Covers
The Most Controversial Magazine CoversThe Most Controversial Magazine Covers
The Most Controversial Magazine Coversguimera
 
Ngam can ho voi nhung duong cong goi cam
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 camXu Pi
 
Best of the 2014 US Open
Best of the 2014 US OpenBest of the 2014 US Open
Best of the 2014 US Openguimera
 
Rethinking Resumes
Rethinking ResumesRethinking Resumes
Rethinking ResumesKarla Wiles
 
Six Things You Need to Know About the Modern Call Center
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 CenterSpoken Communications
 
Beautiful Fuchsia Queen
Beautiful Fuchsia QueenBeautiful Fuchsia Queen
Beautiful Fuchsia QueenThilini
 
White Space Creativity - Frontend Conference CH 2014
White Space Creativity - Frontend Conference CH 2014White Space Creativity - Frontend Conference CH 2014
White Space Creativity - Frontend Conference CH 2014Denise Jacobs
 
My Visual Resume - Saad Ahmed Shaikh
My Visual Resume - Saad Ahmed ShaikhMy Visual Resume - Saad Ahmed Shaikh
My Visual Resume - Saad Ahmed ShaikhSaad Ahmed Shaikh
 
How I landed a job with Slideshare
How I landed a job with SlideshareHow I landed a job with Slideshare
How I landed a job with SlideshareEmiland
 

Destacado (20)

10 minutes of me: Giordano Scalzo's Visual Resume
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
 
The Joy of ServerSide Swift Development
The Joy  of ServerSide Swift DevelopmentThe Joy  of ServerSide Swift Development
The Joy of ServerSide Swift Development
 
Carl Pullein Resume 2.0
Carl Pullein Resume 2.0Carl Pullein Resume 2.0
Carl Pullein Resume 2.0
 
JavaScript Patterns
JavaScript PatternsJavaScript Patterns
JavaScript Patterns
 
Stunning Roses
Stunning RosesStunning Roses
Stunning Roses
 
(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
(BAREFOOTJOURNAL.COM) The 15 Ultimate Places to Chill in 2015
 
Gold Medal Teamwork with Mary Whipple
Gold Medal Teamwork with Mary WhippleGold Medal Teamwork with Mary Whipple
Gold Medal Teamwork with Mary Whipple
 
The Most Controversial Magazine Covers
The Most Controversial Magazine CoversThe Most Controversial Magazine Covers
The Most Controversial Magazine Covers
 
Ngam can ho voi nhung duong cong goi cam
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
 
Best of the 2014 US Open
Best of the 2014 US OpenBest of the 2014 US Open
Best of the 2014 US Open
 
UX designer visual resume
UX designer visual resumeUX designer visual resume
UX designer visual resume
 
Dancing
DancingDancing
Dancing
 
Rethinking Resumes
Rethinking ResumesRethinking Resumes
Rethinking Resumes
 
Six Things You Need to Know About the Modern Call Center
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
 
Beautiful Fuchsia Queen
Beautiful Fuchsia QueenBeautiful Fuchsia Queen
Beautiful Fuchsia Queen
 
White Space Creativity - Frontend Conference CH 2014
White Space Creativity - Frontend Conference CH 2014White Space Creativity - Frontend Conference CH 2014
White Space Creativity - Frontend Conference CH 2014
 
My Visual Resume - Saad Ahmed Shaikh
My Visual Resume - Saad Ahmed ShaikhMy Visual Resume - Saad Ahmed Shaikh
My Visual Resume - Saad Ahmed Shaikh
 
Visual resume
Visual resumeVisual resume
Visual resume
 
Social Media Resume
Social Media ResumeSocial Media Resume
Social Media Resume
 
How I landed a job with Slideshare
How I landed a job with SlideshareHow I landed a job with Slideshare
How I landed a job with Slideshare
 

Similar a How to Clone Flappy Bird in Swift

JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 SpringKiyotaka Oku
 
Greach, GroovyFx Workshop
Greach, GroovyFx WorkshopGreach, GroovyFx Workshop
Greach, GroovyFx WorkshopDierk König
 
JavaFX 2.0 With Alternative Languages - JavaOne 2011
JavaFX 2.0 With Alternative Languages - JavaOne 2011JavaFX 2.0 With Alternative Languages - JavaOne 2011
JavaFX 2.0 With Alternative Languages - JavaOne 2011Stephen Chin
 
HTML5 - Daha Flash bir web?
HTML5 - Daha Flash bir web?HTML5 - Daha Flash bir web?
HTML5 - Daha Flash bir web?Ankara JUG
 
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...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...DroidConTLV
 
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfCompose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfssuserb6c2641
 
Swift - One step forward from Obj-C
Swift -  One step forward from Obj-CSwift -  One step forward from Obj-C
Swift - One step forward from Obj-CNissan Tsafrir
 
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...
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...Stephen Chin
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Tsuyoshi Yamamoto
 
Testing a 2D Platformer with Spock
Testing a 2D Platformer with SpockTesting a 2D Platformer with Spock
Testing a 2D Platformer with SpockAlexander Tarlinder
 
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、GaelykでハンズオンTsuyoshi Yamamoto
 
The definitive guide to java agents
The definitive guide to java agentsThe definitive guide to java agents
The definitive guide to java agentsRafael Winterhalter
 
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...
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...Stephen Chin
 
Swift 3 Programming for iOS : Protocol
Swift 3 Programming for iOS : ProtocolSwift 3 Programming for iOS : Protocol
Swift 3 Programming for iOS : ProtocolKwang Woo NAM
 

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

JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
 
Greach, GroovyFx Workshop
Greach, GroovyFx WorkshopGreach, GroovyFx Workshop
Greach, GroovyFx Workshop
 
Griffon @ Svwjug
Griffon @ SvwjugGriffon @ Svwjug
Griffon @ Svwjug
 
JavaFX 2.0 With Alternative Languages - JavaOne 2011
JavaFX 2.0 With Alternative Languages - JavaOne 2011JavaFX 2.0 With Alternative Languages - JavaOne 2011
JavaFX 2.0 With Alternative Languages - JavaOne 2011
 
HTML5 - Daha Flash bir web?
HTML5 - Daha Flash bir web?HTML5 - Daha Flash bir web?
HTML5 - Daha Flash bir web?
 
A More Flash Like Web?
A More Flash Like Web?A More Flash Like Web?
A More Flash Like Web?
 
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...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
 
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdfCompose로 Android:Desktop 멀티플랫폼 만들기.pdf
Compose로 Android:Desktop 멀티플랫폼 만들기.pdf
 
Swift - One step forward from Obj-C
Swift -  One step forward from Obj-CSwift -  One step forward from Obj-C
Swift - One step forward from Obj-C
 
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...
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
 
Server Side Swift: Vapor
Server Side Swift: VaporServer Side Swift: Vapor
Server Side Swift: Vapor
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
 
Testing a 2D Platformer with Spock
Testing a 2D Platformer with SpockTesting a 2D Platformer with Spock
Testing a 2D Platformer with Spock
 
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
 
The definitive guide to java agents
The definitive guide to java agentsThe definitive guide to java agents
The definitive guide to java agents
 
ES6 Overview
ES6 OverviewES6 Overview
ES6 Overview
 
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...
JavaFX 2.0 With Alternative Languages - Groovy, Clojure, Scala, Fantom, and V...
 
Canvas al ajillo
Canvas al ajilloCanvas al ajillo
Canvas al ajillo
 
Os Secoske
Os SecoskeOs Secoske
Os Secoske
 
Swift 3 Programming for iOS : Protocol
Swift 3 Programming for iOS : ProtocolSwift 3 Programming for iOS : Protocol
Swift 3 Programming for iOS : Protocol
 

Más de Giordano Scalzo

The Joy Of Server Side Swift Development
The Joy Of Server Side Swift DevelopmentThe Joy Of Server Side Swift Development
The Joy Of Server Side Swift DevelopmentGiordano Scalzo
 
A swift introduction to Swift
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to SwiftGiordano Scalzo
 
Tame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperGiordano Scalzo
 
Better Software Developers
Better Software DevelopersBetter Software Developers
Better Software DevelopersGiordano Scalzo
 
Agile Iphone Development
Agile Iphone DevelopmentAgile Iphone Development
Agile Iphone DevelopmentGiordano Scalzo
 
XpUg Coding Dojo: KataYahtzee in Ocp way
XpUg Coding Dojo: KataYahtzee in Ocp wayXpUg Coding Dojo: KataYahtzee in Ocp way
XpUg Coding Dojo: KataYahtzee in Ocp wayGiordano Scalzo
 
Bdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infiniteBdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infiniteGiordano Scalzo
 

Más de Giordano Scalzo (10)

The Joy Of Server Side Swift Development
The Joy Of Server Side Swift DevelopmentThe Joy Of Server Side Swift Development
The Joy Of Server Side Swift Development
 
A swift introduction to Swift
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to Swift
 
Tame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapper
 
Code kata
Code kataCode kata
Code kata
 
Tdd iPhone For Dummies
Tdd iPhone For DummiesTdd iPhone For Dummies
Tdd iPhone For Dummies
 
Better Software Developers
Better Software DevelopersBetter Software Developers
Better Software Developers
 
Agile Iphone Development
Agile Iphone DevelopmentAgile Iphone Development
Agile Iphone Development
 
XpUg Coding Dojo: KataYahtzee in Ocp way
XpUg Coding Dojo: KataYahtzee in Ocp wayXpUg Coding Dojo: KataYahtzee in Ocp way
XpUg Coding Dojo: KataYahtzee in Ocp way
 
Bdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infiniteBdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infinite
 
Scrum in an hour
Scrum in an hourScrum in an hour
Scrum in an hour
 

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
  • 7.
  • 8.
  • 9. How to implement Hello World in Swift?
  • 12.
  • 13.
  • 14. far to be perfect
  • 15. but it was fun
  • 19. ./setup 1 ./setup 2 ./setup 3 ...
  • 20.
  • 22.
  • 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) }
  • 25.
  • 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?
  • 36.
  • 37.
  • 38.
  • 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) ] ))) }
  • 44.
  • 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 } }
  • 53.
  • 55.
  • 56. func flap() { node.physicsBody!.velocity = CGVector(dx: 0, dy: 0) node.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 6)) }
  • 57.
  • 60.
  • 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 }
  • 70.
  • 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 } }
  • 81.
  • 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)) }
  • 89.
  • 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
  • 97.
  • 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) }
  • 101.
  • 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