Есть ли способ использовать Quaternion.FromToRotation для использования одной оси? - PullRequest
2 голосов
/ 07 февраля 2020

Я боролся с этой проблемой слишком много часов, и пришло время попросить вас о помощи.

Ситуация

У меня движущийся персонаж Я иду вокруг и выравниваю с поверхностью, по которой он ходит. Я делаю это путем радиопередачи вниз на следующую поверхность, на которой он будет работать, и получая вращение, необходимое для выравнивания по поверхности с

Quaternion newRotation = Quaternion.FromToRotation(transform.up, foundSurface.normal) * transform.rotation;

Персонаж - это преобразование с именем Modelholder, которое плавно поворачивается к новым поворотам с помощью

modelHolder.rotation = Quaternion.Slerp(modelHolder.rotation, newRotation, Time.deltaTime * modelRotationSmoothing);

Внутри модели Holder находится модель. Я изменяю model.localRotation.y на основе движения челюсти мыши.

Камера - это просто преобразование, которое следует за держателем модели и имеет дочернее преобразование под названием Rotator, которое вращается на основе движения мыши (на этот раз челюсти и шага) , Камера - дитя этого преобразования Ротатора.

Благодаря этому я могу ходить по потолку и ходить, правильно выравнивая все, довольно аккуратно.

Задача

Я сделал захватный крюк, который перемещает персонажа на сцепленные поверхности. Перестройка в конце полета выполняется так же, как и при ходьбе. Это все работает нормально, за исключением случаев, когда вы цепляетесь от земли до потолка (или наоборот). Кажется, что модель выполняет "бочку", чтобы переместиться на новую поверхность, когда я смотрю на восток или запад, но при взгляде на север или юг она делает обратный или передний флип.

Движение ModelHolder: https://streamable.com/qf94k

Движение модели: https://streamable.com/4xkl4

Бочка хорошо катится, но я хочу как-то найти способ поворота, чтобы игрок выиграл после приземления не стоит смотреть в противоположном направлении (или прыгать с потолка, глядя на север или юг, потому что перестройка под действием силы тяжести также вызовет переворот), потому что переворот ужасно дезориентирует.

Вещи I ' я пробовал:

  • Я пытался не разделять модель и холдер. Это не решает проблему и только доставляет мне больше проблем с гладкой, но отзывчивой камерой.

  • Я попытался сохранить направление взгляда перед выравниванием и повернуть в старое направление взгляда во время выравнивания. Это просто супер странный поворот и поворот, что еще более дезориентирует.

  • Я пытался обнаружить различия в сравнении между старым и новым вращением, чтобы посмотреть, смогу ли я как-то «обнаружить», когда он хочет сделать бросок и когда он хочет сделать бросок ствола так что я могу противостоять этому. Я нашел только растерянность и разочарование.

1 Ответ

2 голосов
/ 07 февраля 2020

Вам необходимо вычислить newRotation, которое максимально поддерживает направление вперед, при этом локальный верх будет нормалью поверхности.

FromToRotation гарантирует только одну ось выравнивания в пути что ты это используешь. Вместо этого вы можете использовать перекрестные продукты и Quaternion.LookRotation, чтобы выполнить необходимые вычисления.

Vector3 newPlayerRight = Vector3.Cross(foundSurface.normal, modelHolder.forward);
Vector3 newPlayerForward = Vector3.Cross(newPlayerRight, foundSurface.normal);

Quaternion newRotation = Quaternion.LookRotation(newPlayerForward, foundSurface.normal);

Затем вы можете действовать, как раньше:

modelHolder.rotation = Quaternion.Slerp(modelHolder.rotation, newRotation, 
        Time.deltaTime * modelRotationSmoothing);

Хотя я не одобряю используя методы Slerp / Lerp с t, который, как гарантируют, никогда не достигнет или превысит 1. Я бы вместо этого рекомендовал использовать Quaternion.RotateTowards:

float modelRotationSpeed = 180f;
modelHolder.rotation = Quaternion.RotateTowards(modelHolder.rotation, newRotation, 
        Time.deltaTime * modelRotationSpeed);

, чтобы сохранить прямой угол относительно только что пройденный край, вы можете попробовать другой метод:

Quaternion newRotation;

// ..

Vector3 previousSurfaceNormal = modelHolder.up;
Vector3 previousForward = modelHolder.forward;
bool flyingOrFallingToNewSurface;

if (flyingOrFallingToNewSurface)
{
    Vector3 newPlayerRight = Vector3.Cross(foundSurface.normal, modelHolder.forward);
    Vector3 newPlayerForward = Vector3.Cross(newPlayerRight, foundSurface.normal);

    newRotation = Quaternion.LookRotation(newPlayerForward, foundSurface.normal);
} else
{
    // This direction lies in both surfaces. 
    Vector3 edgeTraversed = Vector3.Cross(previousSurfaceNormal, foundSurface.normal);

    // Find the angle from edgeTraversed to previousForward
    float ang = Vector3.SignedAngle(edgeTraversed, previousForward, previousSurfaceNormal);

    // Find newForward in new plane that's the same angle
    Vector3 newPlayerForward = Quaternion.AngleAxis(ang,foundSurface.normal) * edgeTraversed;

    newRotation = Quaternion.LookRotation(newPlayerForward, foundSurface.normal);
}

// ...

float modelRotationSpeed = 180f;
modelHolder.rotation = Quaternion.RotateTowards(modelHolder.rotation, newRotation, 
        Time.deltaTime * modelRotationSpeed);
...