SpriteKit联合:跟随身体

我被要求简化这个问题,所以这就是我正在做的事情。

我在SpriteKit的物理关节中挣扎(可能还有物理属性)。 我试过每一个子类和许多configuration,但接缝没有任何作品,或者我做错了什么。

我正在开发蛇游戏。 用户可以控制蛇头以恒定的速度向前移动,用户可以顺时针或逆时针旋转。 所有剩余的蛇的碎片应该跟着头 – 他们应该旅行完全相同的path,头一段时间前。

我认为对于这个游戏来说,拼接点应该是答案,哪个锚点恰好在元素之间的中心。

不幸的是结果并不完美。 结构应该是完美的圆形,但不是。 我附上代码,显示当前效果的gif。 有没有人有足够的经验给我任何build议什么属性的身体和/或关节应该在这里适用于预期的效果?

在这里输入图像说明

我的代码:

class GameScene: SKScene { private var elements = [SKNode]() override func didMove(to view: SKView) { physicsWorld.gravity = CGVector(dx: 0, dy: 0) let dummyTurnNode = SKNode() dummyTurnNode.position = CGPoint(x: size.width / 2 - 50, y: size.height / 2) let dummyTurnBody = SKPhysicsBody(circleOfRadius: 1) dummyTurnBody.isDynamic = false dummyTurnNode.physicsBody = dummyTurnBody addChild(dummyTurnNode) for index in 0..<5 { let element = SKShapeNode(circleOfRadius: 10) let body = SKPhysicsBody(circleOfRadius: 10) body.linearDamping = 0 // body.mass = 0 element.physicsBody = body element.position = CGPoint(x: size.width / 2, y: size.height / 2 - 30 * CGFloat(index)) elements.append(element) addChild(element) let label = SKLabelNode(text: "A") label.fontSize = 10 label.fontName = "Helvetica-Bold" element.addChild(label) if index == 0 { element.fillColor = UIColor.blue() body.velocity = CGVector(dx: 0, dy: 30) let dummyTurnJoint = SKPhysicsJointPin.joint(withBodyA: dummyTurnBody, bodyB: body, anchor: dummyTurnNode.position) physicsWorld.add(dummyTurnJoint) } else { body.linearDamping = 1 element.fillColor = UIColor.red() let previousElement = elements[index - 1] let connectingJoint = SKPhysicsJointPin.joint(withBodyA: previousElement.physicsBody!, bodyB: body, anchor: CGPoint(x: size.width / 2, y: size.height / 2 - 30 * CGFloat(index) + CGFloat(15))) physicsWorld.add(connectingJoint) } } } override func update(_ currentTime: TimeInterval) { let head = elements.first!.physicsBody! var velocity = head.velocity velocity.normalize() velocity.multiply(30) head.velocity = velocity } } extension CGVector { var rwLength: CGFloat { let xSq = pow(dx, 2) let ySq = pow(dy, 2) return sqrt(xSq + ySq) } mutating func normalize() { dx /= rwLength dy /= rwLength } mutating func multiply(_ factor: CGFloat) { dx *= factor dy *= factor } } 

“剩下的蛇都应该跟着脑袋 – 它们应该和前一段时间走的路一模一样。”

你应该注意到,无论你做什么,物理关节都可能会有变化。 即使你已经接近完美,你也会在引擎盖下发现四舍五入的错误,使path不准确。

如果所有的尾巴部分都是相同的,你也可以使用不同的方法,这是我为彗星尾巴做的事情。 基本上这个想法是,你有一个尾巴对象的数组,每帧移动最后一个尾巴对象总是移动到头对象相同的位置。 如果头部物体具有较高的z位置,则尾部被拉到下方。

如果你需要保持尾巴的顺序,你可以通过存储头部arrays(每帧path)来改变方法,然后沿着这个path将尾部对象放到你的每帧更新调用中。

例如,看我的代码:

这些是你的头对象variables:

 var tails = [SKEmitterNode]() var tailIndex = 0 

在你的头init函数实例化尾部对象:

 for _ in 0...MAX_TAIL_INDEX { if let remnant = SKEmitterNode(fileNamed: "FireTail.sks") { p.tails.append(remnant) } } 

调用下面的每帧:

 func drawTail() { if tails.count > tailIndex { tails[tailIndex].resetSimulation() tails[tailIndex].particleSpeed = velocity() / 4 tails[tailIndex].emissionAngle = zRotation - CGFloat(M_PI_2) // opposite direction tails[tailIndex].position = position tailIndex = tailIndex < MAX_TAIL_INDEX ? tailIndex + 1 : 0 } } 

从场景update()函数中调用时,效果实际上非常平滑。