Родительский поворот для определенной дочерней позиции и поворота

#c# #unity3d #quaternions

#c# #unity3d #кватернионы

Вопрос:

У меня есть символ, который может поворачиваться только по оси Y, поэтому в основном влево и вправо, если up — это Vector3.up. У персонажа есть дочерний элемент, который является позицией, в которой появляются гранаты (его правая рука), и теперь я пытаюсь выяснить, как я могу рассчитать правильный поворот для родителя, чтобы дочерний элемент смотрел точно на цель, но только «горизонтально». Вращение родительского элемента по x и z должно быть равно 0. Есть ли способ сделать это? Это нормально, если дочерний элемент смотрит только в правильном направлении на «горизонтальной плоскости», но не смотрит вверх или вниз, чтобы идеально смотреть на цель.

Этот ответ почти делает то, что я хочу:http://answers.unity.com/comments/1339850/view.html

Единственная проблема заключается в том, что результирующее вращение представляет собой сочетание всех трех осей, а не просто вращение по оси y.

Ответ №1:

Я бы использовал тригонометрию для решения этой проблемы. Объяснение в комментариях:

 Vector3 rotAxis = Vector3.up;

// flatten child pos, target pos, char pos, and child forward.
Vector3 flatChildPos = Vector3.ProjectOnPlane(childTransform.position, rotAxis);
Vector3 flatTargetPos = Vector3.ProjectOnPlane(targetTransform.position, rotAxis);
Vector3 flatCharPos = Vector3.ProjectOnPlane(charTransform.position, rotAxis);
Vector3 flatChildForward = Vector3.ProjectOnPlane(childTransform.forward, rotAxis);

Vector3 flatCharToTarget = flatTargetPos-flatCharPos;
Vector3 flatChildToChar = flatCharPos-flatChildPos;

// Find the angle going from the direction of child to parent to the child's forward
// The sign will come in handy later
float childAngle = Vector3.SignedAngle(flatChildToChar, flatChildForward,
        rotAxis);
float absChildAngle = Mathf.Abs(childAngle);

// Find the distance between child and character:
float childDist = flatChildToChar.magnitude;

// Find the distance between character and target:
float targetDist = flatCharToTarget.magnitude;

// Consider the triangle made by character position, position of target, and
// desired child position. Use sin rule to find angle of target's corner
float targetAngle = Mathf.Rad2Deg * Mathf.Asin(
        Mathf.Sin(absChildAngle * Mathf.Deg2Rad)
        * childDist/targetDist);

// determine angle in parent's corner:
float desiredParentAngle = 180f - absChildAngle - targetAngle;

// Determine sign of angle at character from target to child. 
// It's the same as the sign of angle at child from char to target.
float sign = Mathf.Sign(childAngle);

// Consider the triangle made by character position, position of target, and 
// current child position. Determine the current signed angle in character corner.
float currentParentAngle = Vector3.SignedAngle(flatCharToTarget, -flatChildToChar, 
        rotAxis);

// Calculate the diff in angle needed to make the current triangle the desired one.
float diffAngle = desiredParentAngle * sign - currentParentAngle;

// Apply the diff in world space
Quaternion newRot = Quaternion.AngleAxis(diffAngle, rotAxis) * charTransform.rotation;
  

Интересно, что в некоторых ситуациях может быть два возможных поворота. Определение того, когда это так, и какой второй поворот остается в качестве упражнения для читателя. Подсказка: иногда существует несколько допустимых значений для targetAngle и desiredParentAngle также 😉

Комментарии:

1. @Lyxodius Ах, я забыл преобразовать между градусами и радианами. Теперь это должно сработать!!!

2. Вау, это действительно работает! Я совершенно поражен! Я работал над этим два дня, и, наконец, это работает! Большое вам спасибо!

Ответ №2:

Afaik вам просто нужно будет инвертировать дочернюю позицию localEulerAngles.y

localEulerAngles — это поворот смещения дочернего элемента в углах Эйлера относительно его родительского элемента. Таким образом, использование только y компонента уже сообщает вам о смещении дочернего элемента по отношению к его родительскому элементу.

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

 // as usual get the direction from child to target
var targetDirection = targetObject.transform.position - childObject.transform.position;

// flatten the direction by erasing any difference in the Y-axis
var targetDirectionFlat = Vector3.Scale(new Vector3(1,0,1), targetDirection).normalized;

// get the Y angle offset between parent and child 
var offset = -childObject.transform.localEulerAngles.y;

// rotate the parent to face the flattened direction minus the offset
// both are rotations around the Y axis only
parent.transform.rotation = Quaternion.LookDirection(targetDirectionFlat) * Quaternion.Euler(Vector3.up * offset);
  

Примечание: Набрано на смартфоне, но я надеюсь, что ides станет понятным

Комментарии:

1. Спасибо за ваш ответ! Кажется, это почти работает, но по какой-то причине возникает довольно существенная ошибка точности. imgur.com/QYlGxk8 Верхняя строка — это дочерний элемент. вперед, а нижний — parent.forward. Дочерний элемент. предполагается, что движение вперед должно проходить точно через центр цели на картинке.

2. @derHugo Я думаю, что вы допустили ту же ошибку, что и я, при моей первой попытке, поскольку я не учел, что локальная позиция для дочернего элемента отлична от нуля. Вам необходимо учитывать изменение положения дочернего элемента в мире в зависимости от поворота родительского элемента. Смотрите мой ответ о том, как я использовал trig для решения этой проблемы 🙂

3. @Ruzihm да, на самом деле это не было ошибкой ^^ Теперь я вижу из комментария, что OP хотел этого, но поскольку он заявил, что тот, который указан в URL, был почти целью, за исключением того, что он хотел игнорировать разницу в Y, я дал ответ, более основанный на предположении, что в этом случае имеет значение только вращение ^^ Теперь я вижу, что предположение было неверным: D