2015-09-07 67 views
1

多次在游戏场景和游戏场景之间切换后,我的一个精灵会冻结。一旦我死亡并重新启动大约6-7次,我的'敌方精灵'就不再对我的设备的倾斜作出反应(如果确实需要很长时间)。使用屏幕上的操纵杆时,我的播放器仍可以很好地移动。整个过程我的FPS读数为60.多次切换场景后SpriteKit游戏被冻结

class GameScene: SKScene, SKPhysicsContactDelegate { 

    let joyStickSprite = SKSpriteNode(imageNamed: "flatLight09") 
    let playerSprite = SKSpriteNode(imageNamed: "p3_front") 
    let enemySprite = SKSpriteNode(imageNamed: "elementExplosive001") 
    let coinSprite = SKSpriteNode(imageNamed: "gold_1") 
    var left = false 
    var right = false 


    var enemyCount = 0 
    var randomNextCoin = Int(arc4random_uniform(5)+1) 

    var motionManager = CMMotionManager() 
    var destY:CGFloat = 0.0 

    struct CollisionCategoryBitmask { 
     static let Player: UInt32 = 0x00 
     static let Enemy: UInt32 = 0x01 
     static let Floor: UInt32 = 0x02 
     static let Coin: UInt32 = 0x03 
    } 

    var background = SKSpriteNode(imageNamed: "blue_land") 

    override func didMoveToView(view: SKView) { 

     self.physicsWorld.contactDelegate = self 


     createBackground() 
     createPlayer() 
     createJoyStick() 
     createScreenBorder() 
     createEnemy() 


     motionManager.accelerometerUpdateInterval = 0.1 
     motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.currentQueue(), withHandler: { 
      (accelerometerData: CMAccelerometerData!, error: NSError!) in 


       var currentY = self.enemySprite.position.y 

       let acceleration = accelerometerData.acceleration 
       self.destY = (CGFloat(acceleration.y) * 0.75) + (self.destY * 0.25) 

      }) 
     } 



    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) { 
     /* Called when a touch begins */ 



     for touch in (touches as! Set<UITouch>) { 
      let location = touch.locationInNode(self) 

      let touchedNode = self.nodeAtPoint(location) 

      if let name = touchedNode.name 
      { 
       if name == "joyStick" 
       { 

        if location.x < joyStickSprite.position.x { 
         left = true 
         right = false 
        } 

        else { 
         right = true 
         left = false 
        } 
       } 
      } 
     } 

    } 

    override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) { 

     left = false 
     right = false 
    } 

    func presentGameOver() { 

     removeAllChildren() 

     let newScene = GameScene(size: size) 
     newScene.scaleMode = scaleMode 
     let reveal = SKTransition.flipHorizontalWithDuration(0.5) 
     view?.presentScene(newScene, transition: reveal) 
     enemySprite.physicsBody?.dynamic = false 
    } 

    func createBackground() { 

     background.position = CGPoint(x: frame.size.width/2, y: frame.size.height/2) 
     background.size.height = self.size.height 

     addChild(background) 

    } 


    func createPlayer() { 

     playerSprite.position = CGPoint(x: self.size.width/2, y: playerSprite.size.height/2) 
     playerSprite.physicsBody = SKPhysicsBody(circleOfRadius: playerSprite.size.width/2) 
     playerSprite.physicsBody?.dynamic = false 
     playerSprite.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Player 
     playerSprite.physicsBody?.collisionBitMask = 0 
     playerSprite.physicsBody?.contactTestBitMask = CollisionCategoryBitmask.Enemy | CollisionCategoryBitmask.Coin 


     addChild(playerSprite) 

    } 

    func movePlayerLeft() { 
     let moveLeft = SKAction.moveByX(-10, y: 0, duration: 1) 
     playerSprite.runAction(moveLeft) 
    } 

    func movePlayerRight() { 
     let moveRight = SKAction.moveByX(10, y: 0, duration: 1) 
     playerSprite.runAction(moveRight) 
    } 

    func createEnemy() { 

     var randomX = Int(arc4random_uniform(600)) 
     var randomXCG = CGFloat(randomX) 


     enemyCount += 1 

     enemySprite.position = CGPoint(x: randomXCG, y: self.size.height) 
     enemySprite.physicsBody = SKPhysicsBody(circleOfRadius: enemySprite.size.width/2) 
     enemySprite.physicsBody?.dynamic = true 
     enemySprite.physicsBody?.allowsRotation = true 
     enemySprite.physicsBody?.restitution = 0.0 
     enemySprite.physicsBody?.friction = 0.0 
     enemySprite.physicsBody?.angularDamping = 0.0 
     enemySprite.physicsBody?.linearDamping = 0.0 
     enemySprite.physicsBody?.affectedByGravity = true 

     enemySprite.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Enemy 
     enemySprite.physicsBody?.collisionBitMask = CollisionCategoryBitmask.Floor 


     println("enemey count \(enemyCount)") 
     println("next coin \(randomNextCoin)") 

     addChild(enemySprite) 
    } 

    func createCoins() { 

     var randomX = Int(arc4random_uniform(600)) 
     var randomXCG = CGFloat(randomX) 

     randomNextCoin = Int(arc4random_uniform(10)) 
     enemyCount = 0 

     coinSprite.size.height = playerSprite.size.height/2 
     coinSprite.size.width = coinSprite.size.height 
     coinSprite.position = CGPoint(x: randomXCG, y: self.size.height) 
     coinSprite.physicsBody = SKPhysicsBody(circleOfRadius: enemySprite.size.width/2) 
     coinSprite.physicsBody?.dynamic = true 
     coinSprite.physicsBody?.affectedByGravity = true 
     coinSprite.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Coin 

     addChild(coinSprite) 
    } 


    func createJoyStick() { 

     joyStickSprite.setScale(0.4) 
     joyStickSprite.position = CGPoint(x: self.size.width/1.1, y: joyStickSprite.size.height) 
     joyStickSprite.name = "joyStick" 
     joyStickSprite.userInteractionEnabled = false 

     addChild(joyStickSprite) 
    } 

    func updateEnemyPosition() { 

     if enemySprite.size.height > enemySprite.position.y { 

      enemySprite.position.x = enemySprite.position.x + destY*20 
     } 


    } 

    func didBeginContact(contact: SKPhysicsContact) { 

     let firstNode = contact.bodyA.node as! SKSpriteNode 
     let secondNode = contact.bodyB.node as! SKSpriteNode 

     if (contact.bodyA.categoryBitMask == CollisionCategoryBitmask.Player) && 
      (contact.bodyB.categoryBitMask == CollisionCategoryBitmask.Enemy) { 

       let transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0) 

       let scene = SecondScene(size: self.scene!.size) 
       scene.scaleMode = SKSceneScaleMode.AspectFill 

       self.scene!.view!.presentScene(scene, transition: transition) 
       } 

     if (contact.bodyA.categoryBitMask == CollisionCategoryBitmask.Player) && 
      (contact.bodyB.categoryBitMask == CollisionCategoryBitmask.Coin) { 

       coinSprite.removeFromParent() 
     } 
    } 

    func createScreenBorder() { 


     // 1. Create a physics body that borders the screen 
     let borderBody = SKPhysicsBody(edgeFromPoint: CGPointMake(0.0, 0.0), toPoint: CGPointMake(self.size.width, 0.0)) 
     // 2. Set the friction of that physicsBody to 0 
     borderBody.friction = 0 

     borderBody.categoryBitMask = CollisionCategoryBitmask.Floor 

     // 3. Set physicsBody of scene to borderBody 
     self.physicsBody = borderBody 
    } 



    override func update(currentTime: CFTimeInterval) { 

     //detect where on joystick player is touching 
     if left == true { 
      movePlayerLeft() 
     } 

     if right == true { 
      movePlayerRight() 
     } 

     //move player to other side when going off screen 
     if playerSprite.position.x < -20.0 { 
      playerSprite.position = CGPoint(x: self.size.width + 20.0, y: playerSprite.position.y) 
     } else if (playerSprite.position.x > self.size.width + 20.0) { 
      playerSprite.position = CGPoint(x: -20.0, y: playerSprite.position.y) 
     } 


     //remove enemeny if off screen 
     if enemySprite.position.x < -20.0 || enemySprite.position.x > self.size.width + 20.0 { 
      self.enemySprite.removeFromParent() 
      createEnemy() 
     } 

     if randomNextCoin == enemyCount { 
      println("coin dropped") 
      coinSprite.removeFromParent() 
      createCoins() 
     } 

     updateEnemyPosition() 


    } 
} 

有没有人有任何建议?

回答

2

通过在motionManager.startAccelerometerUpdatesToQueue闭包中引用self.destY,您将创建一个强大的参考周期。从文档,

如果您分配一个闭合的类实例的属性,并关闭参照实例或它的成员捕获实例,您将创建关闭和之间有很强的参考周期实例。

此参考周期阻止您的场景被释放。既然你不停止运动管理器,那么当你转换场景时,旧的管理者(复数)仍然在运行。这可能会导致当前场景在多次转换后冻结。

斯威夫特使用捕捉列表,以避免强烈的参考周期,其中一个捕获列表的形式

{ [ /* weak or unowned + object, ...*/ ] 
    /* parameters */ in 

} 

您可以定义在一个封闭的捕获作为无主。来自文档,

当闭包和它捕获的实例总是互相引用时,将闭包中的捕获定义为无主引用,并且将始终在同一时间解除分配。

相反,定义一个捕获作为弱引用时所捕获的参考可以在未来的某个时刻成为零。弱引用始终是可选类型,并且在它们引用的实例被释放时自动变为零。这使您可以检查封闭体内的存在。

下面是如何捕捉列表添加到您的加速处理程序的例子:

motionManager.accelerometerUpdateInterval = 0.1 
    motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.currentQueue()) { 
     [unowned self] accelerometerData, error in 

      var currentY = self.enemySprite.position.y 
      let acceleration = accelerometerData.acceleration 
      self.destY = (CGFloat(acceleration.y) * 0.75) + (self.destY * 0.25) 
    } 

最后,这是一个好主意,过渡场景

override func willMoveFromView(view: SKView) { 
     motionManager.stopAccelerometerUpdates() 
    } 
+0

真棒之前停止加速更新。非常感谢。这造成了一个不同的世界,我非常感谢你解释它背后的想法和推理。 – Mir