Как реализовать тракторную балку?

8

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

Привлечь объект к центру луча несложно. Но как только объект окажется достаточно близко к центру, мне нужно держать его там, пока игрок движется, с чем у меня проблемы. Я могу придумать два способа сделать это, и у обоих есть проблемы:

  1. Обновляйте позицию объекта всякий раз, когда позиция игрока изменяется, сохраняя его в центре луча.

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

Перемещение и вращение прекрасно работают с обоими подходами, но физика неверна, когда переносимый объект сталкивается с другими объектами:

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

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

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

Изменить: добавление псевдокода, чтобы проиллюстрировать, как он работает прямо сейчас (это будет второй подход выше)

void attract_object(object, ticks) {
    Vector distance = beam_center - object.center;
    // If the object is not close to the beam center, attract it slowly
    if (magnitude(distance) > 10) {
        object.velocity += distance.normalized() * ticks * 0.1;
        return;
    }

    // Here comes the part we're talking about. That magic 0.5 is just high enough
    // that the object isn't lost while moving around. But it's still so high that
    // other objects are repelled with way too much force.
    object.velocity = distance * ticks * 0.5;
}

Из того, что я вижу, это происходит, когда переносимый объект отталкивает другой объект:

  1. Несущийся объект сталкивается с другим объектом
  2. Скорости объектов распределяются правильно, поэтому переносимый объект отталкивается от центра балки в процессе
  3. Приведенный выше код заставляет переносимый объект возвращаться в центр балки с такой большой скоростью, что он быстро туда возвращается
  4. Когда переносимый объект движется обратно к центру луча, половина его высокой скорости передается другому объекту, сильно его отталкивая. Поскольку начальная скорость переносимого объекта кажется нормальной, я могу себе представить, что шаги с 2 по 4 повторяются многократно, создавая такую ​​высокую скорость.

Это кажется причиной. Я не могу придумать хороший способ исправить это :(

futlib
источник
1
Добро пожаловать на сложность термоядерного реактора Токомак. Вам нужно только построить рабочую математическую модель, а не рабочую магнитную бутылку, но математика идентична и нетривиальна. То, что вы пытаетесь сделать, выполнимо, но вам придется тщательно продумать свою математическую модель, прежде чем кодировать.
Питер Гиркенс

Ответы:

1

По сути, вы ищете, чтобы объект с «лучами» вел себя точно так, как если бы вы хватали его руками.
Один из вариантов - сделать так, чтобы он разделял скорости ускорения и ввода «руки», которая его удерживает, вместо того, чтобы регулировать ее скорость, чтобы заполнить промежуток центром балки.

Допустим, центром луча является удерживающая рука. если ваш персонаж поворачивается на 90 градусов влево за 1 секунду, то скорость руки будет равна:

If R = length of the arm: which is the radius of the rotation circle
R^2 *PI /4 would be the distance traveled over a second.
Сделайте это время elapsedTime кадра, чтобы найти скорость, которую вы должны применить к вашему объекту. Найдите горизонтальную нормаль к лучу, чтобы найти вектор направления.

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

Я бы поиграл с гравитационным пистолетом в HL2, чтобы получить вдохновение для решения этой проблемы, но у меня есть другие планы на сегодня.

РЕДАКТИРОВАТЬ: я извиняюсь, я думал, что это было для 3D лучевой пистолет, но это по сути то же самое с 2D, за исключением различий осей (и нет сложной геометрии)

icosamuel
источник
После того, как я попробовал различные хаки, это то, что помогло мне, результат выглядит неплохо. Скорость при столкновении все еще слишком высока, но я думаю, что могу это понять. Возможно, просто не притягивая объекты с высокой скоростью в другом направлении.
futlib
Возможно, когда «рука» сталкивается со стеной, вы можете вычислить ближайшую вероятную (то есть не сталкивающуюся) позицию для руки и использовать ее как «временную руку» во время столкновения. Мне часто нравится заниматься проблемами с другой точки зрения. Я рад, что смог помочь с этим;).
icosamuel
4

Как насчет добавления пружинного соединения, т. Е. Заставить перемещаемый объект вернуться в положение для переноски на основе расстояния, но все же позволить ему отталкиваться твердыми предметами (например, стенами).

Постоянно регулируйте скорость переносимого объекта (изменяя ускорение в зависимости от положения / расстояния), чтобы он указывал на положение луча трактора (т. Е. Ваш второй заход на посадку). Если объект отодвинут слишком далеко, разорвите соединение (и объект).

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


Изменить: Учитывая обновленный код, проблема довольно тривиально найти:

if (magnitude(distance) > 10) {
    object.velocity += distance.normalized() * ticks * 0.1;
    return;
}

Проблема здесь заключается в том, что расстояние до объекта до его цели постоянно слишком велико (то есть > 10). Пока это условие верно, его скорость просто увеличивается снова и снова (то есть до бесконечности).

Два возможных решения для этого:

Определите максимальную скорость:

object.velocity = min(object.velocity + distance.normalized() * ticks * 0.1, max_velocity);

Применяйте фиксированную скорость вместо ускорения:

object.velocity = distance.normalized() * ticks * magic_factor;

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

Марио
источник
Это то, что я описал в методе 2, не так ли? Это базовая весенняя физика AFAIK. Я добавлю код к вопросу выше, чтобы проиллюстрировать это.
Futlib
Также убрал часть о неправильной скорости выше, на самом деле все нормально, только что проверил. Так что это просто столкновения с другими объектами, которые перепутались.
futlib
Да, это по сути ваш второй подход. Обновление моего ответа.
Марио
Пробовал оба подхода, но это не помогает. Похоже, величина (расстояние)> 10 случая здесь не виновата. Я пытался ограничить скорость для случая <=, но это обычная проблема: скорость либо настолько низкая, что объект падает, либо настолько высокая, что она сильно отталкивает других.
futlib
«настолько высоко, что это сильно отталкивает других»: вам следует перемещать другие объекты на основе эффективной скорости, а не скорости за кадром (т.е. сбрасывать скорость из-за столкновения).
Марио