【发布时间】: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 中的第一个项目,我觉得这里有更好的解决方案,但我只是没有看到它。
-
Sensor和Handle有单独的SKPhysicsBodys。不同的群众,...不知道在你对Handle施加冲动的情况下会发生什么。 -
我在Game Development Stack Exchange 的帖子中添加了有关此方法的详细信息,这可能有助于澄清这个问题。
标签: swift sprite-kit uigesturerecognizer uipangesturerecognizer skphysicsbody