物理速度导致 parent 与 child 分离

Physics velocity causes parent to separate from child

我正在尝试在玩家平移视图时通过编程 adding/deleting 纹理图块来制作无限滚动的地形。新瓷砖只能添加到具有开放边缘的现有瓷砖旁边。为了检测瓷砖是否有开放边缘,我计划附加一个从瓷砖的所有 4 个侧面伸出的小物理体作为传感器。如果传感器接触到任何其他传感器,我们就知道瓷砖的边缘没有打开。

我遇到的问题是传感器并不总是与瓷砖对齐。为了说明这个问题,我用下面的代码创建了一个 SpriteKit 项目。

触摸行为包括 GameScene class 中的手势识别器,它会导致不可见的 Handle object 移动。当手势结束时,我使用手柄的物理体在这条线上给它一点速度:

handle.physicsBody?.applyImpulse(CGVector(dx: velocity.x * multiplier, dy: -velocity.y * multiplier))

我还创建了一个 Tile object(下面的绿色大方块)并将其添加为隐形手柄的 child。太好了,现在我添加的所有 child 个图块都将与其 parent 手柄一起移动。

每当实例化一个图块时,都会创建一个 Sensor object(下面的红色小方块)并添加为图块的 child。这也很棒,现在所有传感器都将随着它们的 parent 方块一起移动,而方块又会随着它的 parent 隐形手柄一起移动。只有一个问题...

当我平移屏幕时,绿色方块及其红色传感器(如下所示)如预期一样一起移动。当我释放我的平移手势时,我给予手柄的额外速度踢也会转移到它的 child 方块,这也符合预期。但是该速度不会影响图块的 child 传感器。一旦我松开手势,传感器就会停在屏幕上,而磁贴会继续与手柄一起移动,直到它们都慢下来停止。期望的行为是传感器与其 parent 方块一起移动。

这是一个 link 视频,它可能比我描述的更清楚地展示了正在发生的事情: https://youtu.be/ccJKdZv-NsM

我不明白为什么图块与其 parent 的运动保持同步,但传感器却不同步。感谢您对此问题的任何见解。

GameScene.swift:

import SpriteKit

class GameScene: SKScene {
    let handle = Handle()
    let startTile = Tile()

    override func didMove(to view: SKView) {
        self.backgroundColor = .white
        self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)

        self.addChild(handle)
        startTile.position.x = handle.anchorPoint.x
        startTile.position.y = handle.anchorPoint.y
        handle.addChild(startTile)

        let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanFrom))
        panGestureRecognizer.cancelsTouchesInView = false
        panGestureRecognizer.delaysTouchesEnded = false
        self.view!.addGestureRecognizer(panGestureRecognizer)
    }

    func handlePanFrom(_ recognizer: UIPanGestureRecognizer) {
        if recognizer.state == .changed {
            var translation = recognizer.translation(in: recognizer.view)
            translation = CGPoint(x: translation.x, y: -translation.y)
            self.panForTranslation(translation)
            recognizer.setTranslation(.zero, in: recognizer.view)
        } else if recognizer.state == .ended {
            let velocity = recognizer.velocity(in: self.view)
            let multiplier = CGFloat(0.5)
            handle.physicsBody?.applyImpulse(CGVector(dx: velocity.x * multiplier, dy: -velocity.y * multiplier))
        }
    }

    func panForTranslation(_ translation: CGPoint) {
        let position = handle.position
        let newPosition = CGPoint(x: position.x + translation.x, y: position.y + translation.y)
        handle.position = newPosition
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        handle.physicsBody?.isResting = true
    }
}

句柄class:

import SpriteKit

class Handle : SKSpriteNode {
    init() {
        super.init(texture: nil, color: .clear, size: CGSize(width: 1, height: 1))
        self.physicsBody = SKPhysicsBody(rectangleOf: self.size)
        self.physicsBody?.mass = 1
        self.physicsBody?.linearDamping = 2
        self.physicsBody?.categoryBitMask = 0
        self.physicsBody?.contactTestBitMask = 0
        self.physicsBody?.collisionBitMask = 0
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

平铺class:

import SpriteKit

class Tile : SKSpriteNode {

    init() {
        super.init(texture: nil, color: .green, size: CGSize(width: 300, height: 300))
        let sensorA = Sensor()
        self.addChild(sensorA)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

传感器class:

import SpriteKit

class Sensor : SKSpriteNode {

    init() {
        super.init(texture: nil, color: .red, size: CGSize(width: 50, height: 50))
        self.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, height: 50))
        self.physicsBody?.categoryBitMask = 0b1
        self.physicsBody?.contactTestBitMask = 0b1
        self.physicsBody?.collisionBitMask = 0
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

更新: Whirlwind 提供的公认答案解决了我遇到的 child 与 parent 分离的问题。我相信问题的原因在那个答案的评论中已经很清楚了。

我的理解是红色方块没有移动,因为它有自己的物理体,在手柄停止移动后没有任何速度。虽然手柄 object(及其 child 方块)保持移动,因为它确实有速度。所以听起来像是红盒子自己的物理体在阻止它。

我真的没有时间去了解为什么你的代码会做一些事情,但如果你想移动另一个物理体连同手柄的物理体,那么你可以将它固定到它。

我只会修改你的代码使其工作,但你应该自己担心封装。首先让 Tile class 内部的传感器变量对外界可见,以便稍后在您的场景中使用它:

class Tile : SKSpriteNode {
    let sensorA = Sensor()
    init() {
        super.init(texture: nil, color: .green, size: CGSize(width: 300, height: 300))

        self.addChild(sensorA)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

然后在你的场景中将传感器固定到手柄上:

   let pin = SKPhysicsJointPin.joint(withBodyA: self.startTile.sensorA.physicsBody!, bodyB: self.handle.physicsBody!, anchor: CGPoint.zero)
   startTile.sensorA.physicsBody?.allowsRotation = false
   self.physicsWorld.add(pin)

我想这就是你想要的: