重置didBeginContact()...或者玩家死亡时场景的联系方式是什么?

时间:2017-06-14 18:04:02

标签: swift sprite-kit skphysicsbody skphysicscontact

我正在处理一个我一直在努力修复的错误,

我的游戏包含一个与平台部分交互的播放器精灵......标准平台游戏。 我通过将薄物理块附加到更大的平台物理体(大白块)来设置我的游戏

每个瘦块代表:

  • 表面 - 绿色
  • 左墙 - 黄色
  • 右墙 - 红色

我可以使用自定义类配置打开或关闭其中每个。

  • 蓝色水斑是杀死玩家的死区

enter image description here

错误说明

所以我现在正在处理一个有趣的错误。 它发生的方式是我的玩家精灵击中一个死区物理体(杀死一个角色的区域)。

通过revive()函数恢复角色后会出现错误。 看来doBeginContact被调用并且相信我的角色与墙壁接触。即使看不到墙体物理体。

如果我在角色复活后开始快速按下跳跃,则只会出现错误。在按下跳转之前等待一段时间会导致错误发生。

联系deadZone时,我的GameScene类中的didBeginContact()代码如下所示

@objc(didBeginContact:) func didBegin(_ contact: SKPhysicsContact) {


        let firstBody: SKPhysicsBody
        let secondBody: SKPhysicsBody

        if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
            firstBody = contact.bodyA
            secondBody = contact.bodyB
        } else {
            firstBody = contact.bodyB
            secondBody = contact.bodyA
        }

    //MARK:  PLAYER with DEADZOne


    if ( firstBody.categoryBitMask == BodyType.player.rawValue && secondBody.categoryBitMask == BodyType.deadZone.rawValue) {

        print("deadzone contact with Player")
        if let somePlayer:Player = firstBody.node as? Player {
            somePlayer.changeRotation(newAngle: 0)
            killPlayer(somePlayer) 
        }
    }

请注意,该功能尚未完成......(缺少播放器 - 墙左(见下文),播放器 - 墙右)

我知道didBeginContact正在被调用,因为调试控制台发出了一个print语句,我在发生错误时从函数的Wall - Player部分编写。它打印&#34;与墙壁doBegin&#34; 联系,当您联系左侧或右侧墙壁时打印。

//MARK:  PLAYER with PLATFORM WALL (LEFT)

if ( firstBody.categoryBitMask == BodyType.player.rawValue && secondBody.categoryBitMask == BodyType.wallLeft.rawValue){


    if let somePlayer:Player = firstBody.node as? Player,
        let theWall:Platform = secondBody.node as? Platform
    {

        if somePlayer.jumpedOccured {

            print("contact with wall didBegin")

            somePlayer.onRightWall = false
            somePlayer.onLeftWall = true


            if somePlayer.isWallJumping {
                somePlayer.stopWallJump()
                playerOnWall(somePlayer, platformNode: theWall)

            } else {
                print("contact with wall didBegin")
                playerOnWall(somePlayer, platformNode: theWall)
            }

        }
    }
}

我的玩家回到开始的方式是通过代码将他移回重生点,在等级开始时通过调用killPlayer()调用一个不可见的精灵,该函数在GameScene类中调用包含此代码的函数: / p>

           var startBackToLocation:CGPoint = CGPoint.zero

            if (somePlayer == thePlayer) {

                if ( respawnPointPlayer1 != CGPoint.zero) {
                    startBackToLocation = respawnPointPlayer1     
                }  

            let wait:SKAction = SKAction.wait(forDuration: somePlayer.reviveTime)
            let move:SKAction = SKAction.move(to: startBackToLocation, duration:0)
            let run:SKAction = SKAction.run {
            somePlayer.playerIsDead = false;
            somePlayer.revive()
        }

        let seq:SKAction = SKAction.sequence([wait, move, wait, run])
        somePlayer.run(seq)

我可以通过简单地增加等待时间来防止错误发生,例如。

let wait:SKAction = SKAction.wait(forDuration: 1)
然而,这使得游戏玩法变得不那么流畅。

我不确定究竟是什么问题?

如果我从一个级别移除所有墙体物理主体,只有表面物理实体,我也可以防止发生错误......

我的猜测是,在调用didStimulatePhysics之前,它与SpriteKit的渲染循环首次调用didEvaluateActions有关......

所以didEvaluateActions将Player精灵移回到开头。 也许玩家在搬回墙壁时会碰到墙壁?

也许刺激物理学还没有完全赶上? 也许循环需要再次运行才能识别出玩家的物理团体已经移动了吗?

我有点失落,并且一直试图修复它。

这是一个展示该错误的视频。 YouTube Link

视频中的第一级

视频显示没有墙体物理主体的水平上没有发生的错误。

第二级

然后视频显示它发生在下一个级别,它有墙体物理机构。

  1. 第一次死亡后没有发生错误
  2. 第二次死亡后发生错误
  3. 第三次死亡后发生错误
  4. 死亡后发生错误
  5. 第五次死亡后发生错误
  6. 第六次死亡后发生错误
  7. 什么没有用

    1。在与deadZone联系时将isDynamic设置为false。

    然后使用Player类中的revive()将其更改为true

    // On Contact with DeadZone this code is executed
    self.physicsBody?.isDynamic = false 
    
    // On revive()
    self.physicsBody?.isDynamic = true 
    

    2。通过在deadZone联系人上将其设置为nil 来删除物理主体,然后在revive()上重新创建它。奇怪的是,在测试过程中,我可以直观地看到在死区接触时移除的物理矩形......然后重新出现......它仍然没有解决问题。

    somePlayer.physicsBody = nil
    

    第3。尝试重置播放器的contactTestMask ..通过在deadZone联系人上将其设置为零,然后在revive()上将其重置为默认值。

    e.g。关于DeadZone联系

    somePlayer.physicsBody?.contactTestBitMask = 0
    

    e.g。 on revive()

     self.physicsBody?.contactTestBitMask = BodyType.platformSurface.rawValue | BodyType.platformCeiling.rawValue | BodyType.wallRight.rawValue |  BodyType.wallLeft.rawValue | BodyType.pole.rawValue |  BodyType.portal.rawValue |  BodyType.deadZone.rawValue |  BodyType.coin.rawValue | BodyType.player.rawValue | BodyType.zipLine.rawValue | BodyType.springBoard.rawValue
    

    4。对每个玩家使用return语句 - 如果是playerIsDead,则使用___联系人退出范围。

    e.g。在左墙联系

    if somePlayer.playerIsDead == true {return} 
    

    5。直接设置玩家位置,而不是在玩家死亡时使用SKAction.move。

    let wait:SKAction = SKAction.wait(forDuration: somePlayer.reviveTime)
    let move:SKAction = SKAction.run {
        somePlayer.position = startBackToLocation
    }
    
    let run:SKAction = SKAction.run {
        somePlayer.playerIsDead = false;
        somePlayer.revive()
    }
    
    let seq:SKAction = SKAction.sequence([wait, move, wait, run])
    somePlayer.run(seq)
    

    什么减少了错误的频率

    1. 我尝试了重置物理功能&#34;在Player Class中...从Player类中的revive()函数调用。
    2. ,同时仍然设置  DeadZone联系人 somePlayer.physicsBody = nil

      func resetPlayerPhysicsBody() {
      
          let removePhysicsBody: SKAction = SKAction.run {
              self.physicsBody = nil
              self.canJumpAgain = false
          }
      
      
          let wait: SKAction = SKAction.wait(forDuration: self.reviveTime)
      
          let resetToDefaults: SKAction = SKAction.run {
              self.setUpPlayerPhysics() // Recreates initial physics body //
              self.canJumpAgain = true
          }
      
          let seq:SKAction = SKAction.sequence([removePhysicsBody, wait, resetToDefaults])
          self.run(seq)}
      

      这实际上使得错误......发生频率低得多......它几乎消除了它......但它也有一些奇怪的副作用,比如玩家在产卵后经常向右移动一点。

      虽然这些代码的添加只会在我最新的视频中出现3倍......

      1. 视频中13秒后死亡
      2. 视频中16秒后死亡
      3. 视频中1分13秒后死亡
      4. 以下是此次尝试的视频。 YouTube Link

        任何人对修复都有任何想法吗?

        我猜测某些方法&#34;重置&#34;或&#34;冲洗&#34; SpriteKit认为正在联系的是什么......但是这种情况发生的原因和原因也让我感到困扰。

1 个答案:

答案 0 :(得分:0)

所以我终于找到了修复...... 它不是超级优雅的。

如果有人有更好的解决方案。请发布它们,我喜欢一个不那么混乱的解决方案,或许更有意义的东西,因为这个解决方案对我如何设计东西施加了一些限制。

  1. 我在GameScene类中使用了以下新变量
  2. class GameScene: SKScene, SKPhysicsContactDelegate {
        var contactDetectionEnabled = true
        var wallContactDetectionEnabled = true
        var wallPhysicsOnNodePosition:CGPoint?
    
    1. 在某些级别中,这个bug非常顽固,我创建了一个&#34; WallPhysicsOnNode&#34;,当玩家通过它时,它会触发墙体物理。我将它们放置在第一个可以在游戏中跳墙的情况下。
    2. 如果存在,则为wallPhysicsOnNodePosition分配一个值,否则它将保持为零。

      enter image description here

      我在didMove(to :)中为wallPhysicsOnNodePosition指定了一个值

       self.enumerateChildNodes(withName: "//*") {
                  node, stop in
      
               if (node.name == "WallPhysicsOnNode") {
                      self.wallPhysicsOnNodePosition = node.position
                      node.isHidden = true
                      print("assigning wallPhysicsOnNodePosition")
               }
       }
      
      1. 我在我的Player类中使用了以下新变量
      2. class Player: SKSpriteNode {
            var jumpedOccured = false  // turned on via update() while isJumping //
            var fallingOccured = false // turned on via update() while isFalling //
        
        1. 在didBeginContact&amp; GameScene中的didEndContact ...我使用了以下if语句来阻止在某些情况下使用该函数。
        2. @objc(didBeginContact:) func didBegin(_ contact: SKPhysicsContact) {
             if contactDetectionEnabled {
          
          @objc(didEndContact:) func didEnd(_ contact: SKPhysicsContact) {
          
              if contactDetectionEnabled {
          
          1. 在didBeginContact&amp;在GameScene中的didEndContact ...我用另一个if语句包围了Wall - Player接触部分的代码,以防止它有时被使用。
          2.  if wallContactDetectionEnabled {
            
            1. 在GameScene中的didBeginContact中...在玩家死亡/与死区联系时我使用了延迟语句,以便在退出范围时发生以下情况。
            2. //MARK:  PLAYER with DEADZOne
              
              
              if ( firstBody.categoryBitMask == BodyType.player.rawValue && secondBody.categoryBitMask == BodyType.deadZone.rawValue){
              
                  print("deadzone contact with Player")
                  if let somePlayer:Player = firstBody.node as? Player{
              
                      if somePlayer.playerIsDead {
                          return
                      }
              
                      defer {
                          self.contactDetectionEnabled = false
                          self.wallContactDetectionEnabled = false
                          killPlayer(somePlayer)
                      }     
                  }
              }
              
              1. 我的GameScene在其更新()中有以下内容。这使得墙体物理只有在玩家通过该节点后才会打开...这是放置在首先可以发生跳墙的位置。
              2.    override func update(_ currentTime: TimeInterval) {
                       /* Called before each frame is rendered */
                
                       if (transitionInProgress == false) {
                         thePlayer.update()
                         thePlayer2.update()
                
                            defer {
                                if thePlayer.jumpedOccured && !thePlayer.playerIsDead && thePlayer.touchOccured && thePlayer.fallingOccured && !contactDetectionEnabled {
                                        contactDetectionEnabled = true
                                }
                
                                if thePlayer.jumpedOccured && !thePlayer.playerIsDead && thePlayer.touchOccured && thePlayer.fallingOccured && !wallContactDetectionEnabled {
                                    if let wallOnNodePosUnwrapped = wallPhysicsOnNodePosition {
                                        if thePlayer.position.x >= wallOnNodePosUnwrapped.x {
                                            wallContactDetectionEnabled = true
                                            print("Player passed wallPhysicsOnNodePosition ... so  \(wallContactDetectionEnabled)")
                                        }
                                    } else if wallPhysicsOnNodePosition == nil {
                                        wallContactDetectionEnabled = true
                                        print("No wallPhysicsOnNodePosition ... but wallContactDetectionEnabled is \(wallContactDetectionEnabled)")
                                    }
                                }
                            }
                

                还有一点......但你几乎可以搞清楚......

                使用Swifts defer {} 能力确实是我解决方案的关键。

                几乎没有必要使用wallContactDetectionEnabled ......但我偶尔也会得到这个bug,所以我添加了那个部分并使用wallPhysicsOnNodePosition来控制它,以防bug仍然存在。

                但是,对于所有级别,我都会在壁跳跃首次发生的位置添加一个WallPhysicsOnNode。

                再一次,解决方案远非理想的......它有点混乱,我可能会在以后清理它......但它的工作正常。

                希望有更好的东西......但是我尝试了很多逻辑应该有用的东西,但没有。直接添加上面的代码不应该是逻辑上必要的。我很震惊,我需要额外的if声明封装了墙 - 玩家联系方式。没有它就很难发生这个错误...但由于某些原因它偶尔也会发生。

                希望有更优雅的东西......因为这是一个真正的胶带解决方案&#34;。但是,至少它是有效的。

                如果有人有任何意见,请随时分享。 我的回答可能有效......但它只能防止这种情况发生。它在技术上并没有触及到底发生的事情。

                这可能会在将来引发问题。