В настоящее время я пишу некоторые функции для решения упругих столкновений в C ++. Я столкнулся с камнем преткновения, когда столкнулся с движущимся объектом, сталкивающимся с неподвижным объектом, потому что вычисления требуют, чтобы неподвижный объект имел бесконечную массу.
Я знаю что std::numeric_limits
обеспечивает infinity()
но я не уверен, что это полностью полезно в этом случае, поскольку, насколько мне известно, это просто максимальное число, возможное в представлении с плавающей запятой. В следующем коде, если aabb_obj
имеет массу, равную std::numeric_limits<double>::max()
кажется, что каждый расчет, который использует его, либо приведет к std::numeric_limits<double>::max()
или же 0
,
double t;
if (intersect_moving_circle_aabb(circle_obj, aabb_obj, t))
{
circle_obj->position += circle_obj->velocity * t;
vec2 closest_point;
closest_pt_point_aabb(circle_obj->position, aabb_obj, closest_point);
vec2 n = (closest_point - circle_obj->position).normalized();
double a = dot_product(circle_obj->velocity, n);
double p = (2.0 * a) / (circle_obj->mass + aabb_obj->mass);
circle_obj->velocity = circle_obj->velocity - p * aabb_obj->mass * n;
}
Это для игры, поэтому результаты не должны быть на 100% физически точными, просто «достаточно хорошими». Каков рекомендуемый способ представления бесконечности в таких вычислениях? Я просто выбираю произвольно большое число?
При использовании чисел с плавающей запятой IEEE эти две бесконечности уже специально обрабатываются аппаратными средствами вместе со значениями Not-A-Number (NAN). И они подчиняются нормальным математическим правилам:
1/0 = +inf
+inf + x = +inf
-inf + +inf = NAN
x/+inf = 0
0*+inf = NAN
...
Теперь вы можете проверить свой код, чтобы увидеть, что произойдет, если вы подключите +inf
для одной из масс: это даст значение p = 0
и в следующей строке p * aabb_obj->mass
либо уступит 0
если aabb_obj->mass
конечно, или NAN
если это бесконечно. Последний случай — это тот, который вам нужно избегать, потому что NAN
будет распространяться через другие операции, давая circle_obj->velocity = NAN
,
Если бы я был тобой, я бы сделал так, чтобы некоторые объекты были неподвижны в моем коде. Например, вместо того, чтобы вставлять значения в одну общую формулу, должен предотвращать перемещение некоторых объектов, иметь разные ветви выполнения для столкновения двух движущихся объектов против столкновения подвижного и неподвижного объекта.
Кроме того, если точность не является большой проблемой, используйте float
с вместо double
s. 🙂
Код в вопросе должен был рассчитать скорость отскока для кругового движущегося объекта, когда он столкнулся с неподвижным объектом AABB. Идеально неподвижный объект должен теоретически иметь бесконечную массу, но, как видно, представление этого в плавающей точке приводит к тому, что вычисления создают вектор (NaN, NaN).
В конце я выбрал явную проверку на бесконечную массу, которая упростила код.
Неподвижный объект не может получить импульс от столкновения, и из-за Сохранение Импульса движущийся объект не может потерять импульс. Это означает, что масса обоих объектов не имеет отношения к вычислениям, поскольку величина скорости движущегося объекта не изменится, изменится только направление.
Понимание этого позволило мне сократить код до простого отражения вектора скорости относительно нормали столкновения:
double t;
if (aabb_obj->mass == std::numeric_limits<double>::infinity()
&& intersect_moving_circle_aabb(circle_obj, aabb_obj, t))
{
circle_obj->position += circle_obj->velocity * t;
vec2 closest_point;
closest_pt_point_aabb(circle_obj->position, aabb_obj, closest_point);
vec2 n = (closest_point - circle_obj->position).normalized();
circle_obj->velocity = circle_obj->velocity - 2.0 * dot_product(circle_obj->velocity, n) * n;
}
насколько я знаю, это просто максимальное число, возможное в представлении с плавающей запятой.
Конечно, это так, потому что числа с плавающей точкой IEEE не могут представлять ничего больше, чем бесконечность. В то же время, это действительно бесконечность, и вы можете использовать его безопасно.