2015-09-07 3 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

По ссылке self.destY в закрытии motionManager.startAccelerometerUpdatesToQueue вы создаете сильный опорный цикл. Из документов,

Если вы назначаете замыкание к свойству экземпляра класса, и закрытие фиксирует этот экземпляр со ссылкой на экземпляр или его членов, вы будете создавать сильный опорный цикл между крышкой и пример.

Этот ссылочный цикл предотвращает освобождение вашей сцены. И поскольку вы не останавливаете менеджера движения, старые менеджеры (множественное число) все еще работают, когда вы переходите к сценам. Вероятно, это приведет к зависанию текущей сцены после нескольких переходов.

Swift использует списки захвата, чтобы избежать сильных опорных циклов, где список захвата имеет вид

{ [ /* 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

Смежные вопросы