【问题标题】:Physics velocity causes parent to separate from child物理速度导致父母与孩子分开
【发布时间】:2017-05-18 12:49:42
【问题描述】:

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

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

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

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

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

每当实例化一个 tile 时,都会创建一个 Sensor 对象(下方的红色小方块)并将其添加为该 tile 的子项。这也很棒,现在所有传感器都将随着它们的父图块一起移动,而父图块又随着它的父图块移动,即不可见的手柄。只有一个问题...

当我平移屏幕时,绿色瓷砖和它的红色传感器(如下所示)一致移动,正如预期的那样。当我释放我的平移手势时,我给手柄的额外速度踢也会传递到它的子瓷砖,这也是预期的。但该速度不会影响瓷砖的子传感器。一旦我松开手势,传感器就会停止在屏幕上,而磁贴继续与手柄一起移动,直到它们都减速停止。所需的行为是让传感器继续与其父图块一起移动。

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

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

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 提供的公认答案解决了我在孩子与父母分离时遇到的问题。我相信问题的原因在那个答案的 cmets 中变得很清楚。

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

【问题讨论】:

  • 顺便说一句,如果有人知道一种更好的设计方法来制作这种无限滚动的地形,我会全力以赴。这是我在 SpriteKit 中的第一个项目,我觉得这里有更好的解决方案,但我只是没有看到它。
  • SensorHandle 有单独的 SKPhysicsBodys。不同的群众,...不知道在你对Handle施加冲动的情况下会发生什么。
  • 我在Game Development Stack Exchange 的帖子中添加了有关此方法的详细信息,这可能有助于澄清这个问题。

标签: swift sprite-kit uigesturerecognizer uipangesturerecognizer skphysicsbody


【解决方案1】:

我真的没有时间解释为什么你的代码会做一些事情,但是如果你想移动另一个物理体和手柄的物理体,那么你可以把它固定到它上面。

我只会修改您的代码以使其正常工作,但您应该自己担心封装。首先让 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)
    }
}

然后在您的场景中将传感器引脚连接到手柄:

   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)

我想这就是你想要的:

【讨论】:

  • 谢谢,它有效!我曾尝试在传感器上设置physicsBody?.pinned = true,但这也需要父母有一个我想避免的物理体。但是我忽略了使用关节。您的代码缺少的一行是在创建 pin 后调用self.physicsWorld.add(pin)。也许这似乎很明显,但像我这样的 SpriteKit 新手可能不得不寻找它。
  • @peacetype 是的,你是对的,新手可能会感到困惑......我在我的回答中添加了这一行。
  • 我暂时不回答这个问题;仍然希望有人能弄清楚为什么速度导致孩子分开。有一个解决方案很棒,但是当某些行为与我认为我理解的概念背道而驰时(即子节点与它们的父节点一起移动),它真的让我很困扰。
  • @peacetype 在这种情况下,子节点在物理模拟方面没有父节点。当你通过改变它的位置来移动手柄时,因为 startTile 是手柄的一个孩子,它会随着它一起移动。但是,当您停止移动手柄,并对手柄施加脉冲时,没有任何东西可以移动 startTile。因此你的错误。当然,您可能会等待其他人对此发表评论:),但这确实是一个完整的故事。我只是不知道你不知道为什么会这样。
  • 但在这种情况下,您释放手势后,磁贴会继续移动。如果我注释掉你的联合密码并运行项目,当我做一个平移手势时,瓷砖(绿色框)在我放手后继续移动,而传感器(红色框)保持静止。手柄是一个带有物理实体的不可见节点,绿色瓷砖附在其上。我唯一的想法是红色传感器不会与手柄的速度保持一致,因为它有自己的物理体,而绿色瓷砖没有。这是我在这里看到的唯一区别因素。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多