I'm trying to write a test game with LibGDX Box2D, bodies can fall down quickly within the amount of 5, but if I change the amount to 10, 12 or more, the falling speed becomes slower and slower. I Cannot figure out why this happen that when the number of bodies increase, the falling speed decreases. As far as i understand, the amount of body should not affect the gravity, even if it is possible, it is only a dozen.
class GameViewActivity : ApplicationAdapter() {
companion object {
private const val SCALE = 2.0f
const val PIXEL_PER_METER = 100f
private const val TIME_STEP = 1 / 60f
private const val VELOCITY_ITERATIONS = 6
private const val POSITION_ITERATIONS = 20
private const val VELOCITY_Y = -9.85f
private const val VELOCITY_X = 0f
}
private var orthographicCamera: OrthographicCamera? = null
private var world: World? = null
private var batch: SpriteBatch? = null
private var ground: Ground? = null
private var discList = ArrayList<Disc>()
private var shapeRenderer: ShapeRenderer? = null
override fun create() {
orthographicCamera = OrthographicCamera()
orthographicCamera!!.setToOrtho(false, Gdx.graphics.width / SCALE, Gdx.graphics.height / SCALE)
world = World(Vector2(VELOCITY_X, VELOCITY_Y), false)
world!!.setContactListener(WorldContactListener())
batch = SpriteBatch()
val shape = PolygonShape()
shape.setAsBox(orthographicCamera!!.viewportWidth, 2f)
ground = Ground(world!!, shape)
for (i in 1..12) {
val d = Disc(world!!, 60f, 10f + i.toFloat()*2, 150f, 30f)
discList.add(d)
}
}
override fun render() {
update()
Gdx.gl.glClearColor(0.5f, 0.8f, 1f, 1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
shapeRenderer = ShapeRenderer()
shapeRenderer!!.begin(ShapeRenderer.ShapeType.Filled);
shapeRenderer!!.color = Color.YELLOW;
shapeRenderer!!.rect (
0f,
ground!!.body!!.position.y * PIXEL_PER_METER - 12f / 2 / SCALE,
orthographicCamera!!.viewportWidth * SCALE,
2f * PIXEL_PER_METER
)
shapeRenderer!!.end();
for (disc in discList) {
val discRenderer = ShapeRenderer()
discRenderer.begin(ShapeRenderer.ShapeType.Filled);
discRenderer.color = disc.getColor()
discRenderer.rect(
300f,
disc.body!!.position.y * PIXEL_PER_METER ,
disc.getWidth() * SCALE,
disc.getHeight() * SCALE
)
discRenderer.end()
}
}
private fun update() {
world!!.step(TIME_STEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS)
cameraUpdate()
}
private fun cameraUpdate() {
orthographicCamera!!.update()
}
}
I am pretty sure that the problem is the update
method.
You only make one world.step
in every render
method, without taking the Gdx.graphics.getDeltaTime()
into account.
So the more objects there are, the longer the render
will take, the slower the game will become.
Example:
Update + render of 5 objects takes 0.0125s. You call world.step
with a fixed TIME_STEP
of 1 / 60 (0.0167s), so your updated uses 0.004s more then it should. With a speed of 5 m/s and a pixel / m ratio of 100, an object would go 2 pixels further then it should.
5m/s * 100px/m -> 500px/s
500px/s * 0.004s -> 2px
Now if you add more and more objects, the update and render will take a little longer to complete, resulting in bigger delta times. If update and render takes 0.02s now, your objects would go 1.65px less then it should.
So the correct update
should look something like this (Java code):
float frameTime = Math.min(deltaTime, 0.25f); // Avoid spiral of death on slow devices
accumulator += frameTime; // Increase member variable accumulator
while (accumulator >= TIME_STEP) {
world.step(TIME_STEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS);
accumulator -= TIME_STEP;
}
So basicaly you check the delta
time (time between the last render and this render call). On slow devices, the delta time might get to big, resulting in a lot of world.step
calls, making delta time even bigger. This is the so called spiral of death, which we avoid by limiting the delta to 0.25f
.
Now we add this delta time to an accumulator. This allows us to store the rest of the delta time, if it is not a multiple of TIME_STEP
.
Next we call world.step
in a loop, as long as our accumulator
is bigger then TIME_STEP
.
After all of this, the game logic has been updated correctly and we can render the changes to the screen.
Other notes:
You are using a pixel to meter conversion. I wouldn't do that, as LibGDX already offers Camera
and Viewport
for this.
Basically thos objects allow you to specify the width in x and y and scale it up to fit the screen (in different ways).
So if you specify a width of 8 and move by 1, you will move by 1 / 8 of the screen width, independent of the resolution.