Орієнтація моделі на ціль


28

У мене два об'єкти (ціль та гравець), обидва мають позицію (Vector3) та обертання (Quaternion). Я хочу, щоб ціль оберталася і була спрямована прямо на гравця. Мішень, коли щось стріляє, має стріляти прямо у гравця.

Я бачив безліч прикладів сперпінгу до гравця, але я не хочу поступового обертання, ну, гадаю, це було б нормально, доки я можу зробити серп на 100%, і поки він насправді спрацював.

FYI - Я в змозі використовувати положення і обертання, щоб зробити багато інших речей, і все це чудово працює, крім цього останнього твору, який я не можу зрозуміти.

Зразки коду виконуються в класі цілі, позиція = позиція цілі, аватар = гравець.

EDIT

Зараз я використовую c # код Maik, який він надав, і він чудово працює!


4
Якщо ви робите 100% -ний серп, ви не використовуєте slerp. Ви просто встановлюєте обертання таким чином 0*(rotation A) + 1*(rotation B)- іншими словами, ви просто встановлюєте обертання на обертання B довгим шляхом. Slerp призначений лише для визначення того, як має виглядати обертання (0% <x <100%) шляху між ними.
— doppelgreener

Гаразд, має сенс, але мета все ще не обертається повністю до гравця ... "довгий шлях" з цим кодом.
— Марк

Відповіді:


20

Існує більше ніж один спосіб зробити це. Ви можете обчислити абсолютну орієнтацію або поворот відносно вашого аватара, це означає, що ваша нова орієнтація = avatarOrientation * q. Ось останній:

  1. Обчисліть вісь обертання, взявши поперечний добуток вектора одиниці вперед вашого аватара та одиничний вектор від аватара до цілі, новий вектор вперед:

    vector newForwardUnit = vector::normalize(target - avatarPosition);
    vector rotAxis = vector::cross(avatarForwardUnit, newForwardUnit);
  2. Обчисліть кут повороту за допомогою крапкового добутку

    float rotAngle = acos(vector::dot(avatarForwardUnit, newForwardUnit));
  3. Створіть кватерніон за допомогою rotAxis та rotAngle та помножте його на поточну орієнтацію аватара

    quaternion q(rotAxis, rotAngle);
    quaternion newRot = avatarRot * q;

Якщо вам потрібна допомога в пошуку поточного вектора вперед для аватара, вхід для 1. просто стріляйте :)

EDIT: Обчислити абсолютну орієнтацію насправді трохи простіше, використовуйте вектор прямої матриці ідентичності замість вектора аватарів вперед як вхід для 1) та 2). І не помножуйте його на 3), замість цього використовуйте безпосередньо як нову орієнтацію:newRot = q


Важливо зазначити: Розчин має 2 аномалії, спричинені природою перехресного продукту:

  1. Якщо передні вектори рівні. Рішення тут - просто повернути кватерніон ідентичності

  2. Якщо вектори вказують точно в зворотному напрямку. Рішення тут полягає у створенні кватерніона, використовуючи аватари вгору по осі обертання та куту 180,0 градусів.

Ось реалізація в C ++, яка бере участь у цих крайніх випадках. Перетворити його на C # має бути просто.

// returns a quaternion that rotates vector a to vector b
quaternion get_rotation(const vector &a, const vector &b, const vector &up)
{   
    ASSERT_VECTOR_NORMALIZED(a);
    ASSERT_VECTOR_NORMALIZED(b);

    float dot = vector::dot(a, b);    
    // test for dot -1
    if(nearly_equal_eps_f(dot, -1.0f, 0.000001f))
    {
        // vector a and b point exactly in the opposite direction, 
        // so it is a 180 degrees turn around the up-axis
        return quaternion(up, gdeg2rad(180.0f));
    }
    // test for dot 1
    else if(nearly_equal_eps_f(dot, 1.0f, 0.000001f))
    {
        // vector a and b point exactly in the same direction
        // so we return the identity quaternion
        return quaternion(0.0f, 0.0f, 0.0f, 1.0f);
    }

    float rotAngle = acos(dot);
    vector rotAxis = vector::cross(a, b);
    rotAxis = vector::normalize(rotAxis);
    return quaternion(rotAxis, rotAngle);
}

EDIT: виправлена ​​версія коду XNA Марка

// the new forward vector, so the avatar faces the target
Vector3 newForward = Vector3.Normalize(Position - GameState.Avatar.Position);
// calc the rotation so the avatar faces the target
Rotation = Helpers.GetRotation(Vector3.Forward, newForward, Vector3.Up);
Cannon.Shoot(Position, Rotation, this);


public static Quaternion GetRotation(Vector3 source, Vector3 dest, Vector3 up)
{
    float dot = Vector3.Dot(source, dest);

    if (Math.Abs(dot - (-1.0f)) < 0.000001f)
    {
        // vector a and b point exactly in the opposite direction, 
        // so it is a 180 degrees turn around the up-axis
        return new Quaternion(up, MathHelper.ToRadians(180.0f));
    }
    if (Math.Abs(dot - (1.0f)) < 0.000001f)
    {
        // vector a and b point exactly in the same direction
        // so we return the identity quaternion
        return Quaternion.Identity;
    }

    float rotAngle = (float)Math.Acos(dot);
    Vector3 rotAxis = Vector3.Cross(source, dest);
    rotAxis = Vector3.Normalize(rotAxis);
    return Quaternion.CreateFromAxisAngle(rotAxis, rotAngle);
}

Гаразд, я зробив цей знімок, як ви можете бачити по моїй редакції у питанні, наданому кодом. Не дуже впевнений, у чому проблема. a і b входи - це вектори прямого руху, або, принаймні, припущення.
— Марк

@Marc дивись мою виправлену версію коду XNA у моїй відповіді. Були дві проблеми: 1) обчислення нового вектора вперед було неправильним, його потрібно нормалізувати AvatarPosition - TargetPosition 2) rotAxis має бути нормалізовано після перехресного продукту в GetRotation
— Maik Semder

@Marc, 2 незначні зміни: 3) джерело та призначення вже нормалізуються, не потрібно їх знову нормалізувати в GetRotation 4) не роби тест на абсолютний 1 / -1 в GetRotation, скоріше використовуй деяку толерантність, я використовував 0,000001f
— Maik Semder

Хм, це все ще не працює. Те ж саме масштабування відбувається з моделлю, і ціль не обертається до аватара (що я помітив у ваших коментарях, ви намагаєтесь повернути аватар до цілі .... повинно бути навпаки) ... в основному, намагаючись щоб отримати мафію до гравця (аватар у камері від третьої особи). Чи не повинен метод GetRotation щось знати про поточні повороти цілі та аватара, як, як ви вважаєте, чи створюється новий Forward належним чином?
— Марк

Якщо об'єкт масштабується, це означає, що кватерніон не має одиничної довжини, це означає, що rotAxis не нормалізується. Ви додали мою останню зміну коду з нормалізацією rotAxis? Однак, будь ласка, покажіть свій поточний код і, наприклад, у випадку, коли він не працює, також опублікуйте значення: newForward rotAngle rotAxisі returned quaternion. Код для мобу буде таким же, як тільки ми змусимо його працювати з аватаром, буде легко змінити заголовок будь-якого об’єкта.
— Майк Семдер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.