Я работаю над простым поведением бильярдных шаров при столкновении друг с другом. Все работает нормально, но возникла проблема, когда при столкновении с несколькими легкими шариками возникает эффект сцепления шариков, и они крутятся друг с другом. Подскажите, как это предотвратить.
bool MGBilliard::CollisingBall(CCPoint curr_point, CCPoint next_point)
{
float dx = next_point.x - (curr_point.x + dvdt.x);
float dy = next_point.y - (curr_point.y - dvdt.y);
float d = dx*dx+dy*dy;
return d <= BALL_RADIUS * BALL_RADIUS;
}
double MGBilliard::angleCollisionBalls(Ball* current, Ball* next)
{
double na;
double dx = fabs(next->location.x - current->location.x);
double dy = fabs(next->location.y - current->location.y);
na = atan(fabs(dy/dx));
if(atan(fabs(current->location.y/current->location.x)) < atan(fabs(next->location.y/next->location.x)))
na = current->angle - na;
else if(atan(fabs(current->location.y/current->location.x)) > atan(fabs(next->location.y/next->location.x)))
na = current->angle + na;
return na;
}
for(unsigned int i = 0;i<BALL_COUNT;++i)
{
if(vBalls[i]->speed > 0){
vBalls[i]->speed += vBalls[i]->acceleration;
float dsdt = vBalls[i]->speed*dt;
dvdt.x = dsdt*cos(vBalls[i]->angle);
dvdt.y = dsdt*sin(vBalls[i]->angle);
vBalls[i]->location.x += dvdt.x;
vBalls[i]->location.y += dvdt.y;
for(unsigned int j = 1; j < BALL_COUNT; ++j)
{
if(i == j) continue;
if(CollisingBall(vBalls[i]->spriteBall->getPosition(),vBalls[j]->spriteBall->getPosition()))
{
vBalls[j]->speed = 600;
double angle;
angle = angleCollisionBalls(vBalls[i],vBalls[j]);
vBalls[i]->angle = (float)-angle;
vBalls[j]->angle = (float)angle;
}
}
}
}
Есть две простые ошибки, которые бросаются мне в глаза при быстром взгляде на ваш код.
Во-первых, это:
vBalls[i]->angle = (float)-angle;
vBalls[j]->angle = (float)angle;
это не правильный способ расчета противоположных углов. Например, он не будет делать то, что вы хотите, когда angle
равен нулю (или 180 градусов, в этом отношении).
Во-вторых, вы перебираете все vBalls
массив несколько раз, один раз с индексом i
и внутренний цикл с индексом j
, Это означает, что столкновения будут рассчитываться дважды, а speed
обоих шаров будет установлено на 600! Изменение вашего внутреннего цикла, чтобы быть таким:
for(unsigned int j = i + 1; j < BALL_COUNT; ++j)
должно предотвратить это
Также есть более тонкая ошибка. Ваше обнаружение столкновений не учитывает время. Каждый шар перемещается на определенное расстояние каждую итерацию вашего игрового цикла. Это означает, что если столкновение не происходит за один «тик», мяч может пройти прямо через другой шар а затем вызвать код столкновения на дальней стороне мяча. В этой ситуации вы не можете выполнить простой тест столкновения на основе радиуса, так как если шар перемещается больше, чем (BALL_RADIUS * BALL_RADIUS) за один шаг, ваша система будет вести себя странно или не работать вообще.
Лично я бы использовал векторы для описания скорости и направления для каждого шара, а не углов и скоростей, но рефакторинг вашего кода для этого немного выходит за рамки этого вопроса.
Других решений пока нет …