【问题标题】:Constant speed orbit around point with SKNodeSKNode绕点等速轨道
【发布时间】:2015-02-28 21:10:13
【问题描述】:

我的目标是让 firstBody 以恒定速度(在本例中为 500)“环绕”firstBody。

基于Constant movement in 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 会变慢。你会注意到我在 SKPhysicsWorld 上设置了各种类型的东西,例如 gravityfriction linearDampingangularDampingSKPhysicsBodyfrictionTorqueSKPhysicsJoint

那么,我做错了什么?在-update 中进行可怕计算的情况下如何保持 secondNode 的速度不变?

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

如果我遗漏了一些简单的东西,您能否建议我设置的“0”和“假”中的哪一个可以删除。

谢谢

【问题讨论】:

  • 我对 Swift 不熟悉,但你为什么不检查和调整,如果需要,更新方法中的 dy 速度?
  • 似乎我与之交谈的每个人都在暗示同样的问题。我觉得这有点小题大做,我应该依靠物理引擎来完成繁重的工作,但似乎我不应该像我准备的那样信任它……

标签: swift sprite-kit


【解决方案1】:

在处理物理引擎时,通常会出现减速行为。它们并不完美,尤其是在约束等更复杂的场景中。您不应该依赖 Sprite Kit 来提供完美的连续运动模拟,因为我们不知道物理引擎在引擎盖下做了什么;涉及的因素很多。

如果您需要连续运动(尤其是像恒定向心运动),最好自己计算并保存这些值。对于这样的行为,您的更新方法中根本没有逃避写入计算。

所以我编写了一个快速示例项目,用于计算绕特定点运行所需的必要速度。您只需指定一个周期、轨道位置和轨道半径。请注意,因为我正在计算所有内容,所以不需要任何 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。请注意,因为它是完全动态的,我们实际上可以在向心点绕轨道移动时移动它。


如果您出于某种原因真的想使用当前的 SKJoints 实现,那么我有另一个解决方案供您使用。 只需不断更新节点的线速度,使其永远不会变慢.

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


无论您选择哪种解决方案(我再次强烈推荐第一个解决方案!),您可以选择随时间应用速度而不是瞬时应用以获得更逼真的效果。我在回答here 中更多地谈论这个

希望我已经为您提供了足够的信息来解决您的问题。如果您有任何问题,请告诉我。祝你游戏好运!

【讨论】:

  • 几乎是我期待 TBH 的答案,但我故意忽略了这个概念,希望物理模拟中有一些我错过的明显内容。谢谢!您是否对人们的建议或建议不要过于相信物理计算有任何引用或参考,因为这对我来说完全是新闻,而且似乎太重要而不能正确记录......
  • 我遇到了一些麻烦,直到我意识到 dt 假设帧速率为 60 FPS,如果你的帧速率发生变化,这些计算将被关闭。您可以通过跟踪当前时间并使用它来计算差异来解决此问题:let deltaTime = currentTime - lastUpdateTime;让 currentFPS = 1 / deltaTime;上次更新时间 = 当前时间; let dt: CGFloat = 1.0/CGFloat(currentFPS) //Delta Time;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-11
  • 1970-01-01
  • 2012-02-08
  • 2018-09-21
相关资源
最近更新 更多