Расчет скорости вращения GameObject с учетом силы в положении C # Unity - PullRequest
0 голосов
/ 06 июня 2018

Я нашел этот скрипт, который возвращает скорость вращения (учитывая, где применяется сила и расстояние от центра масс).

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?), может ли это быть?

Мне нужно это, чтобы я мог вращать свой корабльточнее.

1 Ответ

0 голосов
/ 06 июня 2018

Основной ошибкой была путаница между ускорением и скоростью .Применение крутящего момента приводит к угловому ускорению (радианы в секунду в секунду ), которое задается вашим test (36.1 deg / s^2 вокруг оси Y).Это не угловая скорость, а скорость изменения , поэтому не следует ожидать того же результата.

(Кроме того, сила, передаваемая на ForceToTorque, составляет только половинутребуемая сила.)


Быстрые физические замечания - уравнение крутящего момента:

enter image description here

I - это момент тензор инерции , матрица 3x3, заданная интегралом, приведенным выше, по всем элементам массы тела.Он явно симметричен в своих индексах i и j, поэтому он диагонализируемый (любая книга по приличной линейной алгебре):

enter image description here

D - это тензор M-of-I в главных осях тела базис, а R - матрица вращения от основного к текущему базису.Диагональные элементы D являются значениями вектора body.inertiaTensor, что означает, что Unity всегда выравнивает главные оси объекта с мировыми осями, и что у нас всегда есть I = D.

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

enter image description here

Где последняя строка выполняется Div.


AЛучший способ обеспечить точное вращение - это применить угловой импульс , который непосредственно изменяет угловую скорость.Q и соответствующий требуемый линейный импульс P удовлетворяют:

enter image description here

Это непосредственно изменяет угловую скорость тела.(*) - это условие, которому должны удовлетворять входные параметры.Вы все еще можете использовать 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);
...