菜鸟教程小白 发表于 2022-12-13 15:30:10

ios - 试图制造我可以从下面跳过但可以降落在上面的平台。无法微调逻辑


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

<hr/>

<p>这是在游戏 scene.swift 的顶部在 didMove 之前声明的:</p>

<pre><code>private var JumpThroughPlatformObject = SKSpriteNode()
</code></pre>

<p>这是在 DidMove 中:</p>

<pre><code>if let JumpThroughPlatformObjectNode = self.childNode(withName: &#34;//jumpThroughPlatform1&#34;) as? SKSpriteNode {
      JumpThroughPlatformObject = JumpThroughPlatformObjectNode}
</code></pre>

<p>我引用平台以从 .sks 中获取它的高度,因为我的所有平台都将是相同的高度,我只需要从一个中获取它。</p>

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

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

<p>我写的这个 if 语句应该采用平台的 y 位置并将其高度的一半添加到它,因为 y 位置基于 Sprite 的中心,我认为这会将平台的碰撞放在它的顶部边界。</p>

<p>我对播放器做了同样的事情,但反过来。将玩家碰撞仅放在其边界的底部。但它不能完美地工作,我现在不知道为什么。</p>

<pre><code>if (JumpThroughPlatformObject.position.y + (JumpThroughPlatformObject.size.height / 2)) &gt; (player.position.y - (player.size.height / 2))
</code></pre>

<p>下面的函数给了我三个主要问题:</p>

<ol>
<li><p>我的玩家跳跃总是 dy = 80。如果我要跳到那个 position.y = 90 的平台,玩家跳跃的高峰会停在平台中间,但他会传送到而不是继续倒在地上。</p></li>
<li><p>如果我摔倒,平台的左右边缘仍然会与玩家完全碰撞</p></li>
<li><p>如果我的播放器在一个平台上,而我正上方有另一个播放器,则播放器无法跳过它。</p>

<p>设零:CGFloat = 0</p>

<pre><code>    if let body = player.physicsBody {
      let dy = player.physicsBody?.velocity.dy

      // when I jump dy is greater than zero else I&#39;m falling

       if (dy! &gt;= zero) {

         if (JumpThroughPlatformObject.position.y + (JumpThroughPlatformObject.size.height / 2)) &gt; (player.position.y - (player.size.height / 2)) {

                print(&#34; platform y: \(JumpThroughPlatformObject.position.y)&#34;)
                print (&#34;player position: \(player.position.y)&#34;)

                // 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
      }
    }
</code></pre> </li>
</ol>

<p>任何建议将不胜感激。这几天我一直在扯头发。</p>

<p>在 didBegin 和 didEnd 中编辑:</p>

<pre><code>   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! &gt; zero || body.node!.intersects(platform)) &amp;&amp; ((body.node?.position.y)! - player.size.height / 2 &lt; platform.position.y + platform.size.height / 2) {

                body.collisionBitMask &amp;= ~CollisionTypes.platform.rawValue

            }



    } else if contact.bodyB.node == player {
   //playerCollided(with: contact.bodyA.node!)
      isPlayerOnGround = true

      if (dy! &gt; zero || body.node!.intersects(platform)) &amp;&amp; ((body.node?.position.y)! - player.size.height / 2 &lt; platform.position.y + platform.size.height / 2) {

            body.collisionBitMask &amp;= ~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

    }
    }
}
</code></pre>

<p>添加我所做的,玩家不能再跳过平台。</p></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>这是我为 macOS 和 iOS 目标制作的项目的链接:<br/>
    <a href="https://github.com/fluidityt/JumpUnderPlatform" rel="noreferrer noopener nofollow">https://github.com/fluidityt/JumpUnderPlatform</a> </p>

<p>基本上,这一切都与</p>

<ol>
<li>检测平台碰撞</li>
<li>然后判断你的播放器是否在平台下</li>
<li>让您的玩家通过平台(然后登陆平台)</li>
</ol>

<p>--</p>

<p>SK Physics 使这有点复杂:</p>

<ol start="4">
<li><p>在碰撞检测中,您的播放器的 <code>.position.y</code> 或 <code>.velocity.dy</code>
可能已经更改为“假”状态,以满足上面的 #2 检查(意味着 #3 永远不会发生)。此外,您的播放器会在第一次接触时从平台反弹。</p></li>
<li><p>没有“自动”的方式来确定你的玩家何时完成通过物体(从而允许玩家降落在平台上)</p></li>
</ol>

<p>--</p>

<p>因此,为了让一切顺利进行,必须使用一点创造力和独创性!</p>

<hr/>

<h1>1:检测平台碰撞:</h1>

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

<p>我们将严重依赖 3 大位掩码、联系人、类别和碰撞:</p>

<p><em>(仅供引用,我不喜欢在物理中使用枚举和位数学,因为我是个叛逆的白痴)</em>:</p>

<pre><code>struct BitMasks {
static let playerCategory = UInt32(2)
static let jupCategory    = UInt32(4) // JUP = JumpUnderPlatform
}

override func didBegin(_ contact: SKPhysicsContact) {

// Crappy way to do &#34;bit-math&#34;:
let contactedSum = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask

switch contactedSum {

case BitMasks.jupCategory + BitMasks.playerCategory:
// ...
}
</code></pre>

<p>--</p>

<p>现在,你说你想使用 SKSEditor,所以我已经适应了你:</p>

<p> <a href="/image/qDEYu.png" rel="noreferrer noopener nofollow"><img src="/image/qDEYu.png" alt="enter image description here"/></a>
<a href="/image/yRY1j.png" rel="noreferrer noopener nofollow"><img src="/image/yRY1j.png" alt="enter image description here"/></a>
<a href="/image/BTi4E.png" rel="noreferrer noopener nofollow"><img src="/image/BTi4E.png" alt="enter image description here"/></a>
<a href="/image/LP8vY.png" rel="noreferrer noopener nofollow"><img src="/image/LP8vY.png" alt="enter image description here"/></a> </p>

<pre><code>// 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&#39;s like they all get RESET if you put them in here...
required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }   
}
</code></pre>

<p>--</p>

<h1>现在给玩家:</h1>

<pre><code>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() -&gt; 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
}
}
</code></pre>

<hr/>

<hr/>

<h1>2。 (并处理#4):确定是否在接触平台下:</h1>

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

<p>要实现这一点,我们必须掌握更多技术知识,因为物理系统和 SK 在其循环中的工作方式并不总是 100% 与我们<em>认为</em>应该工作的方式相吻合. </p>

<p>基本上,我必须为播放器创建一个 <code>initialDY</code> 属性,该属性在 <code>update</code></p> 中的每一帧中不断更新

<p><code>initialDY</code> 将为我们提供第一次接触平台所需的正确数据,让我们告诉我们更改碰撞掩码,并将玩家的 CURRENT dy 重置为首字母 dy(因此玩家不会反弹)。</p>

<hr/>

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

<p>要通过平台,我们需要使用 <code>collisionBitMasks</code>。我选择让玩家的碰撞 mask= 玩家的 categoryMask,这可能不是正确的做法,但它适用于这个演示。 </p>

<p>你会在 <code>didBegin</code> 中得到这样的魔法:</p>

<pre><code>// Check if jumping; if not, then just land on platform normally.
guard player.initialDY &gt; 0 else { return }

// Gives us the ability to pass through the platform!
player.pb.collisionBitMask = BitMasks.playerCategory
</code></pre>

<p>现在,处理 #5 将需要我们向我们的播放器类添加另一个状态。我们需要临时存储接触到的平台,以便我们可以检查播放器是否已成功完成通过平台(所以我们可以重置碰撞掩码)</p>

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

<p>这里是所有文件,还有一个github链接:<br/>
<a href="https://github.com/fluidityt/JumpUnderPlatform" rel="noreferrer noopener nofollow">https://github.com/fluidityt/JumpUnderPlatform</a> </p>

<hr/>

<hr/>

<h1>Player.swift:</h1>

<pre><code>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&#39;s didBeginContact (because current DY is altered by the time we need it)
var initialDY = CGFloat(0)
}

// MARK: - Funkys:
extension Player {
static func makePlayer() -&gt; 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() -&gt; Bool {
    guard let platform = platformToPassThrough else { fatalError(&#34;wtf is the platform!&#34;) }

    if frame.minY &gt; platform.frame.maxY { return true}
    else                              { return false }
}

func landOnPlatform() {
      print(&#34;resetting stuff!&#34;)
      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() }
    }
}
}
</code></pre>

<hr/>

<hr/>

<h1>JumpUnderPlatform 和 BitMasks.swift(分别:)</h1>

<pre><code>// 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)
}
</code></pre>

<hr/>

<hr/>

<h1>GameScene.swift:</h1>

<p>-</p>

<p>确保您的 SKS 编辑器中有两个节点:
<a href="/image/N6mfI.png" rel="noreferrer noopener nofollow"><img src="/image/N6mfI.png" alt="enter image description here"/></a>
<a href="/image/hbMPQ.png" rel="noreferrer noopener nofollow"><img src="/image/hbMPQ.png" alt="enter image description here"/></a> </p>

<p>-</p>

<pre><code>// MARK: - Props:
class GameScene: SKScene, SKPhysicsContactDelegate {

// Because I hate crashes related to spelling errors.
let names = (jup: &#34;jup&#34;, resetLabel: &#34;resetLabel&#34;)

let player = Player.makePlayer()
}


// MARK: - Physics handling:
extension GameScene {

private func findJup(contact: SKPhysicsContact) -&gt; JumpUnderPlatform? {
    guard let nodeA = contact.bodyA.node, let nodeB = contact.bodyB.node else { fatalError(&#34;how did this happne!!??&#34;) }

    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 &gt; 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(&#34;must be platform!&#34;) }
      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()
}
}
</code></pre>

<hr/>

<p>我希望这会有所帮助!</p></p>
                                   
                                                <p style="font-size: 20px;">关于ios - 试图制造我可以从下面跳过但可以降落在上面的平台。无法微调逻辑,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/44913398/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/44913398/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - 试图制造我可以从下面跳过但可以降落在上面的平台。无法微调逻辑