我试图通过在播放器平移视图时以编程方式添加/删除纹理贴图来制作无限滚动地形。只应在具有开放边缘的现有切片旁边添加新切片。为了检测瓷砖是否有开放边缘,我计划附加一个小的物理主体,从瓷砖的所有4个边伸出来充当传感器。如果传感器与任何其他传感器接触,我们就知道瓷砖的边缘没有打开。
我遇到的问题是传感器并不总是与瓷砖保持对齐。为了显示这个问题,我使用下面的代码创建了一个SpriteKit项目。
触摸行为包括GameScene
类中的手势识别器,该手势识别器会导致不可见的Handle
对象移动。当手势结束时,我使用手柄的物理体在这条线上给它一点速度:
handle.physicsBody?.applyImpulse(CGVector(dx: velocity.x * multiplier, dy: -velocity.y * multiplier))
我还创建了一个Tile
对象(下方的大绿色方块)并将其添加为隐形句柄的子项。这很棒,现在我添加的所有子图块都将与其父句柄一起移动。
每当实例化一个图块时,就会创建一个Sensor
对象(下方的小红色方块),并将其添加为图块的子图层。这也很棒,现在所有的传感器都会随着它们的父牌一起移动,而父牌又与其父牌即不可见的牌一起移动。只有一个问题......
当我平移屏幕时,绿色瓷砖及其红色传感器(如下所示)将按预期一致地一起移动。当我释放我的平移手势时,我给手柄的额外速度也会延伸到其子平铺,也如预期的那样。但该速度不会影响瓷砖的子传感器。一旦我释放手势,传感器在屏幕上停止死亡,同时瓷砖继续随手柄一起移动,直到它们都缓慢停止。所需的行为是传感器与其父图块一起移动。
我无法理解为什么瓷砖与其父母的动作保持同步,但传感器没有做同样的事情。感谢您对此问题的任何见解。
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提供的接受答案解决了我与孩子从父母分离时遇到的问题。我相信在答案的评论中,问题的原因变得清晰了。
我对它的理解是红色方块没有移动,因为它有自己的物理体,在手柄停止移动后没有接收任何速度。虽然句柄对象(及其子图块)保持移动,因为它确实具有速度。所以听起来像红盒子自己的物理机构正在把它拉回来。
答案 0 :(得分:2)
我真的没有时间介绍为什么你的代码会做一些事情,但如果你想将另一个物理体与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)
}
}
然后在你的场景针传感器到手柄:
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)
我想这就是你想要的: