// // GameScene.swift // FlappyBird // // Created by Nate Murray on 6/2/14. // Copyright (c) 2014 Fullstack.io. All rights reserved. // import SpriteKit class GameScene: SKScene, SKPhysicsContactDelegate{ let verticalPipeGap = 150.0 var bird:SKSpriteNode!//鸟精灵 var skyColor:SKColor!//天空颜色 var pipeTextureUp:SKTexture!//柱子纹理上 var pipeTextureDown:SKTexture!//柱子纹理下 var movePipesAndRemove:SKAction!//移动柱子并且消除动作 var moving:SKNode!//移动节点 var pipes:SKNode!//柱子节点 var canRestart = Bool()//能否重新开始 var scoreLabelNode:SKLabelNode!//分数标签 var score = NSInteger()//分数 //物理引擎标识 let birdCategory: UInt32 = 1 << 0//鸟 let worldCategory: UInt32 = 1 << 1//地面 let pipeCategory: UInt32 = 1 << 2//柱子 let scoreCategory: UInt32 = 1 << 3//分数 //切换入视图 override func didMoveToView(view: SKView) { //是否能重新开始为否 canRestart = false // setup physics self.physicsWorld.gravity = CGVectorMake( 0.0, -5.0 )//重力 self.physicsWorld.contactDelegate = self//物体碰撞检测回调函数 // setup background color skyColor = SKColor(red: 81.0/255.0, green: 192.0/255.0, blue: 201.0/255.0, alpha: 1.0) self.backgroundColor = skyColor moving = SKNode() self.addChild(moving) pipes = SKNode() moving.addChild(pipes) // ground //地面图片 let groundTexture = SKTexture(imageNamed: "land") groundTexture.filteringMode = .Nearest // shorter form for SKTextureFilteringMode.Nearest //地面的动画 let moveGroundSprite = SKAction.moveByX(-groundTexture.size().width * 2.0, y: 0, duration: NSTimeInterval(0.02 * groundTexture.size().width * 2.0)) let resetGroundSprite = SKAction.moveByX(groundTexture.size().width * 2.0, y: 0, duration: 0.0) let moveGroundSpritesForever = SKAction.repeatActionForever(SKAction.sequence([moveGroundSprite,resetGroundSprite])) //地面图片拼接 for var i:CGFloat = 0; i < 2.0 + self.frame.size.width / ( groundTexture.size().width * 2.0 ); ++i { let sprite = SKSpriteNode(texture: groundTexture) sprite.setScale(2.0) sprite.position = CGPointMake(i * sprite.size.width, sprite.size.height / 2.0) sprite.runAction(moveGroundSpritesForever) moving.addChild(sprite) } // skyline //天空图片 let skyTexture = SKTexture(imageNamed: "sky") skyTexture.filteringMode = .Nearest //天空动作 let moveSkySprite = SKAction.moveByX(-skyTexture.size().width * 2.0, y: 0, duration: NSTimeInterval(0.1 * skyTexture.size().width * 2.0)) let resetSkySprite = SKAction.moveByX(skyTexture.size().width * 2.0, y: 0, duration: 0.0) let moveSkySpritesForever = SKAction.repeatActionForever(SKAction.sequence([moveSkySprite,resetSkySprite])) //播放动作 for var i:CGFloat = 0; i < 2.0 + self.frame.size.width / ( skyTexture.size().width * 2.0 ); ++i { let sprite = SKSpriteNode(texture: skyTexture) sprite.setScale(2.0) sprite.zPosition = -20 sprite.position = CGPointMake(i * sprite.size.width, sprite.size.height / 2.0 + groundTexture.size().height * 2.0) sprite.runAction(moveSkySpritesForever) moving.addChild(sprite) } // create the pipes textures pipeTextureUp = SKTexture(imageNamed: "PipeUp") pipeTextureUp.filteringMode = .Nearest pipeTextureDown = SKTexture(imageNamed: "PipeDown") pipeTextureDown.filteringMode = .Nearest // create the pipes movement actions //柱子动作 let distanceToMove = CGFloat(self.frame.size.width + 2.0 * pipeTextureUp.size().width) let movePipes = SKAction.moveByX(-distanceToMove, y:0.0, duration:NSTimeInterval(0.01 * distanceToMove)) let removePipes = SKAction.removeFromParent() movePipesAndRemove = SKAction.sequence([movePipes, removePipes]) // spawn the pipes //2秒生产一对柱子 let spawn = SKAction.runBlock({() in self.spawnPipes()})//执行函数动画 let delay = SKAction.waitForDuration(NSTimeInterval(2.0)) let spawnThenDelay = SKAction.sequence([spawn, delay]) let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay) self.runAction(spawnThenDelayForever) // setup our bird let birdTexture1 = SKTexture(imageNamed: "bird-01") birdTexture1.filteringMode = .Nearest let birdTexture2 = SKTexture(imageNamed: "bird-02") birdTexture2.filteringMode = .Nearest let birdTexture3 = SKTexture(imageNamed: "bird-03") birdTexture3.filteringMode = .Nearest let birdTexture4 = SKTexture(imageNamed: "bird-04") birdTexture4.filteringMode = .Nearest //执行动画 let anim = SKAction.animateWithTextures([birdTexture1, birdTexture2,birdTexture3,birdTexture4], timePerFrame: 0.2) let flap = SKAction.repeatActionForever(anim) bird = SKSpriteNode(texture: birdTexture1) bird.setScale(2.0) bird.position = CGPoint(x: self.frame.size.width * 0.35, y:self.frame.size.height * 0.6) bird.runAction(flap) bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.size.height / 2.0) bird.physicsBody.dynamic = true bird.physicsBody.allowsRotation = false bird.physicsBody.categoryBitMask = birdCategory//自己的掩码 bird.physicsBody.collisionBitMask = worldCategory | pipeCategory//弹开的掩码 bird.physicsBody.contactTestBitMask = worldCategory | pipeCategory//碰撞事件掩码 self.addChild(bird) // create the ground var ground = SKNode() ground.position = CGPointMake(0, groundTexture.size().height) ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, groundTexture.size().height * 2.0)) ground.physicsBody.dynamic = false ground.physicsBody.categoryBitMask = worldCategory self.addChild(ground) // Initialize label and create a label which holds the score score = 0 scoreLabelNode = SKLabelNode(fontNamed:"MarkerFelt-Wide") scoreLabelNode.position = CGPointMake( CGRectGetMidX( self.frame ), 3 * self.frame.size.height / 4 ) scoreLabelNode.zPosition = 100 scoreLabelNode.text = String(score) self.addChild(scoreLabelNode) } func spawnPipes() { let pipePair = SKNode() pipePair.position = CGPointMake( self.frame.size.width + pipeTextureUp.size().width * 2, 0 ) pipePair.zPosition = -10 let height = UInt32( self.frame.size.height / 4 ) let y = arc4random() % height + height let pipeDown = SKSpriteNode(texture: pipeTextureDown) pipeDown.setScale(2.0) pipeDown.position = CGPointMake(0.0, CGFloat(y) + pipeDown.size.height + CGFloat(verticalPipeGap)) pipeDown.physicsBody = SKPhysicsBody(rectangleOfSize: pipeDown.size) pipeDown.physicsBody.dynamic = false pipeDown.physicsBody.categoryBitMask = pipeCategory pipeDown.physicsBody.contactTestBitMask = birdCategory pipePair.addChild(pipeDown) let pipeUp = SKSpriteNode(texture: pipeTextureUp) pipeUp.setScale(2.0) pipeUp.position = CGPointMake(0.0, CGFloat(y)) pipeUp.physicsBody = SKPhysicsBody(rectangleOfSize: pipeUp.size) pipeUp.physicsBody.dynamic = false pipeUp.physicsBody.categoryBitMask = pipeCategory pipeUp.physicsBody.contactTestBitMask = birdCategory pipePair.addChild(pipeUp) var contactNode = SKNode() contactNode.position = CGPointMake( pipeDown.size.width + bird.size.width / 2, CGRectGetMidY( self.frame ) ) contactNode.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake( pipeUp.size.width, self.frame.size.height )) contactNode.physicsBody.dynamic = false contactNode.physicsBody.categoryBitMask = scoreCategory contactNode.physicsBody.contactTestBitMask = birdCategory pipePair.addChild(contactNode) pipePair.runAction(movePipesAndRemove) pipes.addChild(pipePair) } func resetScene (){ // Move bird to original position and reset velocity bird.position = CGPointMake(self.frame.size.width / 2.5, CGRectGetMidY(self.frame)) bird.physicsBody.velocity = CGVectorMake( 0, 0 ) bird.physicsBody.collisionBitMask = worldCategory | pipeCategory bird.speed = 1.0 bird.zRotation = 0.0 // Remove all existing pipes pipes.removeAllChildren() // Reset _canRestart canRestart = false // Reset score score = 0 scoreLabelNode.text = String(score) // Restart animation moving.speed = 1 } override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { /* Called when a touch begins */ if moving.speed > 0 { for touch: AnyObject in touches {//多点触摸 let location = touch.locationInNode(self) bird.physicsBody.velocity = CGVectorMake(0, 0)//把速度设为0 不设的话有加速度 bird.physicsBody.applyImpulse(CGVectorMake(0, 30))//推力 } }else if canRestart { self.resetScene() } } // TODO: Move to utilities somewhere. There's no reason this should be a member function func clamp(min: CGFloat, max: CGFloat, value: CGFloat) -> CGFloat { if( value > max ) { return max } else if( value < min ) { return min } else { return value } } override func update(currentTime: CFTimeInterval) { /* Called before each frame is rendered */ //鸟的角度 bird.zRotation = self.clamp( -1, max: 0.5, value: bird.physicsBody.velocity.dy * ( bird.physicsBody.velocity.dy < 0 ? 0.003 : 0.001 ) ) NSLog(String(bird.physicsBody.velocity.dy), "velocity") } func didBeginContact(contact: SKPhysicsContact) { NSLog("contact", "title") if moving.speed > 0 { if ( contact.bodyA.categoryBitMask & scoreCategory ) == scoreCategory || ( contact.bodyB.categoryBitMask & scoreCategory ) == scoreCategory {//如果通过分数区域 // Bird has contact with score entity score++ scoreLabelNode.text = String(score) // Add a little visual feedback for the score increment scoreLabelNode.runAction(SKAction.sequence([SKAction.scaleTo(1.5, duration:NSTimeInterval(0.1)), SKAction.scaleTo(1.0, duration:NSTimeInterval(0.1))])) } else {//如果碰撞到其他的物体 moving.speed = 0 //碰到柱子后只能和地面有碰撞 bird.physicsBody.collisionBitMask = worldCategory bird.runAction( SKAction.rotateByAngle(CGFloat(M_PI) * CGFloat(bird.position.y) * 0.01, duration:1), completion:{self.bird.speed = 0 }) // Flash background if contact is detected self.removeActionForKey("flash") self.runAction(SKAction.sequence([SKAction.repeatAction(SKAction.sequence([SKAction.runBlock({ self.backgroundColor = SKColor(red: 1, green: 0, blue: 0, alpha: 1.0) }),SKAction.waitForDuration(NSTimeInterval(0.05)), SKAction.runBlock({ self.backgroundColor = self.skyColor }), SKAction.waitForDuration(NSTimeInterval(0.05))]), count:4), SKAction.runBlock({ self.canRestart = true })]), withKey: "flash") } } } }