Поворот при помощи Rigidbody
Есть два объекта, один который мы поворачиваем при помощи Rigidbody и второй, чей угол поворота он должен повторить. Нужна помощь со скриптом, тот скрипт что я написал на данный момент, при некоторых положениях приводит к глюкам.
Вот часть кода:
private Vector3 SpeedFindAng()
{
Vector3 tragetRot;
Vector3 bodyRot;
Quaternion Quat = Traget.transform.rotation;
Vector3 XYZ = Vector3.zero;
//Получение угла
XYZ.x = Mathf.Atan2(2 * (Quat.w * Quat.x + Quat.y * Quat.z), 1 - 2 * (Quat.x * Quat.x + Quat.y * Quat.y));
if (2 * (Quat.w * Quat.y - Quat.z * Quat.x) >= 1)
{
if (2 * (Quat.w * Quat.y - Quat.z * Quat.x) < 0)
{
XYZ.y = -Mathf.PI;
}
}
else
{
XYZ.y = Mathf.Asin(2 * (Quat.w * Quat.y - Quat.z * Quat.x));
}
XYZ.z = Mathf.Atan2(2 * (Quat.w * Quat.z + Quat.x * Quat.y), 1 - 2 * (Quat.y * Quat.y + Quat.z * Quat.z));
if (XYZ.x < 0)
{
XYZ.x = 360 * Mathf.Deg2Rad + XYZ.x;
}
if (XYZ.z < 0)
{
XYZ.z = 360 * Mathf.Deg2Rad + XYZ.z;
}
XYZ.y = Traget.transform.rotation.eulerAngles.y * Mathf.Deg2Rad;
tragetRot = XYZ / Mathf.Deg2Rad;
Quat = Body.transform.rotation;
XYZ.x = Mathf.Atan2(2 * (Quat.w * Quat.x + Quat.y * Quat.z), 1 - 2 * (Quat.x * Quat.x + Quat.y * Quat.y));
if (2 * (Quat.w * Quat.y - Quat.z * Quat.x) >= 1)
{
if (2 * (Quat.w * Quat.y - Quat.z * Quat.x) < 0)
{
XYZ.y = -Mathf.PI;
}
}
else
{
XYZ.y = Mathf.Asin(2 * (Quat.w * Quat.y - Quat.z * Quat.x));
}
XYZ.z = Mathf.Atan2(2 * (Quat.w * Quat.z + Quat.x * Quat.y), 1 - 2 * (Quat.y * Quat.y + Quat.z * Quat.z));
if (XYZ.x < 0)
{
XYZ.x = 360 * Mathf.Deg2Rad + XYZ.x;
}
if (XYZ.z < 0)
{
XYZ.z = 360 * Mathf.Deg2Rad + XYZ.z;
}
XYZ.y = Body.transform.rotation.eulerAngles.y * Mathf.Deg2Rad;
bodyRot = XYZ / Mathf.Deg2Rad;
//Нахождение разницы
if (tragetRot.z - bodyRot.z < -180)
{
DeltaAng.z = 720 + (tragetRot.z - bodyRot.z);
}
else if (tragetRot.z - bodyRot.z > 180)
{
DeltaAng.z = (tragetRot.z - bodyRot.z) - 360;
}
if (tragetRot.y - bodyRot.y < -180)
{
DeltaAng.y = 720 + (tragetRot.y - bodyRot.y);
}
else if (tragetRot.y - bodyRot.y > 180)
{
DeltaAng.y = (tragetRot.y - bodyRot.y) - 360;
}
else
{
DeltaAng.y = tragetRot.y - bodyRot.y;
}
if (tragetRot.x - bodyRot.x < -180)
{
DeltaAng.x = 720 + (tragetRot.x - bodyRot.x);
}
else if (tragetRot.x - bodyRot.x > 180)
{
DeltaAng.x = (tragetRot.x - bodyRot.x) - 360;
}
else
{
DeltaAng.x = (tragetRot.x - bodyRot.x);
}
DeltaAng.x *= ModRot.x;
DeltaAng.y *= ModRot.y;
DeltaAng.z *= ModRot.z;
print(bodyRot + " "+ tragetRot);
print("U" + Quat.eulerAngles);
DeltaAng = (Mathf.Deg2Rad * DeltaAng);
SpeedRotXYZ = (DeltaAng / (Time.deltaTime * A)) * 0.02f;
return SpeedRotXYZ;
}
В этом коде объект(body) используя rigidbody повторяет угол поворота объекта(target).
Я вспомнил что написано в этом коде. Когда я проверял прошлые версии этого кода, где еще использовались только углы эйлера, я заметил, что при вращении по оси X там нет 180°, по этому я использовал формулы преобразования кватернионов в углы эйлера.
Ответы (4 шт):
отвечу не кодом.
у нас есть 2 квантериона. Мы их сравниваем что бы понять в какую сторону нужно поворачивать. Фактически сравниваем по каждой оси.
Как следствие у нас выйдет 4 переменных как результат сравнения.
Дальше мы по этих осях вычисляем будущее ускорение для поворота. Суть в том что чем больше разница - тем быстрее поворачиваем. НО! Нам нужно еще учесть что у нас должен быть порог при котором обьекты не будут задавать скорость(будут задавать нулевую скорость). Это нужно что бы избежать дерганья.
Ну и последний шаг -- задавание скорости при помощиRigidbody.MoveRotation.
Как альтернативное решение - можно задавать физический поворот одновременно двум обьектам.
Условно, если у тебя есть уже расчитанная скорость поворота одного из обьектов - применяй ее сразу на твоих 2 обьекта.
Это более оптимальное и простое решение, но оно может быть не всегда доступно.
Я бы советовал использовать Quaternion.ToEulerAngles() или Quaternion.ToEuler() или новый вариант transform.rotation.eulerAngles.
И просто проверять в своих переменных Vector3 нужный поворот, вычитать или добавлять нужные значения и преобразовать обратно.
private Vector3 SpeedFindAng()
{
Quaternion targetRot = Target.transform.rotation;
Quaternion bodyRot = Body.transform.rotation;
Quaternion DeltaAngQ = Quaternion.Euler(Vector3.zero);
//Нахождение разницы
DeltaAngQ = targetRot * Quaternion.Inverse(bodyRot);
if (DeltaAngQ.eulerAngles.x > 180)
{
DeltaAng.x = DeltaAngQ.eulerAngles.x - 360;
}
else
{
DeltaAng.x = DeltaAngQ.eulerAngles.x;
}
if (DeltaAngQ.eulerAngles.y > 180)
{
DeltaAng.y = DeltaAngQ.eulerAngles.y - 360;
}
else
{
DeltaAng.y = DeltaAngQ.eulerAngles.y;
}
if (DeltaAngQ.eulerAngles.z > 180)
{
DeltaAng.z = DeltaAngQ.eulerAngles.z - 360;
}
else
{
DeltaAng.z = DeltaAngQ.eulerAngles.z;
}
SpeedRotXYZ = DeltaAng;
return SpeedRotXYZ;
}
Всё намного проще:
public class Foo: MonoBehaviour {
[SerializeField] private float _rotationSpeed = 90;
private Transform _transform;
private IEnumerator _rotationCoroutine;
private void Awake () {
_transform = transform;
}
public void RotateTo (Vector3 target) {
// станавливаем корутину, если есть
if (_rotationCoroutine != null)
StopCoroutine(_rotationCoroutine);
// запускаем новую корутину
_rotationCoroutine = RotationCoroutine(target);
StartCoroutine(_rotationCoroutine);
}
private IEnumerator RotationCoroutine (Vector3 target) {
Vector3 baseRotation = _transform.eulerAngles;
// угол между нунешнем и целевым
float Angle = Vector3.Angle(baseRotation, target);
// время поворота
float RotationTime = Angle/_rotationSpeed;
float Timer = 0;
while (Timer < RotationTime) {
float Way = Timer/RotationTime;
_transform.localEulerAngles = Vector3.Lerp(baseRotation, target, Way);
yield return null;
Timer += Time.deltaTime;
}
_transform.localEulerAngles = target;
_rotationCoroutine = null;
}
}