SpriteKit остановит вращающееся колесо под определенным углом - PullRequest
0 голосов
/ 25 января 2019

У меня есть вращающееся колесо, вращающееся с угловой скоростью ω, ускорение отсутствует, реализовано с помощью SpriteKit.

Когда пользователь нажимает кнопку, мне нужно медленно замедлить колесо с текущего угла ∂0 и в конечном итоге под заданным углом (назовем его ∂f).

Я создал связанный с ним mass из 2.

Я уже пробовал angularDamping и SKAction.rotate(toAngle: duration:) , но они не соответствуют моим потребностям , потому что:

  1. С помощью angularDamping я не могу легко указать угол ∂f, где я хочу оказаться.
  2. С SKAction.rotate(toAngle: duration:) я не могу начать замедляться с текущей скорости вращения, и она не ведет себя нормально.

Единственный оставшийся подход, который я попробовал, - это использование SKAction.applyTorque(duration:). Это звучит интересно, но У меня проблемы с вычислением формулы для получения правильного крутящего момента, применяемого и особенно для инерции и радиуса колеса.

Вот мой подход:

  • Я принимаю начальную угловую скорость ω как: wheelNode.physicsBody?.angularVelocity.

  • Я беру mass с wheelNode.physicsBody?.mass

  • Время t является константой 10 (это означает, что через 10 секунд я хочу, чтобы колесо замедлилось до конечного угла ∂f).

  • Замедление, которое я рассчитал как: let a = -1 * ω / t

  • Инерция должна быть: let I = 1/2 * mass * pow(r, 2)*. (см. примечания относительно радиуса, пожалуйста)

  • Затем, наконец, я рассчитал конечный крутящий момент для применения в виде: let t = I * a (с учетом противоположности текущей угловой скорости колеса).

Примечание:

Так как у меня нет ясного, как получить радиус колеса Я попытался схватить его оба из:

  1. wheelNode.physicsBody?.area as let r = sqrt(wheelNode.physicsBody?.area ?? 0 / .pi)
  2. путем преобразования пикселя в метры, как указано в области документации . Тогда у меня есть let r = self.wheelNode.radius / 150.

Забавно: я получаю 2 разных значения: (

НЕОБХОДИМОСТЬ что-то в этом подходе не работает, потому что до сих пор я не представляю, как оказаться в указанном угле, а колесо все равно не останавливается, как должно (или крутящий момент слишком велик) и вращается в другом направлении или не достаточно). Таким образом, применяемый крутящий момент также кажется неправильным.


Знаете ли вы лучший способ добиться нужного мне результата? Это правильный подход? Если да, что не так с моими расчетами?

1 Ответ

0 голосов
/ 27 января 2019

Кинематика заставляет мою голову болеть, но вот, пожалуйста.Я сделал это там, где вы можете ввести количество оборотов, и колесо будет вращаться столько раз, сколько его замедляется до указанного вами угла.Другая функция и расширение предназначены для того, чтобы сохранить код относительно чистым / читабельным.Поэтому, если вам нужна только одна гигантская функция беспорядка, измените ее.

• Убедитесь, что angularDampening узла = 0,0

• Убедитесь, что у узла есть круговое физическое тело

// Stops a spinning SpriteNode at a specified angle within a certain amount of rotations
//NOTE: Node must have a circular physicsbody
// Damping should be from 0.0 to 1.0
func decelerate(node: SKSpriteNode, toAngle: CGFloat, rotations: Int) {

    if node.physicsBody == nil { print("Node doesn't have a physicsbody"); return } //Avoid crash incase node's physicsbody is nil
    var cw:CGFloat { if node.physicsBody!.angularVelocity < CGFloat(0.0) { return -1.0} else { return 1.0} } //Clockwise - using int to reduce if statments with booleans

    let m = node.physicsBody!.mass // Mass
    let r = CGFloat.squareRoot(node.physicsBody!.area / CGFloat.pi)() // Radius
    let i = 0.5 * m * r.squared // Intertia

    let wi = node.physicsBody!.angularVelocity // Initial Angular Velocity
    let wf:CGFloat = 0 // Final Angular Velocity

    let ti =  CGFloat.unitCircle(node.zRotation)  // Initial Theta
    var tf = CGFloat.unitCircle(toAngle) // Final Theta

    //Correction constant based on rate of rotation since there seems to be a delay between when the action is calcuated and when it is run
    //Without the correction the node stops a little off from its desired stop angle
    tf -= 0.00773889 * wi //Might need to change constn

    let dt = deltaTheta(ti, tf, Int(cw), rotations)

    let a = -cw * 0.5 * wi.squared / abs(dt) // Angular Acceleration - cw used to determine direction
    print("A:\(a)")
    let time:Double = Double(abs((wf-wi) / a)) // Time needed to stop
    let torque:CGFloat = i * a // Torque needed to stop

    node.run(SKAction.applyTorque(torque, duration: time))
}

func deltaTheta(_ ti:CGFloat, _ tf:CGFloat, _ clockwise: Int, _ rotations: Int) -> CGFloat {

    let extra = CGFloat(rotations)*2*CGFloat.pi

    if clockwise == -1 {
        if tf>ti { return tf-ti-2*CGFloat.pi-extra }else{ return tf-ti-extra }
    }else{
        if tf>ti { return tf-ti+extra }else{ return tf+2*CGFloat.pi+extra-ti }
    }
}

}

extension CGFloat {

public var squared:CGFloat { return self * self }

public static func unitCircle(_ value: CGFloat) -> CGFloat {
    if value < 0 { return 2 * CGFloat.pi + value }
    else{ return value }
}
}
...