Есть ли более элегантный / умный / краткий способ приблизить неизвестное число к 0?

В основном я занимаюсь перетаскиванием в игре с физикой. Боковое движение должно быть
уменьшено (как при приближении к 0, а не только в отрицательном), но я не знаю, в каком направлении он движется, поэтому я не знаю, добавлять ли перетаскивание или вычитать перетаскивание.

(Если предмет движется вправо, у него есть положительная сила, движущаяся влево, отрицательная сила)

Прямо сейчас я делаю это:

if (objects[i]->force.x > 0)
objects[i]->force.x -= drag;
else
objects[i]->force.x += drag;

Это прекрасно работает, но я чувствую, что для этого существует более изящный способ.

Также не помешает найти способ убедиться, что он не пройдет 0

3

Решение

Если вы пытаетесь подражать трению, самый простой способ — это знать скорость и использовать математическую модель, или дифференциальное уравнение приблизить это.

     dx
-c * --
dt

в основном сила трения пропорциональна скорости и в противоположном направлении. Таким образом, вы можете рассчитать силу трения (или сопротивления) просто следующим образом:

objects[i]->force = -drag_coefficient * objects[i]->velocity

Если вы не знаете скорость, вам будет трудно получить хорошие результаты, так как у вас может быть сила, толкающая вправо, пока объект движется влево (поэтому вы бы применили перетаскивание в неправильном направлении).

Редактировать:

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

force = -drag_coefficient * sign(velocity)

Однако в вычислительном отношении это становится нестабильным, когда скорость достигает 0, превышает ее и колеблется вперед и назад.

Еще одна модель для мокрое трение, это тот, который я использовал в первом примере:

force = -drag_coefficient * velocity

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

Ни одна из вышеперечисленных моделей не на 100% точна, но для игры их должно быть более чем достаточно

6

Другие решения

Перетаскивание должно быть функцией скорости: drag = r_1 * v где r_1 < 0, Это приведет к тому, что сила сопротивления будет противоположной скорости. Тогда вы можете просто добавить силу перетаскивания.

2

Во-первых, мы должны уточнить, что здесь представлено. Вы пишете «Если предмет движется вправо, у него есть положительная сила…» Обычно, если предмет движется вправо, мы ожидаем, что он будет иметь положительную скорость в этом направлении, а не обязательно положительную силу.

Если вы перетаскиваете твердый объект на твердую поверхность, то «идеальное» трение, противодействующее движению при перемещении, представляет собой силу, толкающую объект на поверхность (например, гравитацию), умноженную на коэффициент трения (в зависимости от используемых материалов). Деление этой силы на массу объекта вызывает ускорение. В ситуации, когда объект (включая его массу), материалы и перпендикулярное усилие не изменяются, вы можете рассчитать это ускорение один раз и считать его постоянным, a,

В непрерывной модели это ускорение плавно уменьшает скорость до нуля. В вашей модели вы, вероятно, используете дискретные временные шаги, некоторое количество времени t, Таким образом, на каждом нормальном шаге скорость уменьшается на ускорение, умноженное на время шага, a*t, Мы также можем рассчитать это и присвоить его некоторой переменной, скажем d,

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

if (velocity > d)
velocity -= d; // Resist rightward velocity for a full time step.
else if (velocity < -d)
velocity += d; // Resist leftward velocity for a full time step.
else
velocity = 0;  // Resist small velocity until it becomes zero.

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

velocity *= factor;

Одна из проблем заключается в том, что некоторые процессоры имеют очень низкую производительность для ненормальных чисел. Наиболее распространенная используемая арифметика с плавающей точкой, IEEE-754, задает интервал для очень маленьких чисел, которые обрабатываются специально (чтобы получить определенные математические свойства, которые помогают с доказательствами и предсказанием поведения). Выражение, такое как velocity *= factor; будет приводить к уменьшению или уменьшению чисел в отсутствие каких-либо других изменений, пока они не достигнут этого субнормального интервала. На уязвимых процессорах это может привести к резкому снижению производительности программы. Это, конечно, может испортить игровую физику. Некоторые системы устанавливают режим процессора, в котором субнормальные числа преобразуются в ноль. Это позволяет избежать проблем с производительностью, но нарушает стандарт IEEE-754. Это может быть приемлемо для игровой физики.

2

Если вы довольны своей моделью (отметив предостережения в комментариях), вы можете сократить выражение, используя сигнум функция. Хотя в C ++ выбор одного, по-видимому, несколько связан с ….

Какой бы вы ни выбрали, я буду ссылаться на него здесь как sgn(), Затем вы хотите, чтобы знак примененного перетаскивания был напротив знак force.x:

objects[i]->force.x += -1 * sgn(objects[i]->force.x) * drag;

(Я знаю, что я мог бы просто использовать унарный минус здесь; я думаю, что это проясняет намерение)

1
По вопросам рекламы [email protected]