【问题标题】:SpriteKit joint: follow the bodySpriteKit 关节:跟随身体
【发布时间】:2016-07-15 04:45:44
【问题描述】:

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

我在 SpriteKit 的物理关节(以及可能的物理身体属性)中苦苦挣扎。我尝试了每个子类和许多配置,但接缝好像没有任何效果,或者我做错了什么。

我正在开发贪吃蛇游戏。用户控制蛇头,它应该以恒定的速度向前移动,用户可以顺时针或逆时针转动它。所有剩余的蛇的碎片都应该跟随头部 - 它们应该沿着前一段时间头部的路径移动。

我认为对于这个游戏,Pin 关节应该是答案,哪个锚点恰好位于元素之间的中心。

不幸的是,结果并不完美。结构应该形成完美的圆圈,但事实并非如此。我附上代码和显示当前效果的 gif。有没有人有足够的经验给我任何建议,应该在这里应用身体和/或关节的哪些特性以获得预期的效果?

我的代码:

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
    }
}

【问题讨论】:

  • 你使用什么 Swift 版本?
  • @AlessandroOrnano 3

标签: ios sprite-kit physics skphysicsbody skphysicsjoint


【解决方案1】:

“所有剩余的蛇的碎片都应该跟随头部 - 它们应该沿着前一段时间头部的路径移动。”

您应该注意,对于物理关节,无论您做什么,都可能会出现差异。即使您将它接近完美,您也会在引擎盖下出现舍入错误,从而导致路径不准确。

如果所有尾巴部分都相等,您也可以使用不同的方法,这是我为彗尾所做的事情。基本上,这个想法是你有一个尾部对象数组,每帧移动将最后一个尾部对象始终移动到与头部对象相同的位置。如果头部对象的 z 位置较高,则在其下方绘制尾部。

如果您需要按顺序保持尾巴,您可以通过存储头部位置数组(每帧路径)来改变方法,然后在对蛇的每帧更新调用中沿着该路径放置尾部对象.

请参阅下面的代码,例如:

这些是你的头对象变量:

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

在你的 head 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() 函数中调用它时,产生的效果实际上非常平滑。

【讨论】:

  • 保持阵列与点和头旋转,然后将其应用于其他节点是可能的,但是我想尽可能多地利用游戏框架。该解决方案还需要对初始位置进行一些预计算。顺便说一句:我认为舍入误差是准确的。我希望蛇身跟随头部,几乎看不见的舍入误差对我来说是可以接受的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-05
  • 1970-01-01
相关资源
最近更新 更多