Заставьте мяч отскочить до остановки

У меня есть класс мяча со следующими переменными:

int x,y,width,height;
double velX,velY;

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

//Gravity & movement for the ball
ball.setVelY(ball.getVelY() + 0.2);

ball.setY((int)(ball.getY() + ball.getVelY()));

//Make the ball bounce.
if(ball.getBounds().intersects(block.getBounds()){
     ball.setVelY(ball.getVelY() * -0.7);          
}

Я думал, что это заставит мяч медленно подпрыгивать до тех пор, пока ячмень вообще не начнет двигаться (что я бы проверил, а затем полностью остановил мяч), но это не так. Мяч будет отскакивать несколько раз, как обычно, причем каждый отскок будет меньше, чем предыдущий. Однако после нескольких отскоков мяч перестанет отскакивать ниже, чем предыдущие отскоки. Почему это и что мне делать, чтобы это исправить?


person Oliver    schedule 27.05.2015    source источник


Ответы (2)


Проблема здесь в том, что вы, по сути, применяете небольшие импульсы силы к мячу, дискретно. Несмотря на то, что вы уменьшаете импульс, вы постоянно добавляете это значение 0.2 к импульсу мяча.

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

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

person Nicholas Betsworth    schedule 27.05.2015
comment
Я согласен с этой оценкой. Лучше применять силы непрерывно, а затем проверять пороговые значения (например, 0 или меньше). Таким образом, вы можете избежать периодического обновления и просто держать его в постоянном движении, пока на него не повлияют другие силы и т. д. - person Sh4d0wsPlyr; 27.05.2015

Проблема может быть числовой. Что вы делаете, когда пишете

ball.setVelY(ball.getVelY() + 0.2);
ball.setY(ball.getY() + ball.getVelY());

интегрирует дифференциальное уравнение с помощью метода Эйлера. На самом деле это упрощение:

ball.setVelY(ball.getVelY() + g * DeltaTime); // g = 9.8m/s² for earth gravity
ball.setY(ball.getY() + ball.getVelY() * DeltaTime);

где DeltaTime – ваш шаг интегрирования, который для простоты принимается равным единице.

К сожалению, метод Эйлера только условно стабилен, а это значит, что вы можете оказаться в ситуации, когда скорость расходится (или это тип бесконечных колебаний). Чтобы восстановить стабильность, лучше всего уменьшить шаг по времени, что в вашем случае означает уменьшение скорости или, скорее, уменьшение гравитации. Попробуйте установить g от 0,2 до 0,05 и увеличьте частоту таймера на 4, чтобы убедиться в этом.

Если вы разбираетесь в математике, вы также можете взглянуть на безусловно стабильные методы, такие как обратный метод Эйлера.

person Eric Leibenguth    schedule 27.05.2015