试图建立平台,我可以从下面跳过,但降落在上面。难以微调逻辑

时间:2017-07-04 20:07:32

标签: ios swift3 sprite-kit game-physics

我的目标是在.sks文件中设置我的所有平台,以便更轻松地设计我的关卡。

这是在didMove:

之前在游戏scene.swift的顶部声明的
private var JumpThroughPlatformObject = SKSpriteNode()

这是在DidMove:

if let JumpThroughPlatformObjectNode = self.childNode(withName: "//jumpThroughPlatform1") as? SKSpriteNode {
        JumpThroughPlatformObject = JumpThroughPlatformObjectNode} 

我引用平台从.sks获得它的高度,因为我的所有平台都将达到相同的高度,我只需从一个平台获得它。

以下是我尝试在我的更新方法中使用的方法来关闭碰撞,直到我的播放器完全位于平台之上。仅检查我的球员速度是否大于零的主要问题是:如果球员处于跳跃的峰值(他的速度减慢到零)。如果发生这种情况且玩家在平台内,他会立即弹到平台顶部或向下发射。

我不希望我的平台必须是1像素高的线。我还需要让玩家拥有一个完整的碰撞盒,因为他将与其他类型的环境进行交互。这让我相信我不知何故只需要将平台的顶部注册为碰撞盒而不是整个平台。

我写的if语句应该取平台的y位置并将其高度的一半加到它上面,因为y位置是基于sprite的中心我认为这会使平台的碰撞发生在它的顶部边界。

我为玩家做了同样的事情,但反之亦然。将球员碰撞只放在边界的底部。但它不能很好地工作,我不知道为什么在这一点。

if (JumpThroughPlatformObject.position.y + (JumpThroughPlatformObject.size.height / 2)) > (player.position.y - (player.size.height / 2))

以下功能给出了3个主要问题:

  1. 我的球员跳跃总是dy = 80.如果我跳到一个位置.y = 90的平台,跳跃的球员高峰停在平台中间,但他传送到它顶部而不是继续倒在地上。

  2. 如果我摔倒,平台的左右边缘仍然与玩家完全碰撞

  3. 如果我的播放器在平台上并且在我上面还有另一个播放器,则播放器无法跳过它。

    设为零:CGFloat = 0

        if let body = player.physicsBody {
            let dy = player.physicsBody?.velocity.dy
    
            // when I jump dy is greater than zero else I'm falling
    
           if (dy! >= zero) {
    
               if (JumpThroughPlatformObject.position.y + (JumpThroughPlatformObject.size.height / 2)) > (player.position.y - (player.size.height / 2)) {
    
                    print(" platform y: \(JumpThroughPlatformObject.position.y)")
                    print ("player position: \(player.position.y)")
    
                    // Prevent collisions if the hero is jumping
                    body.collisionBitMask = CollisionTypes.saw.rawValue | CollisionTypes.ground.rawValue
    
                }
    
           }
            else {
                // Allow collisions if the hero is falling
                body.collisionBitMask = CollisionTypes.platform.rawValue | CollisionTypes.ground.rawValue | CollisionTypes.saw.rawValue
            }
        } 
    
  4. 任何建议都将不胜感激。我已经把头发撕了几天了。

    在didBegin和didEnd编辑:

       func didBegin(_ contact: SKPhysicsContact) {
    
        if let body = player.physicsBody {
            let dy = player.physicsBody?.velocity.dy
            let platform = JumpThroughPlatformObject
            let zero:CGFloat = 0
    
    
        if contact.bodyA.node == player {
          // playerCollided(with: contact.bodyB.node!)
    
                if (dy! > zero || body.node!.intersects(platform)) && ((body.node?.position.y)! - player.size.height / 2 < platform.position.y + platform.size.height / 2) {
    
                    body.collisionBitMask &= ~CollisionTypes.platform.rawValue
    
                }
    
    
    
        } else if contact.bodyB.node == player {
         //  playerCollided(with: contact.bodyA.node!)
            isPlayerOnGround = true
    
            if (dy! > zero || body.node!.intersects(platform)) && ((body.node?.position.y)! - player.size.height / 2 < platform.position.y + platform.size.height / 2) {
    
                body.collisionBitMask &= ~CollisionTypes.platform.rawValue}
            }
        }
    }
    
    func didEnd(_ contact: SKPhysicsContact) {
    
        if let body = player.physicsBody {
    //            let dy = player.physicsBody?.velocity.dy
    //            let platform = JumpThroughPlatformObject
    
        if contact.bodyA.node == player {
    
    
            body.collisionBitMask |= CollisionTypes.platform.rawValue
    
        }else if contact.bodyB.node == player {
    
            body.collisionBitMask |= CollisionTypes.platform.rawValue
    
        }
        }
    }
    

    添加我所做的,玩家无法再跳过平台。

3 个答案:

答案 0 :(得分:2)

你只需要一个条件让你知道你是否身体健康。我还清理了你的代码,以避免意外地输入错误的类别

if let body = player.physicsBody,   let dy = body.velocity.dy {
   // when I am jumping or I am in a platform, then do not register
   if (dy > zero || body.node.intersects(platform) && (body.node.position.y - body.node.size.height/2 != platform.position.y + platform.size.height / 2)  {
        body.collisionBitMask &= ~CollisionTypes.platform.rawValue

   }
    else {
        // Allow collisions if the hero is falling
        body.collisionBitMask |= CollisionTypes.platform.rawValue

答案 1 :(得分:2)

这是我为macOS和iOS目标制作的项目的链接:
   https://github.com/fluidityt/JumpUnderPlatform

基本上,这一切都与

有关
  1. 检测平台的碰撞
  2. 然后确定您的玩家是否在平台下
  3. 允许您的玩家通过该平台(然后登陆该平台)
  4. -

    SK物理学使这有点复杂:

    1. 在碰撞检测中,您的播放器为.position.y.velocity.dy 可能已经改变为“假”状态,指的是满足上面的#2检查(意味着#3永远不会发生)。此外,您的播放器将在首次联系时从平台上弹回。

    2. 没有“自动”方式来确定玩家何时完成穿过对象(从而允许玩家降落在平台上)

    3. -

      所以为了让一切工作,必须使用一点创造力和独创性!

      1:检测平台的碰撞:

      所以,解决1是最简单的:我们只需要使用内置的didBegin(contact:)

      我们将严重依赖3大比特马斯,联系人,类别和碰撞:

      (fyi,我不喜欢在物理上使用枚举和bitmath,因为我是反叛的白痴)

      struct BitMasks {
        static let playerCategory = UInt32(2)
        static let jupCategory    = UInt32(4) // JUP = JumpUnderPlatform 
      }
      
      override func didBegin(_ contact: SKPhysicsContact) {
      
        // Crappy way to do "bit-math":
        let contactedSum = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask
      
        switch contactedSum {
      
        case BitMasks.jupCategory + BitMasks.playerCategory:
        // ...
      }  
      

      -

      现在,你说你想使用SKSEditor,所以我接待了你:

      enter image description here enter image description here enter image description here enter image description here

      // Do all the fancy stuff you want here...
      class JumpUnderPlatform: SKSpriteNode {
      
        var pb: SKPhysicsBody { return self.physicsBody! } // If you see this on a crash, then WHY DOES JUP NOT HAVE A PB??
      
        // NOTE: I could not properly configure any SKNode properties here..
        // it's like they all get RESET if you put them in here...
        required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }   
      }
      

      -

      现在为玩家:

      class Player: SKSpriteNode {
      
        // If you see this on a crash, then WHY DOES PLAYER NOT HAVE A PB??
        var pb: SKPhysicsBody { return self.physicsBody! }
      
        static func makePlayer() -> Player {
      
          let newPlayer = Player(color: .blue, size: CGSize(width: 50, height: 50))
          let newPB = SKPhysicsBody(rectangleOf: newPlayer.size)
      
          newPB.categoryBitMask = BitMasks.playerCategory
          newPB.usesPreciseCollisionDetection = true
      
          newPlayer.physicsBody = newPB
          newPlayer.position.y -= 200 // For demo purposes.
      
          return newPlayer
        }
      }
      

      2。 (并处理#4):确定是否在联系平台下:

      有很多方法可以做到这一点,但我选择使用KOD提到的player.pb.velocity.dy方法来跟踪玩家的位置...如果你的dy超过0,那么你就是跳跃(在平台)如果没有,那么你要么站着不动要么摔倒(需要与平台接触并坚持下去)。

      为了实现这一点,我们必须更加技术化,因为物理系统和SK在其循环中的工作方式并不总是100%与我们认为它应该如何工作。

      基本上,我必须为Player initialDY

      每个帧不断更新一个update属性

      这个initialDY将为我们提供第一次与平台联系所需的正确数据,允许我们告诉我们更改碰撞掩码,并将我们玩家的CURRENT dy重置为最初的dy(所以玩家不会反弹。)

      3。 (并处理#5):允许玩家通过平台

      要浏览平台,我们需要使用collisionBitMasks。我选择让玩家的碰撞面具=玩家的categoryMask,这可能不是正确的方法,但它适用于这个演示。

      你最终会在didBegin

      中使用这样的魔法
        // Check if jumping; if not, then just land on platform normally.
        guard player.initialDY > 0 else { return }
      
        // Gives us the ability to pass through the platform!
        player.pb.collisionBitMask = BitMasks.playerCategory
      

      现在,处理#5将需要我们为我们的玩家类添加另一个状态..我们需要临时存储所联系的平台,以便我们可以检查玩家是否已成功完成通过平台(所以我们可以重置碰撞掩码)

      然后我们只检查didFinishUpdate玩家的框架是否高于该平台,如果是,我们重置掩码。

      以下是所有文件,以及github的链接:
      https://github.com/fluidityt/JumpUnderPlatform

      Player.swift:

      class Player: SKSpriteNode {
      
        // If you see this on a crash, then WHY DOES PLAYER NOT HAVE A PB??
        var pb: SKPhysicsBody { return self.physicsBody! }
      
        // This is set when we detect contact with a platform, but are underneath it (jumping up)
        weak var platformToPassThrough: JumpUnderPlatform?
      
        // For use inside of gamescene's didBeginContact (because current DY is altered by the time we need it)
        var initialDY = CGFloat(0)
      }
      
      // MARK: - Funkys:
      extension Player {
        static func makePlayer() -> Player {
      
          let newPlayer = Player(color: .blue, size: CGSize(width: 50, height: 50))
          let newPB = SKPhysicsBody(rectangleOf: newPlayer.size)
      
          newPB.categoryBitMask = BitMasks.playerCategory
          newPB.usesPreciseCollisionDetection = true
      
          newPlayer.physicsBody = newPB
          newPlayer.position.y -= 200 // For demo purposes.
      
          return newPlayer
        }
      
      
        func isAbovePlatform() -> Bool {
          guard let platform = platformToPassThrough else { fatalError("wtf is the platform!") }
      
          if frame.minY > platform.frame.maxY { return true  }
          else                                { return false }
        }
      
        func landOnPlatform() {
            print("resetting stuff!")
            platformToPassThrough = nil
            pb.collisionBitMask = BitMasks.jupCategory
        }
      }
      
      // MARK: - Player GameLoop:
      extension Player {
      
        func _update() {
          // We have to keep track of this for proper detection of when to pass-through platform
          initialDY = pb.velocity.dy
        }
      
        func _didFinishUpdate() {
      
          // Check if we need to reset our collision mask (allow us to land on platform again)
          if platformToPassThrough != nil {
            if isAbovePlatform() { landOnPlatform() }
          }
        }
      }
      

      JumpUnderPlatform&amp; BitMasks.swift(分别为:)

      // Do all the fancy stuff you want here...
      class JumpUnderPlatform: SKSpriteNode {
      
        var pb: SKPhysicsBody { return self.physicsBody! } // If you see this on a crash, then WHY DOES JUP NOT HAVE A PB??
      
        required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
      
      }
      
      struct BitMasks {
        static let playerCategory = UInt32(2)
        static let jupCategory    = UInt32(4)
      }
      

      GameScene.swift:

      -

      请确认您在SKS编辑中有两个节点: enter image description here enter image description here

      -

      // MARK: - Props:
      class GameScene: SKScene, SKPhysicsContactDelegate {
      
        // Because I hate crashes related to spelling errors.
        let names = (jup: "jup", resetLabel: "resetLabel")
      
        let player = Player.makePlayer()
      }
      
      
      // MARK: - Physics handling:
      extension GameScene {
      
        private func findJup(contact: SKPhysicsContact) -> JumpUnderPlatform? {
          guard let nodeA = contact.bodyA.node, let nodeB = contact.bodyB.node else { fatalError("how did this happne!!??") }
      
          if      nodeA.name == names.jup { return (nodeA as! JumpUnderPlatform) }
          else if nodeB.name == names.jup { return (nodeB as! JumpUnderPlatform) }
          else                            { return nil }
        }
      
        // Player is 2, platform is 4:
        private func doContactPlayer_X_Jup(platform: JumpUnderPlatform) {
      
          // Check if jumping; if not, then just land on platform normally.
          guard player.initialDY > 0 else { return }
      
          // Gives us the ability to pass through the platform!
          player.physicsBody!.collisionBitMask = BitMasks.playerCategory
      
          // Will push the player through the platform (instead of bouncing off) on first hit
          if player.platformToPassThrough == nil { player.pb.velocity.dy = player.initialDY }
          player.platformToPassThrough = platform
        }
      
      func _didBegin(_ contact: SKPhysicsContact) {
      
        // Crappy way to do bit-math:
        let contactedSum = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask
      
        switch contactedSum {
      
        case BitMasks.jupCategory + BitMasks.playerCategory:
            guard let platform = findJup(contact: contact) else { fatalError("must be platform!") }
            doContactPlayer_X_Jup(platform: platform)
      
          // Put your other contact cases here...
          // case BitMasks.xx + BitMasks.yy:
      
          default: ()
          }
        }
      }
      
      // MARK: - Game loop:
      extension GameScene {
      
        // Scene setup:
        override func didMove(to view: SKView) {
          physicsWorld.contactDelegate = self
          physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
          addChild(player)
        }
      
        // Touch handling: (convert to touchesBegan for iOS):
        override func mouseDown(with event: NSEvent) {
          // Make player jump:
          player.pb.applyImpulse(CGVector(dx: 0, dy: 50))
      
          // Reset player on label click (from sks file): 
          if nodes(at: event.location(in: self)).first?.name == names.resetLabel {
            player.position.y = frame.minY + player.size.width/2 + CGFloat(1)
          }
        }
      
        override func update(_ currentTime: TimeInterval) {
          player._update()
        }
      
        func didBegin(_ contact: SKPhysicsContact) {
          self._didBegin(contact)
        }
      
        override func didFinishUpdate() {
          player._didFinishUpdate()
        }
      }
      

      我希望这有帮助!

答案 2 :(得分:0)

嗯,上面的答案效果很好,但是答案很完整。 简单的答案是使用Platform effector 2D组件。它应用了各种“平台”行为,例如单向碰撞,消除了侧面摩擦/弹跳等。 请查看此Unity的官方tutorial,以获取更多许可。