Я разрабатываю трехмерный космический шутер в XNA как школьный проект (в основном это Астероиды в 3D с усилениями), и я работаю над реализацией крена, тангажа и рыскания относительно локальных осей корабля.(Я должен подчеркнуть: вращение не относится к осям абсолютного / мирового x, y и z.) К сожалению, я боролся с этим в течение последних нескольких недель.Google и мой неолитический мозг обезьяны подвели меня;Может быть, вы, люди, можете помочь!
Вот мои настройки:
Через ввод с клавиатуры у меня есть следующие переменные, готовые к работе:
- yawRadians, в котором хранятся нужныеотклонение от начальной позиции корабля
- pitchRadians, в котором хранится желаемый шаг вдали от начальной позиции корабля
- rollRadians, в котором хранится требуемый крен от начальной позиции корабля
Корабль также поддерживает свои собственные векторы передних, задних, правых, левых, верхних и нижних частей, которые используются как для вращения, так и для движения.(Различные ключи будут продвигать корабль вперед, назад и т. Д. Эта часть отлично работает.)
В конечном счете, я генерирую матрицу вращения mShipRotation, представляющую все вращения корабля, которые передаются на корабль.Метод рисования.
У меня проблема с самими вращениями.Разные решения, которые я пробовал, имели разные результаты.Вот что я сделал до сих пор:
Метод 1 - Yaw, Pitch и Roll относительно абсолютных / мировых осей x, y и z
Сначала я наивно пытался использовать следующее в методе обновления моего корабля:
qYawPitchRoll = Quaternion.CreateFromYawPitchRoll(yawRadians, pitchRadians, rollRadians);
vFront = Vector3.Transform(vOriginalFront, qYawPitchRoll);
vBack = -1 * vFront;
vRight = Vector3.Transform(vOriginalRight, qYawPitchRoll);
vLeft = -1 * vRight;
vTop = Vector3.Transform(vOriginalTop, qYawPitchRoll);
vBottom = -1 * vTop;
mShipRotation = Matrix.CreateFromQuaternion(qYawPitchRoll);
(vOriginalFront, vOriginalRight и vOriginalTop просто сохраняют начальную ориентацию корабля.)
Выше фактически работает безлюбые ошибки, за исключением того, что повороты всегда выполняются относительно осей x, y и z, а не относительно передних / задних / правых / левых / верхних / нижних векторов корабля.Это приводит к тому, что корабль не всегда рыдает и качается, как ожидалось.(В частности, рыскание вырождается в вращение, если вы наклонились вверх, чтобы корабль указывал на вершину. Это имеет смысл, поскольку рыскание в этом решении просто вращается вокруг оси вверх мира.)
Я слышал оМетод Quarternion.CreateFromAxisAngle, который звучит идеально.Я мог бы просто объединить три вращения кватернионов, по одному вокруг каждой локальной оси корабля.Что может пойти не так?
Метод 2 - Quaternion.CreateFromAxisAngle
Вот второй фрагмент кода, который я использовал в методе обновления моего корабля:
qPitch = Quaternion.CreateFromAxisAngle(vRight, pitchRadians);
qYaw = Quaternion.CreateFromAxisAngle(vTop, yawRadians);
qRoll = Quaternion.CreateFromAxisAngle(vFront, rollRadians);
qPitchYawAndRoll = Quaternion.Concatenate(Quaternion.Concatenate(qPitch, qYaw), qRoll);
vFront = Vector3.Normalize(Vector3.Transform(vOriginalFront, qPitchYawAndRoll));
vBack = -1 * vFront;
vRight = Vector3.Normalize(Vector3.Transform(vOriginalRight, qPitchYawAndRoll));
vLeft = -1 * vRight;
vTop = Vector3.Normalize(Vector3.Transform(vOriginalTop, qPitchYawAndRoll));
vBottom = -1 * vTop;
mShipRotation = Matrix.CreateFromQuaternion(qPitchYawAndRoll);
Вышеописанное работает отлично, если я делаю только один оборот за раз (рыскание, тангаж или крен), но если я объединяю более одного вращения одновременно, корабль начинает дико вращаться и указывать во многих разных направлениях, получая все больше и большедеформирован, пока не исчезнет полностью.
Я пробовал варианты выше, где я сначала применяю Pitch ко всем векторам, затем к Yaw, затем крену, но не повезло.
Iтакже пробовал использовать Матрицы напрямую, несмотря на опасения Gimbal Lock:
Метод 3: Матрицы
mShipRotation = Matrix.Identity;
mShipRotation *= Matrix.CreateFromAxisAngle(vRight, pitchRadians);
mShipRotation *= Matrix.CreateFromAxisAngle(vFront, rollRadians);
mShipRotation *= Matrix.CreateFromAxisAngle(vTop, yawRadians);
vFront = Vector3.Normalize(Vector3.Transform(vOriginalFront, mShipRotation));
vBack = -1 * vFront;
vRight = Vector3.Normalize(Vector3.Transform(vOriginalRight, mShipRotation));
vLeft = -1 * vRight;
vTop = Vector3.Normalize(Vector3.Transform(vOriginalTop, mShipRotation));
vBottom = -1 * vTop;
Не повезло;У меня такое же поведение.Одно вращение за раз нормально, но вращение вокруг нескольких осей приводило к тому же причудливому поведению вращения.
После некоторой блестящей отладки (читается как: слепая выдача переменных на консоль), я заметил, что Front / Right/ Верхние векторы медленно, со временем становились менее ортогональными друг другу.Я добавил нормализацию к векторам практически на каждом этапе пути, а также попытался вычислить новые векторы на основе перекрестных произведений, чтобы убедиться, что они всегда остаются перпендикулярными друг другу, но даже тогда они не были идеально ортогональными.Я предполагаю, что это связано с тем, что математика с плавающей запятой не совсем точна.
Обратите внимание, что я регенерирую матрицу mShipRotation при каждом методе обновления, поэтому она не может напрямую накапливать отклонения или неточности.Я думаю, что применение нескольких вращений Quarternion может накапливать ошибку (поскольку я могу сделать один поворот просто отлично), но мои попытки исправить это не сработали.
Короче:
- Я могу отлично наклонять / вращать / рыскать относительно мировых осей x, y и z.Это просто не то, что ожидал бы игрок, так как крен / качка / рыскание не связаны с кораблем, но с миром.
- Я могу катиться, наклоняться или рыскать вокруг локальных осей корабля (перед/ Back / Top / Bottom / Left / Right) просто отлично, но только по одному.Любая их комбинация приведет к тому, что корабль будет спирально и быстро деформироваться.
Я пробовал кватернионы и матрицы.Я пробовал предложения, найденные на разных форумах, но в конечном итоге не нашел работающего решения.Часто люди рекомендуют использовать Quaternion.CreateFromYawPitchRoll, на самом деле не понимая, что цель состоит в том, чтобы корабль вращался вокруг своих (постоянно меняющихся) осей, а не (фиксированных) мировых осей.
Есть идеи?Учитывая ситуацию, когда вам задают крен, угол наклона и рыскание относительно переднего, правого и верхнего векторов корабля, как бы вы пошли на создание матрицы вращения?