I'm developing a simple game, but I have a problem with FPS and gravity. So I need to sync FPS and gravity, because the higher FPS is the player fall faster. How can I sync them?
My code to update player:
while (true) {
try {
Thread.sleep(1000 / GameMain.maxFPS); //Max Fps Limiter
GameMain.activeEntities.forEach((ent) -> { //Revalidade All Entities and Positions
ent.onTick();
});
gamePanel.repaint(); //Repaint Frame, Update Positions
} catch (InterruptedException e) {
e.printStackTrace();
}
}
My code to update gravity:
@Override
public void onTick() {
this.velocity = this.velocity.add(0, gravityForce()/*<- I'm using 9.807 divided by 60*/);
if (this.velocity.y > maxSpeed)
{
this.velocity.y = maxSpeed;
}
this.getPosition().y = this.getPosition().y + (this.velocity.y);
}
Using Thread.sleep is going to be problematic as Thread.sleep will not guarantee that the Thread sleeps for exactly 1000ms, but rather than the Thread will sleep for at least 1000ms. Further, whenever it comes to working with threads there will be differences in behavior dependent upon the actually system, as thread scheduling logic is not a function of your application or the JVM, but rather the underlying hardware.
My recommendation: Either decouple draw logic from recalculation logic or let it run free without blocking the Thread with Thread.sleep. From there, to get good motion we should recall that change in position is not just a factor of speed, but also time. Therefore, we need to calculate the time passing between different draw frames by some mechanism such as System.currentTimeMillis()
or System.nanoTime()
.
From there, you can recalculate positions by multiplying their velocity times the amount of time passing since recalculating.
Rough implementation:
double c = 1 / 60; // Some constant factor to bound motion over time
long previousCycleTime = System.currentTimeMillis();
while(somePredicate()) { // Logical cycle
long t = System.currentTimeMillis() - previousCycleTime;
gameObjects.forEach( o -> {
// t -> time passage
// dx -> change on the x axis
// c -> some constant factor to bound movement to the 'virtual space'
o.x += o.dx * t * c;
o.y += o.dy * t * c;
}
redraw();
}
Alternatively, you could redraw in a separate thread to decouple the two processes. This should not be taken lightly though, as transitioning from a single threaded application to multithreaded application introduces a great deal of additional complexity.