一尘不染

用SKNode绕点恒速运行

swift

我的目标是使firstBody以恒定的速度(在本例中为500)处于“轨道” firstBody。

基于SpriteKit中的恒定运动,我实现了以下内容:

override func didMoveToView(view: SKView) {

    physicsWorld.gravity = CGVector(dx: 0, dy: 0)

    let firstNode = SKShapeNode(circleOfRadius: 10)
    addChild(firstNode)
    firstNode.position = CGPointMake(CGRectGetWidth(frame) / 2.0, CGRectGetHeight(frame) / 2.0)
    let firstPhysicsBody = SKPhysicsBody(circleOfRadius: 10)
    firstPhysicsBody.dynamic = false
    firstNode.physicsBody = firstPhysicsBody

    let secondNode = SKShapeNode(circleOfRadius: 10)
    addChild(secondNode)
    secondNode.position = CGPointMake(CGRectGetWidth(frame) / 2.0 + 40, CGRectGetHeight(frame) / 2.0 + 40)
    let secondPhysicsBody = SKPhysicsBody(circleOfRadius: 10)
    secondPhysicsBody.friction = 0
    secondPhysicsBody.linearDamping = 0
    secondPhysicsBody.angularDamping = 0
    secondPhysicsBody.affectedByGravity = false
    secondNode.physicsBody = secondPhysicsBody

    let joint = SKPhysicsJointPin.jointWithBodyA(firstPhysicsBody, bodyB: secondPhysicsBody, anchor: firstNode.position)
    joint.frictionTorque = 0
    physicsWorld.addJoint(joint)

    secondPhysicsBody.velocity = CGVector(dx: 0, dy: 500)
}

我遇到的问题是,随着时间的流逝,secondNode的速度正在放缓。你会发现,我设置各式各样的东西像gravitySKPhysicsWorldfriction
linearDampingangularDampingSKPhysicsBodyfrictionTorqueSKPhysicsJoint

那么,我在做什么错呢?以及如何在不进行可怕的计算的情况下保持secondNode的速度恒定-update

另外-我知道我可以添加SKAction来遵循循环路径,但是在这种情况下,这不是一个合理的解决方案。

如果我缺少一些简单的东西,也可以建议删除我设置的“ 0”和“ false”中的哪一个。

谢谢


阅读 203

收藏
2020-07-07

共1个答案

一尘不染

当与物理引擎打交道时,通常会放慢行为。它们并不完美,尤其是在约束等更复杂的场景中。您不应该依赖Sprite
Kit提供完美的连续运动仿真,因为我们不知道物理引擎在引擎盖下做什么。涉及的因素太多。

在需要连续运动的情况下(尤其是恒定的向心运动),最好总是自己计算并保存这些值。对于这种行为,在update方法中根本没有逃脱编写计算的麻烦。

因此,我写了一个快速的示例项目,计算出绕特定点运行所需的必要速度。您只需指定周期,轨道位置和轨道半径。请注意,由于我正在计算所有内容,因此不需要任何内容SKJoints,因此使此实现也更加轻巧。我强烈建议您以这种方式进行轨道运行,因为它可以让您完全控制节点彼此之间的运行方式(例如,可以让节点轨道移动的节点,可以具有椭圆形的轨道,可以在关键时刻调整轨道游戏中的瞬间等)

import SpriteKit

class GameScene: SKScene {
    var node1: SKShapeNode!
    var node2: SKShapeNode!
    var node2AngularDistance: CGFloat = 0

    override func didMoveToView(view: SKView) {
        physicsWorld.gravity = CGVector(dx: 0, dy: 0)
        node1 = SKShapeNode(circleOfRadius: 10)
        node1.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
        node1.physicsBody = SKPhysicsBody(circleOfRadius: 10)
        node2 = SKShapeNode(circleOfRadius: 10)
        node2.position = CGPoint(x: self.size.width/2.0+50, y: self.size.height/2.0)
        node2.physicsBody = SKPhysicsBody(circleOfRadius: 10)
        self.addChild(node1)
        self.addChild(node2)
    }
    override func update(currentTime: NSTimeInterval) {
        let dt: CGFloat = 1.0/60.0 //Delta Time
        let period: CGFloat = 3 //Number of seconds it takes to complete 1 orbit.
        let orbitPosition = node1.position //Point to orbit.
        let orbitRadius = CGPoint(x: 50, y: 50) //Radius of orbit.

        let normal = CGVector(dx:orbitPosition.x + CGFloat(cos(self.node2AngularDistance))*orbitRadius.x ,dy:orbitPosition.y + CGFloat(sin(self.node2AngularDistance))*orbitRadius.y);
        self.node2AngularDistance += (CGFloat(M_PI)*2.0)/period*dt;
        if (fabs(self.node2AngularDistance)>CGFloat(M_PI)*2)
        {
            self.node2AngularDistance = 0
        }
        node2.physicsBody!.velocity = CGVector(dx:(normal.dx-node2.position.x)/dt ,dy:(normal.dy-node2.position.y)/dt);
    }
    override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
        node1.position = (touches.first! as! UITouch).locationInNode(self)
    }
}

以下是显示向心运动的gif图像。注意,因为它是完全动态的,所以实际上我们可以在向心点旋转时移动它。

enter image description here

If however you really want to use your currentSKJoints implementation for
some reason then I have another solution for you.
Simply keep updating your
node’s linear velocity so that it never slows down.

override func update(currentTime: NSTimeInterval) {
        let magnitude = sqrt(secondNode.physicsBody!.velocity.dx*secondNode.physicsBody!.velocity.dx+secondNode.physicsBody!.velocity.dy*secondNode.physicsBody!.velocity.dy)
        let angle = Float(atan2(secondNode.physicsBody!.velocity.dy, secondNode.physicsBody!.velocity.dx))
        if magnitude < 500 {
            secondNode.physicsBody!.velocity = CGVector(dx: CGFloat(cosf(angle)*500), dy: CGFloat(sinf(angle)*500))
        }
    }

Regardless of the solution you pick (and once again I highly recommend the
first solution!), you can choose to apply the velocity over time rather than
instantaneously for a more realistic effect. I talk more about this in my
answer here

Hopefully I have provided you with enough information to resolve your issue.
Let me know if you have any questions. And best of luck with your game!

2020-07-07