Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

How to Clone Flappy Bird in Swift

19.335 visualizaciones

Publicado el

Publicado en: Móvil, Software
  • And if you've ever taken a girl home, gotten hot and heavy and then felt embarrassment and PANIC when you take off your pants and see the look of DISAPPOINTMENT on her face, you need to go check this out right now. ★★★ http://ishbv.com/pebible/pdf
       Responder 
    ¿Estás seguro?    No
    Tu mensaje aparecerá aquí
  • How we discovered the real reason nice guys don't get laid, and a simple "fix" that allows you to gain the upper hand with a girl... without changing your personality or pretending to be someone you're not. learn more... ♥♥♥ http://ishbv.com/unlockher/pdf
       Responder 
    ¿Estás seguro?    No
    Tu mensaje aparecerá aquí
  • Hey guys! Who wants to chat with me? More photos with me here 👉 http://www.bit.ly/katekoxx
       Responder 
    ¿Estás seguro?    No
    Tu mensaje aparecerá aquí
  • Nice !! Download 100 % Free Ebooks, PPts, Study Notes, Novels, etc @ https://www.ThesisScientist.com
       Responder 
    ¿Estás seguro?    No
    Tu mensaje aparecerá aquí
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Responder 
    ¿Estás seguro?    No
    Tu mensaje aparecerá aquí

How to Clone Flappy Bird in Swift

  1. How To Clone FlappyBird in Swift
  2. A little introduction
  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. How to implement Hello World in Swift?
  8. println("Hello, World!")
  9. println("Hello, World!") Not exciting :-(
  10. far to be perfect
  11. but it was fun
  12. instructions
  13. git clone http://github.com/gscalzo/FlappySwift.git
  14. ./setup
  15. ./setup 1 ./setup 2 ./setup 3 ...
  16. walking skeleton ./setup 1
  17. class GameScene: SKScene { private var screenNode: SKSpriteNode! override func didMoveToView(view: SKView) { // ... }
  18. 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) }
  19. parallax layers ./setup 2
  20. override func didMoveToView(view: SKView) { //... Background(textureNamed: "background").addTo(screenNode).start() Ground(textureNamed: "ground").addTo(screenNode).start() }
  21. protocol Startable { func start() -> Startable func stop() -> Startable }
  22. class Background { private var parallaxNode: ParallaxNode! private let textureName: String init(textureNamed textureName: String) { } func addTo(parentNode: SKSpriteNode!) -> Background { return self } }
  23. 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 }
  24. extension Background : Startable { func start() -> Startable { parallaxNode.start(duration: 20.0) return self } func stop() -> Startable { parallaxNode.stop() return self } }
  25. class Ground { private var parallaxNode: ParallaxNode! private let textureName: String init(textureNamed textureName: String) { } func addTo(parentNode: SKSpriteNode!) -> Ground { return self } }
  26. 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 }
  27. extension Ground : Startable { func start() -> Startable { parallaxNode.start(duration: 5.0) return self } func stop() -> Startable { parallaxNode.stop() return self } }
  28. How to implement ParallaxNode?
  29. 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() { } }
  30. 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)) }
  31. 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 }
  32. func zPosition(zPosition: CGFloat) -> ParallaxNode { node.zPosition = zPosition return self } func addTo(parentNode: SKSpriteNode) -> ParallaxNode { parentNode.addChild(node) return self } func stop() { node.removeAllActions() }
  33. func start(#duration: NSTimeInterval) { node.runAction(SKAction.repeatActionForever(SKAction.sequence( [ SKAction.moveToX(-node.size.width/2.0, duration: duration), SKAction.moveToX(0, duration: 0) ] ))) }
  34. Our hero ./setup 3
  35. 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() }
  36. 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 } }
  37. 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 } }
  38. extension Bird : Startable { func start() -> Startable { animate() return self } func stop() -> Startable { node.physicsBody!.dynamic = false node.removeAllActions() return self } }
  39. private func animate(){ let animationFrames = textureNames.map { texName in SKTexture(imageNamed: texName) } node.runAction( SKAction.repeatActionForever( SKAction.animateWithTextures(animationFrames, timePerFrame: 0.1) )) }
  40. 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 } }
  41. Bird Physics 101
  42. Impulse
  43. func flap() { node.physicsBody!.velocity = CGVector(dx: 0, dy: 0) node.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 6)) }
  44. Pipes!
  45. Pipes! ./setup 4
  46. class GameScene: SKScene { override func didMoveToView(view: SKView) { //... Pipes(textureNames: ["pipeTop.png", "pipeBottom.png"]).addTo(screenNode).start() }
  47. 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 } }
  48. func start() -> Startable { let createAction = SKAction.repeatActionForever( SKAction.sequence( [ SKAction.runBlock { self.createNewPipe() }, SKAction.waitForDuration(3) ] ) ) parentNode.runAction(createAction, withKey: Pipes.createActionKey) return self }
  49. 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 }
  50. 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)) }
  51. 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!
  52. 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 }
  53. func start() { pipesNode.runAction(SKAction.sequence( [ SKAction.moveToX(finalOffset, duration: 6.0), SKAction.removeFromParent() ] )) }
  54. 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 }
  55. Contact ./setup 5
  56. 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 {
  57. 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()
  58. 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() }
  59. 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 } }
  60. 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() }
  61. 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() } } }
  62. extension GameScene: SKPhysicsContactDelegate { func didBeginContact(contact: SKPhysicsContact!) { } func didEndContact(contact: SKPhysicsContact!) { } }
  63. 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 } }
  64. 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 } }
  65. Final touches ./setup 6
  66. class GameScene: SKScene { private var actors: [Startable]! private var score: Score!
  67. 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() } }
  68. 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)" } }
  69. 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)
  70. func didEndContact(contact: SKPhysicsContact!) { //... case BodyType.gap.toRaw() | BodyType.bird.toRaw(): score.increase()
  71. 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)) }
  72. Into the Cave ./setup 7
  73. 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)
  74. 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"]) } }
  75. 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) } }
  76. extension PipePair { private func createPipe(#imageNamed: String) -> SKSpriteNode { let pipeNode = SKSpriteNode(imageNamed: imageNamed) pipeNode.shadowCastBitMask = BodyType.bird.toRaw() pipeNode.physicsBody = SKPhysicsBody.rectSize(pipeNode.size) {
  77. private func createNode(textureNamed: String, x: CGFloat) -> SKNode { let node = SKSpriteNode(imageNamed: textureNamed, normalMapped: true) node.lightingBitMask = BodyType.bird.toRaw()
  78. What if Michael Bay was the designer? ./setup 8
  79. Explosions!
  80. override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { switch touches.count { case 1: bird.flap() default: shoot() } }
  81. 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) }
  82. Game menu
  83. Leaderboard
  84. Swifterims!
  85. Please: Fork and PRs github.com/gscalzo/FlappySwift
  86. 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
  87. Thank you!
  88. Questions?

×