Arhn - архитектура программирования

Проблема с вращением вектора С# в кватернион

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

Однако у меня возникла проблема, так как я пытаюсь заставить объект стрелять пулями в направлении игрока. Объект использует чистую векторную математику для движения к игроку, но, учитывая то, как работают мои функции, пули должны использовать кватернионы.

Итак, если объект движется к игроку, как я могу сделать вращение кватерниона, которое может использоваться функцией пули для создания пуль, которые также движутся к игроку, а их источником является объект?

Моя функция для рисования объекта, следующего за игроком:

private void drawEnemy(GameTime gameTime)
{
    angle += 0.1f;

    velocity = Vector3.Normalize(xwingPosition - enemyPos) * 0.02f;

    enemyPos = enemyPos + velocity;
    enemyWorldMatrix = Matrix.CreateRotationY(angle) * Matrix.CreateTranslation(enemyPos);
    DrawObject(enemy, enemyWorldMatrix, viewMatrix, projectionMatrix);

    //enemyRotation = GetRotation(xwingPosition, enemyPos);

    //Quaternion additionalRot = Quaternion.CreateFromAxisAngle(new Vector3(0, -1, 0), -0.1f) * Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), -0.1f);
    //enemyRotation *= additionalRot;

    double currentTime = gameTime.TotalGameTime.TotalMilliseconds;
    if (currentTime - enemyLastBulletTime > 1000)
    {
        EnemyBullet newBullet = new EnemyBullet();
        newBullet.position = new Vector3(enemyPos.X + 0.2f, 71, enemyPos.Z + 0.2f);
        newBullet.t = 0.01f;
        newBullet.proportion = newBullet.t / 120;
        newBullet.playerPosition = xwingPosition;
        newBullet.enemyPosition = enemyPos;
        newBullet.currentAccel = 0.025f;
        enemyBulletList.Add(newBullet);

        enemyLastBulletTime = currentTime;
    }
}

Как видите, он использует векторную математику для отслеживания и перемещения к игроку.

Как видите, я пытаюсь создать пули. В пулях используется вращение кватерниона, которое, я надеюсь, создал правильно.

Моя функция для обновления позиций маркеров после их создания из первой написанной мной функции.

private void UpdateEnemyBulletPositions()
{
    for (int i = 0; i < enemyBulletList.Count; i++)
    {
        EnemyBullet currentBullet = enemyBulletList[i];
        currentBullet.t += 0.01f;
        currentBullet.position = enemyPos + (Vector3.Normalize(currentBullet.playerPosition - currentBullet.enemyPosition) * currentBullet.proportion);
        MoveForward(ref currentBullet.position, currentBullet.rotation, 0.04f + (currentBullet.currentAccel));
        enemyBulletList[i] = currentBullet;

        BoundingSphere bulletSphere = new BoundingSphere(currentBullet.position, 0.05f);
        CollisionType colType = CheckCollision(bulletSphere);
        if (colType != CollisionType.None)
        {
            enemyBulletList.RemoveAt(i);
            i--;

            if (colType == CollisionType.Target)
                gameSpeed *= 1.05f;
        }
    }
}

xwingPosition — текущая позиция игрока.

enemyPos — текущая позиция объекта, следующего за игроком.

Проблема показана в этом ролике на YouTube, который я только что подготовил...

http://www.youtube.com/watch?v=PnkCVKdfN9Y

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

13.10.2012

  • Я не уверен, что вам нужно что-то вращать, чтобы рассчитать траекторию пуль. Конечно, если ваши пули не имеют сферической формы, вам понадобится вращение, чтобы нарисовать их форму. Это твоя проблема? 13.10.2012
  • ради этого пули в основном просто вектор 3. разве мне не нужно вращение, чтобы указать, в каком направлении двигаться? 13.10.2012
  • Предположим, что игрок находится в позиции P, а противник в позиции E (P и E оба трехмерные векторы), тогда траектория будет функцией: T = E + t(P-E) с t в диапазоне от 0 до 1. 13.10.2012
  • Так что технически мне лучше не быть упрямым и модифицировать мою функцию UpdateEnemyBulletPositions(), чтобы вместо использования вращения для направления использовалась просто функция, которую вы упомянули? 13.10.2012
  • Допустим, я все равно хотел использовать четвертник. Как мне преобразовать нужную траекторию в четвертник, чтобы моя функция могла ее обработать? 13.10.2012
  • Ну, чтобы вычислить эту линейную траекторию, вам не нужно вращение. Отсутствие необходимости иметь дело с угловой математикой также ускорит ваш код. 13.10.2012
  • Итак, я попытался изменить его, сделав это currentBullet.Position = Vector3.Normalize(xwingPosition - вражескийПос) * 0.03f; Однако пули появляются там, где должны, а затем исчезают. 13.10.2012
  • (xwingPosition - вражескийПос) — это вектор, который указывает направление вашей пули. Вы должны умножить его на коэффициент в диапазоне от 0 до 1, чтобы оживить вашу пулю, и добавить его к вашему врагу, чтобы движение началось от врага (иначе оно началось бы с начала вашей системы координат). 13.10.2012
  • Хм. currentBullet.position = xwingPosition + (Vector3.Normalize(xwingPosition - вражеская позиция) * 0,03f); Кажется, это заставляет пули волшебным образом телепортироваться прямо к игроку без скорости между противником и игроком. 13.10.2012
  • не умножайте его на константу 0,03f, а сделайте его диапазоном от 0 до 1, чтобы оживить 13.10.2012
  • Хорошо, я изменил 0.03f на переменную угла из функции drawEnemy(). Однако все пули собираются в одну и ту же позицию и движутся как одна. Как мне их выделить? 13.10.2012
  • 0.03f не является угловой переменной. Это доля траектории, которую прошла пуля на пути от врага к игроку. У вас может быть более одного объекта маркера. Более высокие коэффициенты t будут показывать пули ближе к игроку: если t=0, пуля начинает свой путь. Если t=0,5, то это половина пути. Если t=1, то он попал в игрока. 13.10.2012
  • Как рассчитать т? Вышеупомянутая функция упомянула как t, так и T. 13.10.2012
  • T - результирующее положение пули. t — это коэффициент, который вы должны анимировать от 0 до 1, чтобы анимировать пулю. Если вы начнете с t = 0 и добавите к нему 1/60 (при 60 кадрах в секунду), ваша пуля достигнет игрока за одну секунду. 13.10.2012
  • Кажется, это работает нормально. Единственная проблема сейчас заключается в том, что линия пуль, идущих от врага к игроку, движется вместе с движением игроков, поэтому пули все равно всегда следуют за игроком. Мне это нужно, чтобы, как только они изначально появляются и направляются к игроку именно в это время, они продолжали направляться туда, где игрок БЫЛ, а не там, где игрок находится СЕЙЧАС. Требуется ли для этого статическая переменная или что-то еще? 13.10.2012
  • Это легко решить. В вашем объекте пули сохраните два вектора: один для начального положения врага и один для начального положения игрока. Скопируйте их при выстреле пули, а затем основывайте свои вычисления на этих, а не на их новых позициях. 13.10.2012
  • Но где бы я присвоил текущие позиции игроков и врагов переменным структуры Bullet. Этот метод вызывается каждую секунду, разве в него не будут записываться новые позиции? 13.10.2012
  • У каждой пули должна быть своя собственная переменная t, чтобы отслеживать, где она находится на траектории. Просто скопируйте векторы if(t==0), так как это точный момент выстрела пули. 13.10.2012
  • У меня все еще есть проблема, которую трудно объяснить, поэтому я собираюсь загрузить ее видео. По сути, пули сохраняют свое направление после создания и не продолжают тяготеть к игроку, что хорошо. Однако они продолжают создавать один за другим друг перед другом, пока не появится линия пуль, идущих от объекта противника к объекту игрока и даже мимо объекта игрока. Эти пули по-прежнему движутся к позиции игрока, но выстраиваются в длинную линию. 13.10.2012
  • Я прав в том, что это XNA? 13.10.2012
  • XNA — это игровая библиотека для C#. И да, это XNA 13.10.2012
  • Ну, ты должен перестать создавать пули. Кроме того, когда t==1, вы можете заставить свою пулю взорваться и исчезнуть. 13.10.2012
  • Но, например, первая пуля появляется при t == 0, следующая появляется при t == 0,1 и так далее. 13.10.2012
  • Я должен пойти на обед; через пару часов ;-) 13.10.2012
  • youtube.com/watch?v=PnkCVKdfN9Y 13.10.2012
  • Я думаю, проблема заключается в том, чтобы установить t на общее количество прошедших миллисекунд, а не на счетчик, начинающийся с 0. Дело в том, что я не знаю, как запускать счетчик с 0 каждый раз, когда производится пуля. 13.10.2012
  • Я расширил свой ответ. Я надеюсь, что это помогает 13.10.2012

Ответы:


1

Линейную траекторию между двумя точками E и P (трехмерные векторы, представляющие противника и игрока) можно рассчитать по следующей формуле:

T(t) = E + t*(P-E)

где t — коэффициент, меняющийся от 0 до 1 (для анимации пули).

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

  1. Рассчитайте направление вашей пули: D = P-E
  2. Вычислите его длину: len = sqrt(Dx^2 + Dy^2 + Dz^2). Где Dx — это координата x точки D и т. д., а ^2 означает квадрат
  3. Нормируйте вектор направления ND = (1/len)*D. (Просто разделите каждую координату на len)

Теперь в ND у вас будет вектор длины один, в направлении врага к игроку. Каждый раз, когда вы стреляете пулей, вы должны выделять структуру, в которой вы храните:

  • Текущий ND
  • Позиция, откуда была выпущена пуля (то есть E, текущая позиция врага)
  • Счетчик t = 0

В каждом цикле вашей игры для каждой "живой" пули (следующий псевдокод):

bullet.t += 1
if(bullet.t < maxNumberOfCyclesABulletLives){
  bPos = bullet.E + bullet.t * coefficient * bullet.ND // coefficient is the distance the bullet should move in each cycle
  if(bPos touches P){
    damageToPlayer()
    releaseTheStructureForThisBullet()
  }
  else drawBulletInPosition(bPos) // depending on your gaming library, you may have to erase it from the previous position
}
else{
  makeTheBulletExplode()
  releaseTheStructureForThisBullet() //or rather return it to a pool, where it can be reused
}

Кстати, ваша игра выглядит довольно круто. Я ожидаю бесплатную копию, как только вы ее опубликуете ;-)

13.10.2012
  • Итак, учитывая ошибку в этом видео, как вы думаете, где мой код работает неправильно? И я ничего не знаю о счетчиках... 14.10.2012
  • Я думаю, что вы, вероятно, не стираете свою пулю, когда перемещаете ее в новое место. Или вы можете создавать пули, а не уничтожать их. Под счетчиком я подразумеваю просто целочисленную переменную, которую вы увеличиваете на 1 в каждом цикле вашей игры. 14.10.2012
  • Проблема, похоже, в том, что каждая пуля появляется перед тем, где появилась предыдущая. Я трудился над кодом, и я не могу понять, почему. 14.10.2012
  • Может быть, вражеский космический корабль движется к игроку по направлению пули? Может быть, вы не начинаете новую пулю, устанавливая ее t = 0? 14.10.2012
  • Если t — это переменная, полученная из игрового времени, как мне сделать так, чтобы она всегда начиналась с 0? 14.10.2012
  • Это не производное от игрового времени. Он начинает считать всякий раз, когда пуля выпущена. У каждой пули должен быть свой экземпляр. Возможно, имя t сбивает с толку; просто назовите это как-то иначе. 14.10.2012
  • Ну, я создал newBullet.t = 0.01f; и newBullet.proportion = newBullet.t / 120; Который создается как переменная для каждой производимой пули. Затем t постепенно увеличивается при обновлении позиции маркера. Моя ошибка до сих пор не решена. Я обновил исходный psot своим текущим кодом на случай, если вы заметите очевидные ошибки. 14.10.2012
  • Я думаю, чего не хватает, так это той части, где вы убираете свою пулю из списка. Я бы сравнил newbullet.t с максимальным значением и, когда оно достигает его, не оказывая влияния на игрока, просто уничтожил бы пулю и удалил ее из списка. В противном случае вы создадите бесконечный поток пуль, которые будут путешествовать в бесконечное пространство. 14.10.2012
  • Я не возражаю, если пули летят в бесконечное пространство, меня беспокоит тот факт, что они создаются перед последней пулей, а не в месте последней пули. 15.10.2012
  • Это также проблема ресурсов: если вы не избавитесь от бесполезных пуль, ваш список в конечном итоге станет очень большим, и для его обработки потребуется время, которое замедлит вашу игру. В какой-то момент у вас также закончится память, и ваша игра вылетит. Ошибка в UpdateEnemyBulletPositions(). Третья строка внутри вашего оператора for должна быть currentBullet.position = currentBullet.enemyPos + ... В противном случае вы бы использовали положение врага сейчас, а не положение, которое было в момент выстрела пули. 15.10.2012
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..