swift - 物理速度导致 parent 与 child 分离

标签 swift sprite-kit uigesturerecognizer uipangesturerecognizer skphysicsbody

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

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

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

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

我还创建了一个 Tile 对象(下方的绿色大方 block )并将其添加为不可见句柄的子项。太好了,现在我添加的所有子图 block 都将与其父图 block 一起移动。

每当实例化一个 tile 时,都会创建一个 Sensor 对象(下面的红色小方 block )并将其添加为 tile 的子项。这也很好,现在所有传感器都将与其父图 block 一起移动,而父图 block 又与其父图 block 一起移动,即隐形 handle 。只有一个问题...

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

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

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

Screenshot of result in scene

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
    }
}

句柄类:

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)
    }
}

瓷砖类:

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)
    }
}

传感器类:

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 分离的问题。我相信问题的原因在那个答案的评论中已经很清楚了。

我的理解是,红色方 block 没有移动,因为它有自己的物理体,在 handle 停止移动后,它没有接收任何速度。虽然 handle 对象(及其子图 block )保持移动,因为它确实具有速度。所以听起来像是红色盒子自己的物理体在阻止它。

最佳答案

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

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

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)
    }
}

然后在场景中将传感器固定到 handle 上:

   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)

我想这就是你想要的:

sensor

关于swift - 物理速度导致 parent 与 child 分离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44048257/

相关文章:

xcode - 错误 : value of tuple type '(NSString, NSString)' has no member '0'

ios - 如何在不妨碍缩放和滚动的情况下在 ScrollView 中的图像上绘制形状?

ios - 检测 UICollectionView 上的所有触摸

ios - touchIDLockout 在 iOS 11.0 中被弃用

ios - Alamofire JSON 编码 bool 问题?

ios - 在哪个线程上 [SKScene 更新 :] get called?

swift - 带有 swift 的 Sprite Kit 中的长按识别器

swift - 使用 SKAction 调用带参数的函数

ios - 检测用户是否点击了UIImageView

iphone - UIWebView 上的 TapGesture 与链接单击冲突