物理速度导致 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)
我想这就是你想要的:
我正在尝试在玩家平移视图时通过编程 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)
我想这就是你想要的: