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


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.