SpriteKit关节:跟随身体 [英] SpriteKit joint: follow the body

查看:115
本文介绍了SpriteKit关节:跟随身体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人要求我简化此问题,所以我正在这样做.

I've been asked to simplify this question, so that's what I'm doing.

我在SpriteKit的物理关节(以及可能的物理身体特性)中挣扎.我尝试了每个子类和许多配置,但是接缝无济于事,或者我做错了什么.

I'm struggling in SpriteKit's physic joints (and possibly physic body properties). I tried every single subclass and many configurations but seams like nothing works or I'm doing something wrong.

我正在开发Snake游戏.用户控制蛇的头部,蛇的头部应以恒定的速度向前移动,并且用户可以顺时针或逆时针旋转它.其余所有蛇的碎片都应该跟随头部-它们应该沿着与前一段时间完全相同的路径行进.

I'm developing Snake game. User controls head of snake which should move at constant speed ahead and user can turn it clockwise or anticlockwise. All the remaining snake's pieces should follow the head - they should travel exactly the same path that head was some time ago.

对于这个游戏,我认为Pin接头应该是答案,锚点恰好在元素之间的中心.

I think for this game the Pin joint should be the answer, which anchor point is exactly in the centre between elements.

不幸的是,结果并不完美.该结构应形成一个完美的圆,但不是.我附加了代码,并显示了当前效果的gif.有没有人有足够的经验给我任何建议,应该在此处应用物理身体和/或关节的哪些特性才能达到预期的效果?

Unfortunately the result is not perfect. The structure should make the perfect circle, but it doesn't. I'm attaching the code, and gif showing the current effect. Is anyone experience enough to give me any suggestion what properties of physic body and or joints should are apply here for desired effect?

我的代码:

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

推荐答案

其余所有蛇的碎片都应该跟随头部-它们应该沿着与头部一段时间相同的路径行进."

"All the remaining snake's pieces should follow the head - they should travel exactly the same path that head was some time ago."

您应该注意,使用物理关节时,无论您做什么,都可能会出现差异.即使您接近完美,您也会在引擎盖下出现舍入误差,从而使路径不精确.

You should note that with Physics joints you are likely going to have variance no matter what you do. Even if you have it close to perfect you'll have rounding errors under the hood making the path not exact.

如果所有的尾巴部分都相等,那么您也可以使用其他方法,这是我为彗星尾巴所做的.基本上,这个想法是,您有一个尾巴对象数组,并且每帧移动最后一个尾巴对象总是将其移动到与头部对象相同的位置.如果头部对象的z位置较高,则将在其下方绘制尾巴.

If all the tail parts are equal you can also use a different approach, this is something I've done for a comet tail. Basically the idea is that you have an array of tail objects and per-frame move move the last tail-object always to the same position as the head-object. If the head-object has a higher z-position the tail is drawn below it.

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

If you need to keep your tail in order you could vary the approach by storing an array of head-positions (per-frame path) and then place the tail objects along that path in your per-frame update call to the snake.

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

See my code below for example:

这些是您的主要对象变量:

These are you head-object variables:

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

在您的head init函数中实例化尾部对象:

In your head init function instantiate the tail objects:

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()函数中调用效果后,效果实际上真的很平滑.

The resulting effect is actually really smooth when you call it from the scene update() function.

这篇关于SpriteKit关节:跟随身体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆