计算游戏对象的旋转速度,给定位置 C# Unity 的力
Calculating rotational speed of a GameObject, given a force at position C# Unity
我发现这个脚本 returns 旋转速度(考虑到施加力的位置和与质心的距离)。
public Vector3 ForceToTorque(Vector3 force, Vector3 position, ForceMode forceMode = ForceMode.Force)
{
Vector3 t = Vector3.Cross(position - body.worldCenterOfMass, force);
ToDeltaTorque(ref t, forceMode);
return t;
}
private void ToDeltaTorque(ref Vector3 torque, ForceMode forceMode)
{
bool continuous = forceMode == ForceMode.VelocityChange || forceMode == ForceMode.Acceleration;
bool useMass = forceMode == ForceMode.Force || forceMode == ForceMode.Impulse;
if (continuous) torque *= Time.fixedDeltaTime;
if (useMass) ApplyInertiaTensor(ref torque);
}
private void ApplyInertiaTensor(ref Vector3 v)
{
v = body.rotation * Div(Quaternion.Inverse(body.rotation) * v, body.inertiaTensor);
}
private static Vector3 Div(Vector3 v, Vector3 v2)
{
return new Vector3(v.x / v2.x, v.y / v2.y, v.z / v2.z);
}
在 2100 牛顿的作用下,我得到了 0.6 弧度(36 度)的旋转。
var test = rotationScript.ForceToTorque(shipFront.right * 2100, shipFront.position, ForceMode.Force);
Debug.Log(test + " " + test * Mathf.Rad2Deg);
// Above gives (0, 0.6, 0) and (0, 36.1, 0)
但是使用AddForceAtPosition
以相同的力旋转船我没有得到相同的结果
if (currTurn > 0) {
body.AddForceAtPosition(shipFront.right * 2100, shipFront.position, ForceMode.Force);
body.AddForceAtPosition(-shipBack.right * 2100, shipBack.position, ForceMode.Force);
} else if (currTurn < 0) {
body.AddForceAtPosition(-shipFront.right * 2100, shipFront.position, ForceMode.Force);
body.AddForceAtPosition(shipBack.right * 2100, shipBack.position, ForceMode.Force);
}
它没有给我每秒 36 度的旋转度 - 我通过计算完成 360 度旋转所需的时间进行了测试,据说它应该在 10 秒内完成,但只需要 10 秒才能旋转 ~90º。
第一个脚本中有很多我不明白的地方,比如大部分物理部分,但我没有看到它考虑到我的飞船质量 (body.worldCenterOfMass
?),这可能是是吗?
我需要这个,这样我就可以更精确地旋转我的飞船。
主要错误是混淆了加速度和速度。施加扭矩会导致 angular 加速度(弧度每秒 每秒),这是由您的 test
(36.1 deg / s^2
在 Y-axis).这不是 angular 速度,而是 rate-of-change,所以你不应该期待相同的结果。
(传给ForceToTorque
的力也只是所需力的一半。)
快速物理笔记 - 扭矩方程:
I
是 moment-of-inertia 张量 ,由上面的积分给出的 3x3 矩阵,涵盖 body 的所有质量元素。它的索引 i
和 j
显然是对称的,因此它是可对角化的(任何体面的线性代数书籍):
D
是body的主轴基中的M-of-I张量,R
是旋转从本金到当前基础的矩阵。 D
的对角线元素是向量 body.inertiaTensor
的值,这意味着 Unity 始终将 object 的主轴与世界轴对齐,并且我们始终有 I = D
.
因此要获得由扭矩引起的angular加速度:
最后一行由Div
执行。
确保准确旋转的更好方法是应用 angular 脉冲,它会直接改变 angular 速度。 Q
和对应的线性冲量P
都需要满足:
这直接改变了 body 的 angular 速度。 (*)
是输入参数必须满足的条件。您仍然可以将 AddForceAtPosition
与 ForceMode.Impulse
一起使用。代码:
Vector3 AngularvelocityToImpulse(Vector3 vel, Vector3 position)
{
Vector3 R = position - body.worldCenterOfMass;
Vector3 Q = MultiplyByInertiaTensor(vel);
// condition (*)
if (Math.Abs(Vector3.Dot(Q, R)) > 1e-5)
return new Vector3();
// one solution
// multiply by 0.5 because you need to apply this to both sides
// fixes the factor-of-2 issue from before
return 0.5 * Vector3.Cross(Q, R) / R.sqrMagnitude;
}
Vector3 MultiplyByInertiaTensor(Vector3 v)
{
return body.rotation * Mul(Quaternion.Inverse(body.rotation) * v, body.inertiaTensor);
}
Vector3 Mul(Vector3 v, Vector3 a)
{
return new Vector3(v.x * a.x, v.y * a.y, v.z * a.z);
}
申请:
var test = AngularvelocityToImpulse(...);
body.AddForceAtPosition(test, shipFront.position, ForceMode.Impulse);
body.AddForceAtPosition(-test, shipBack.position, ForceMode.Impulse);
我发现这个脚本 returns 旋转速度(考虑到施加力的位置和与质心的距离)。
public Vector3 ForceToTorque(Vector3 force, Vector3 position, ForceMode forceMode = ForceMode.Force)
{
Vector3 t = Vector3.Cross(position - body.worldCenterOfMass, force);
ToDeltaTorque(ref t, forceMode);
return t;
}
private void ToDeltaTorque(ref Vector3 torque, ForceMode forceMode)
{
bool continuous = forceMode == ForceMode.VelocityChange || forceMode == ForceMode.Acceleration;
bool useMass = forceMode == ForceMode.Force || forceMode == ForceMode.Impulse;
if (continuous) torque *= Time.fixedDeltaTime;
if (useMass) ApplyInertiaTensor(ref torque);
}
private void ApplyInertiaTensor(ref Vector3 v)
{
v = body.rotation * Div(Quaternion.Inverse(body.rotation) * v, body.inertiaTensor);
}
private static Vector3 Div(Vector3 v, Vector3 v2)
{
return new Vector3(v.x / v2.x, v.y / v2.y, v.z / v2.z);
}
在 2100 牛顿的作用下,我得到了 0.6 弧度(36 度)的旋转。
var test = rotationScript.ForceToTorque(shipFront.right * 2100, shipFront.position, ForceMode.Force);
Debug.Log(test + " " + test * Mathf.Rad2Deg);
// Above gives (0, 0.6, 0) and (0, 36.1, 0)
但是使用AddForceAtPosition
以相同的力旋转船我没有得到相同的结果
if (currTurn > 0) {
body.AddForceAtPosition(shipFront.right * 2100, shipFront.position, ForceMode.Force);
body.AddForceAtPosition(-shipBack.right * 2100, shipBack.position, ForceMode.Force);
} else if (currTurn < 0) {
body.AddForceAtPosition(-shipFront.right * 2100, shipFront.position, ForceMode.Force);
body.AddForceAtPosition(shipBack.right * 2100, shipBack.position, ForceMode.Force);
}
它没有给我每秒 36 度的旋转度 - 我通过计算完成 360 度旋转所需的时间进行了测试,据说它应该在 10 秒内完成,但只需要 10 秒才能旋转 ~90º。
第一个脚本中有很多我不明白的地方,比如大部分物理部分,但我没有看到它考虑到我的飞船质量 (body.worldCenterOfMass
?),这可能是是吗?
我需要这个,这样我就可以更精确地旋转我的飞船。
主要错误是混淆了加速度和速度。施加扭矩会导致 angular 加速度(弧度每秒 每秒),这是由您的 test
(36.1 deg / s^2
在 Y-axis).这不是 angular 速度,而是 rate-of-change,所以你不应该期待相同的结果。
(传给ForceToTorque
的力也只是所需力的一半。)
快速物理笔记 - 扭矩方程:
I
是 moment-of-inertia 张量 ,由上面的积分给出的 3x3 矩阵,涵盖 body 的所有质量元素。它的索引 i
和 j
显然是对称的,因此它是可对角化的(任何体面的线性代数书籍):
D
是body的主轴基中的M-of-I张量,R
是旋转从本金到当前基础的矩阵。 D
的对角线元素是向量 body.inertiaTensor
的值,这意味着 Unity 始终将 object 的主轴与世界轴对齐,并且我们始终有 I = D
.
因此要获得由扭矩引起的angular加速度:
最后一行由Div
执行。
确保准确旋转的更好方法是应用 angular 脉冲,它会直接改变 angular 速度。 Q
和对应的线性冲量P
都需要满足:
这直接改变了 body 的 angular 速度。 (*)
是输入参数必须满足的条件。您仍然可以将 AddForceAtPosition
与 ForceMode.Impulse
一起使用。代码:
Vector3 AngularvelocityToImpulse(Vector3 vel, Vector3 position)
{
Vector3 R = position - body.worldCenterOfMass;
Vector3 Q = MultiplyByInertiaTensor(vel);
// condition (*)
if (Math.Abs(Vector3.Dot(Q, R)) > 1e-5)
return new Vector3();
// one solution
// multiply by 0.5 because you need to apply this to both sides
// fixes the factor-of-2 issue from before
return 0.5 * Vector3.Cross(Q, R) / R.sqrMagnitude;
}
Vector3 MultiplyByInertiaTensor(Vector3 v)
{
return body.rotation * Mul(Quaternion.Inverse(body.rotation) * v, body.inertiaTensor);
}
Vector3 Mul(Vector3 v, Vector3 a)
{
return new Vector3(v.x * a.x, v.y * a.y, v.z * a.z);
}
申请:
var test = AngularvelocityToImpulse(...);
body.AddForceAtPosition(test, shipFront.position, ForceMode.Impulse);
body.AddForceAtPosition(-test, shipBack.position, ForceMode.Impulse);