SpriteKit 以定义的角度停止旋转轮
SpriteKit stop spinning wheel in a defined angle
我有一个以 angular 速度 ω
旋转的纺车,不涉及加速度,使用 SpriteKit 实现。
当用户按下按钮时,我需要让轮子从当前角度 ∂0
开始慢慢减速,然后以指定的角度结束(我们称之为 ∂f
)。
我创建了一个与之关联的 mass
of 2
。
我已经尝试了 angularDamping
和 SKAction.rotate(toAngle: duration:)
但它们不符合我的需要 因为:
- 使用
angularDamping
我无法简单地指定我想要结束的角度 ∂f
。
- 使用
SKAction.rotate(toAngle: duration:)
我无法从当前旋转速度开始减速,而且它表现得不自然。
我尝试的唯一剩下的方法是使用 SKAction.applyTorque(duration:)
。
这听起来很有趣,但是 我在计算公式以获得要应用的正确扭矩时遇到了问题,尤其是对于车轮的惯性和半径。
这是我的方法:
我将起始 angular 速度 ω
设为:
wheelNode.physicsBody?.angularVelocity
.
我要从 wheelNode.physicsBody?.mass
中选择 mass
时间t
是常数10
(这意味着在10秒内我希望轮子减速到最终角度∂f
)。
我计算的减速度为:
let a = -1 * ω / t
惯性应为:let I = 1/2 * mass * pow(r, 2)
*。 (请参阅有关半径的注释)
然后,最后,我计算出要应用的最终扭矩为:let t = I * a
(注意与车轮当前 angular 速度相反)。
注意:
因为我不清楚如何得到轮子的半径我试着从抓取它两个来自:
wheelNode.physicsBody?.area
为 let r = sqrt(wheelNode.physicsBody?.area ?? 0 / .pi)
- 通过将像素转换为米作为 area documentation says。然后我有
let r = self.wheelNode.radius / 150
.
有趣:我获得了 2 个不同的值 :(
不幸的是 这种方法中的某些东西不起作用,因为到目前为止我不知道如何以指定的角度结束并且轮子无论如何也不会停止(或者力矩太大而向另一个方向旋转,或者力矩不够)。所以,施加的扭矩似乎也是错误的。
你知道实现我需要的结果的更好方法吗?这是正确的方法吗?如果是,我的计算有什么问题?
运动学让我很头疼,但是给你。我已经到了你可以输入旋转量的地方,当它减速到你指定的角度时,轮子会旋转那么多次。其他功能和扩展是为了保持代码相对 clean/readable。因此,如果您只想要一个巨大的混乱函数,请继续修改它。
• 确保节点的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 }
}
}
我有一个以 angular 速度 ω
旋转的纺车,不涉及加速度,使用 SpriteKit 实现。
当用户按下按钮时,我需要让轮子从当前角度 ∂0
开始慢慢减速,然后以指定的角度结束(我们称之为 ∂f
)。
我创建了一个与之关联的 mass
of 2
。
我已经尝试了 angularDamping
和 SKAction.rotate(toAngle: duration:)
但它们不符合我的需要 因为:
- 使用
angularDamping
我无法简单地指定我想要结束的角度∂f
。 - 使用
SKAction.rotate(toAngle: duration:)
我无法从当前旋转速度开始减速,而且它表现得不自然。
我尝试的唯一剩下的方法是使用 SKAction.applyTorque(duration:)
。
这听起来很有趣,但是 我在计算公式以获得要应用的正确扭矩时遇到了问题,尤其是对于车轮的惯性和半径。
这是我的方法:
我将起始 angular 速度
ω
设为:wheelNode.physicsBody?.angularVelocity
.我要从
wheelNode.physicsBody?.mass
中选择 时间
t
是常数10
(这意味着在10秒内我希望轮子减速到最终角度∂f
)。我计算的减速度为:
let a = -1 * ω / t
惯性应为:
let I = 1/2 * mass * pow(r, 2)
*。 (请参阅有关半径的注释)然后,最后,我计算出要应用的最终扭矩为:
let t = I * a
(注意与车轮当前 angular 速度相反)。
mass
注意:
因为我不清楚如何得到轮子的半径我试着从抓取它两个来自:
wheelNode.physicsBody?.area
为let r = sqrt(wheelNode.physicsBody?.area ?? 0 / .pi)
- 通过将像素转换为米作为 area documentation says。然后我有
let r = self.wheelNode.radius / 150
.
有趣:我获得了 2 个不同的值 :(
不幸的是 这种方法中的某些东西不起作用,因为到目前为止我不知道如何以指定的角度结束并且轮子无论如何也不会停止(或者力矩太大而向另一个方向旋转,或者力矩不够)。所以,施加的扭矩似乎也是错误的。
你知道实现我需要的结果的更好方法吗?这是正确的方法吗?如果是,我的计算有什么问题?
运动学让我很头疼,但是给你。我已经到了你可以输入旋转量的地方,当它减速到你指定的角度时,轮子会旋转那么多次。其他功能和扩展是为了保持代码相对 clean/readable。因此,如果您只想要一个巨大的混乱函数,请继续修改它。
• 确保节点的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 }
}
}