Fixing your time step, such an issue and yet, it's not that difficult. All it takes is to spare a moment from your time and try to understand how a simulation works and what exactly is a time step.
Before i even developed games (actually in that age i was just playing counter strike offline in my computer with my 17 inch square monitor), there were some guys who explained those subjects. So, give a look at these 2 posts by Glenn Fiedler, Integration Basics and Fix Your Timestep. When you are done and you think that you have understood almost everything, or at least a big portion of our subjects, then come back, because we will implement a fixed time step with interpolation to our game.
Fixing your timestep with LibGDX
Now then, in our " GameStage " class, to step our simulation we used;
accumulator += delta;
while (accumulator >= delta) {
world.step(TIME_STEP, 8, 4);
accumulator -= TIME_STEP;
}
Since we are using box2D, we don't have to worry about implementing our own integration. All we have to do is call the world.step method which performs collision detection, integration and constraint solution for us. The numbers 8 and 4 that you see in our code, are called Velocity and Position Iterations.
" In addition to the integrator, Box2D also uses a larger bit of code called a constraint solver. The
constraint solver solves all the constraints in the simulation, one at a time. A single constraint can be
solved perfectly. However, when we solve one constraint, we slightly disrupt other constraints. To get a
good solution, we need to iterate over all constraints a number of times.
There are two phases in the constraint solver: a velocity phase and a position phase. In the velocity
phase the solver computes the impulses necessary for the bodies to move correctly. In the position
phase the solver adjusts the positions of the bodies to reduce overlap and joint detachment. Each phase
has its own iteration count. In addition, the position phase may exit iterations early if the errors are
small.
The suggested iteration count for Box2D is 8 for velocity and 3 for position. You can tune this number to
your liking, just keep in mind that this has a trade-off between performance and accuracy. Using fewer
iterations increases performance but accuracy suffers. Likewise, using more iterations decreases
performance but improves the quality of your simulation. "
The above text was a copy paste from the box2D documentation, paragraph 2.4 page 10.
I found those numbers best in my case, so i just switched from 3 position iterations to 4. Also, as stated in some questions on the box2D forums, box2D uses a Semi - Implicit Euler integrator which by now you most probably know what it is.
Remember, we aim for the best possible and fair experience for as many players as we can. That's the reason we made our game fully responsive for all the currently available screen resolutions and that's is the reason why we want it to run on both slow and fast devices the same way. and in the best possible way. If we just used a fixed delta time and because the number of times the world.step will be called. depends on the underlying hardware, we could end up with completely different behaviours on different devices.
We modify our " GameStage " class and also add interpolation.
package com.getest.game.stages;
// Imports ...
public class GameStage extends Stage implements ContactListener{
private float accumulator, TIME_STEP, alpha;
private Box2DDebugRenderer renderer;
private WorldMisc wrl;
private World world;
private GroundActor ground;
private LeftWallActor leftWall;
private RightWallActor rightWall;
private HeroActor hero;
private Boolean right = false, left = false;
private Skin leftButtonSkin, rightButtonSkin, jumpButtonSkin, slideButtonSkin;
private ImageButton leftButton, rightButton, jumpButton, slideButton;
private Texture backGroundTexture;
private Image backGroundImage;
private Timer timerEnemy = new Timer();
public GameStage(){
super(new MyViewport(16f, 9f,
new AndroidCamera(16f, 9f)));
accumulator = 0.0F;
TIME_STEP = 1/300F; // Recomended by libgdx
setupWorld();
}
// Rest of the code
@Override
public void act(float delta) {
super.act(delta);
Array<Body> bodies = new Array<Body>(world.getBodyCount());
world.getBodies(bodies);
step(delta);
for(Body body : bodies){
update(body);
}
if (left) {
hero.moveLeft();
} else if (right) {
hero.moveRight();
} else {
hero.moveStop();
}
}
public void step(float dt) {
if ( dt > 0.25){
dt = 0.25f;
}
accumulator += dt;
while( accumulator >= TIME_STEP){
world.step(TIME_STEP, 8,4); // Recomended by libgdx (8,3)
accumulator -= TIME_STEP;
}
alpha = accumulator / TIME_STEP;
Array<Body> bodies = new Array<Body>(world.getBodyCount());
world.getBodies(bodies);
for ( Body body : bodies){
if ( !(body.getType() == BodyDef.BodyType.StaticBody)){
Vector2 previousPos = body.getPosition();
float previousAngle = body.getAngle();
float posX = previousPos.x * (1.0F - alpha) + body.getPosition().x * alpha;
float posY = previousPos.y * (1.0F - alpha) + body.getPosition().y * alpha;
float angle = previousAngle * (1.0F - alpha) + body.getAngle() * alpha;
body.setTransform(posX, posY, angle);
}
}
}
private void update(Body body) {
if((!BodyMisc.bodyInBounds(body) && BodyMisc.bodyIsEnemy(body))){
world.destroyBody(body);
}
}
// @Override
// public void draw() {
// super.draw();
// renderer.render(world, getViewport().getCamera().combined);
// }
// Rest of the code
}
We also commented out the draw method because we don't want the bodies to appear in our screen.
Run the game!
That's it for today's tutorial! Feel free to ask any questions in the comments below.
Run the game!
That's it for today's tutorial! Feel free to ask any questions in the comments below.
Comments
Post a Comment