Unity движение объекта по траектории. Движение объекта к точке в Unity3D

Предположим, что у нас есть объект, который должен двигаться к точке. Задачка-то простенькая, использовать интерполяцию, например. Но что, если наш объект может поворачиваться на случайный угол? Как тогда задать точку для интерполирования? Ведь наверняка наша условная вагонетка должна двигаться только по направлению своих колес. Соответственно либо тыльной, либо фронтальной стороной. С этой задачей нам поможет справиться векторная алгебра.

Теория

Мы приняли факт, что с помощью векторной алгебры данная задача разрешима. Значит, нам необходимо что-то делать с векторами. Но что? Для начала спроецируем понятие вектора на нашу задачу. По условию задачи нам нужно задать точку для интерполяции. То есть точку, относительно глобальной/локальной системы координат, в которую будет впоследствии двигаться объект. Значит отрезок между точкой объекта, которую мы приняли для задания движения, и конечной искомой точкой будет являться вектором.

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

  • координаты начала вектора;
  • длина вектора;
  • угол наклона к осям координат.
Предположим, что все это мы знаем. Тогда задача сводится к простейшим формулам теоремы синусов.

$inline$x/sin(α) = a/sin (90)$inline$
$inline$ x = a* sin(α)$inline$
$inline$y/sin (90 - α) = a/sin(90)$inline$
$inline$ y = a * sin (90 - α)$inline$

Где a - длина вектора, α - угол наклона к оси координат

Собственно, этих знаний нам пока достаточно для решения задачи на практике.

Практика

Итак, нам известно:
  • где фронтальная и тыльная сторона объекта;
  • текущее положение объекта;
  • угол, на который повернулся объект.
  • расстояние, которое должен преодолеть объект.
Почти вся информация у нас есть, но согласно нашему случаю, нам численно известен угол поворота объекта, но неизвестно относительно какой оси он повернулся. Необходимы дополнительные данные. Тогда вводим понятие четверти. Ни для кого не секрет, что в двумерной декартовой системе координат существуют 4 четверти, с 1 до 4 соответственно. В каждой четверти оси имеют разные знаки.

И в unity это тоже работает. Для начала, нам нужно определить четверти в сцене. Создаем куб в начале координат, перемещаем его и смотрим, какие координаты отрицательные, а какие положительные. В примере видно, что обе координаты отрицательные, значит куб находится в третьей четверти.

Теперь можно приступать непосредственно к скрипту. На вход мы принимаем Transform исходного объекта после поворота и Transform пустышки, к которой в последствии будем двигаться. Пустышка изначально имеет координаты объекта. Далее определяем четверть, в которой находится фронт объекта. Так как тригонометрическая окружность ограничена от 0 до 360 градусов, то труда это не составляет. Определив четверть, вычисляем углы наклона для каждой координаты. Потом делаем проверку, чтобы наши углы имели правильный знак. После этого отправляем углы наклона в конечную формулу вычисления координат. И наконец, задаем новые координаты пустышке, к которой будем интерполировать.

Void ChekingQuarterUp(Transform vectorAngle, ref Transform empty) { float zangle = 0; float xangle= 0; float zcoord = 0.0f; float xcoord = 0.0f; int normangle = Mathf.RoundToInt (vectorAngle.eulerAngles.y); if (normangle >= 0 && normangle <= 90) // 1-ая четверть { zangle = 90 - normangle; xangle = 0 - normangle; xangle = (xangle < 0) ? xangle * -1: xangle; zangle = (zangle < 0) ? zangle * -1: zangle; } if (normangle > 270 && normangle <= 360) // 2-ая четверть { xangle = 360 - normangle; zangle = 270 - normangle; xangle = (xangle > 0) ? xangle * -1: xangle; zangle = (zangle < 0) ? zangle * -1: zangle; } if (normangle > 180 && normangle <= 270) // 3-ая четверть { xangle = 180 - normangle; zangle = 270 - normangle; xangle = (xangle > 0) ? xangle * -1: xangle; zangle = (zangle > 0) ? zangle * -1: zangle; } if (normangle > 90 && normangle <= 180) // 4-ая четверть { zangle = 90 - normangle; xangle = 180 - normangle; zangle = (zangle > 0) ? zangle * -1: zangle; xangle = (xangle < 0) ? xangle * -1: xangle; } zcoord = path * Mathf.Sin (zangle *Mathf.PI/180); xcoord = path * Mathf.Sin (xangle * Mathf.PI/180); float newpathx = empty.position.x + xcoord; float newpathz = empty.position.z + zcoord; empty.position = new Vector3 (newpathx, empty.transform.position.y, newpathz); }

Заключение

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

Для движения «задом» нужно всего лишь диаметрально изменить знаки координат согласно четвертям. Если вы решили определять точку в трехмерном пространстве, то учитывайте, что «четвертей» там будет больше.

Пример реализации метода можно взять

1

Я работаю над игрой, которая будет бросать снаряд, реагирующий на физические силы. Я использую встроенную физику Unity в игре, но я рисую траекторию снаряда до его запуска. Для этого я использую LineRenderer и простой класс физики я свернутый вычислить позиции:

Public class SlingPhysics { private Vector3 HalfGravity; private float ForceMultiplier; public void Init(Vector3 gravity, float slingForce, float mass, float drag) { HalfGravity = gravity/2.0f; ForceMultiplier = slingForce/mass/(1 + drag); } public Vector3 GetPosition(Vector3 start, Vector3 direction, float timeDelta) { Vector3 pos = direction * (timeDelta * ForceMultiplier); pos.y += (HalfGravity.y * timeDelta * timeDelta); return start + pos; } ///

/// /// Starting Position /// /// Time increment of the samples /// /// Buffer to fill with positions public int GetPositions(Vector3 startPos, Vector3 direction, float timeIncrement, float yFloor, Vector3 positions) { int maxItems = positions.Length; int i = 0; for (; i < maxItems; i++) { Vector3 pos = GetPosition(startPos, direction, timeIncrement * i); if (pos.y < yFloor) break; positions[i] = pos; } return i; } }

Это прекрасно работает и (несколько удивительно) точно соответствует фактической траектории, что снаряд в конечном итоге путешествия при запуске и управляется двигателем Unity Physics.

Но теперь мы решили ввести перетаскивание на Жесткое тело, и траектория уже не идеальна. Я мог бы продолжать пытаться изменить свою физическую модель в соответствии с тем, что делает Unity, но это похоже на проигрышную битву.

Есть ли способ прекомпретировать и провести траекторию с использованием движка Unity Physics?

Если нет, то как осуществляется перетаскивание и угловое перемещение в математике Unity?

Есть ли источник документации для реализации физики Unity"s (5.3.4)?

1

Я искал то же самое, но не удалось найти официальную документацию. Однако достаточно просто проверить это самостоятельно. Я не помню с головы, но я использовал жесткое тело с величиной перетаскивания и один без него и написал простой скрипт для имитации перетаскивания и попробовал пару типичных формул, и это оказалось одним из простые. Тогда вам все равно придется вручную вычислять все, чтобы соответствовать реализации Unity, потому что они не предлагают API для того, что вы хотите. - Xarbrough 01 июн. 16 2016-06-01 23:39:21

0

Xarbrough, спасибо. Я выслежу формулу перетаскивания и попробую попробовать, несмотря на советы мудреца программиста, чтобы избежать глупости преследовать Единство. Мой код очень прост, поэтому немного сложнее не убьет меня; надеюсь, он кончается там. И когда выйдет следующий великий двигатель физики, я перейду через этот мост. - ThisGuy 02 июн. 16 2016-06-02 05:37:29

  • 2 ответа
  • Сортировка:

    Активность

2

Способ вычисления перетаскивания в соответствии с движком Unity состоит в том, чтобы вычислять и отслеживать скорость в FixedUpdate, а затем обновлять скорость для Drag.

Это действительно просто; ключевая часть заключается в следующем:

Velocity *= Mathf.Clamp01(1f - Drag * Time.fixedDeltaTime);

Пользовательский класс физики должен быть переработан, чтобы потребовать, чтобы приращение времени быть Time.fixedDeltaTime Юнити (время между FixedUpdate вызовов)

Public class SlingPhysics { private Vector3 Gravity; private float Force, Mass, Drag; public void Init(Vector3 gravity, float force, float mass, float drag) { Gravity = gravity; Force = force; Mass = mass; Drag = drag; } ///

/// Computes an array of Trajectory object positions by time. /// /// Number of positions filled into the buffer /// Starting Position /// Direction (and magnitude) vector /// Minimum height, below which is clipped /// Buffer to fill with positions public int GetPositions(Vector3 startPos, Vector3 direction, float yFloor, Vector3 positions) { float timeIncrement = Time.fixedDeltaTime; int maxItems = positions.Length; int i = 0; Vector3 velocity = direction * Force/Mass; Vector3 pos = startPos; for (; i < maxItems; i++) { velocity += Gravity * timeIncrement; velocity *= Mathf.Clamp01(1f - Drag * timeIncrement); pos += velocity * timeIncrement; if (pos.y < yFloor) break; positions[i] = pos; } return i; } }

5

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

Нет, не делайте этого. Всякий раз, когда Unity обновляется, материал физики, как этот разрыв. Например, физики Единства 4 и 5 не являются одними и теми же. Должен был сильно изменить мой физический код Unity 4, чтобы он работал правильно.

Самое легкое решение вашей проблемы - не ничего не вычислять. Я делаю это, чтобы создать два снаряда . Projectile1 - главный снаряд, который можно увидеть и выстрелить. Projectile2 - скрытый снаряд, который не может быть замеченным игроком.

В режиме перетаскивания, стрелять Projectile2 (скрытый), а затем хранить его путь или положение (Vector3) он путешествовал через в List . Используйте этот сохраненный путь, чтобы нарисовать Projectile Trajectory .

Вам нужно только снять Projectile2 (спрятать) один раз и сохранить путь. Если во время режима перетаскивания, пальцевые, мыши или перетащить перемещаются в другую позицию, то вы должны ясный List , переснять Projectile2 (скрытые) еще раз, сохранить положение на List , а затем обновить Projectile Trajectory снова,