Skip to main content
Don't repeat tags in title
Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401

LibGdx - Objects move with different speed on different devices

Source Link

LibGdx - Objects move with different speed on different devices

I was making a 2d top-down game, but when I tested it on two devices (Samsung M31 and Samsung S25) there was a problem with player speed - it was faster on S25 and slower on M31. I tried using some fixed steps and delta things, but nothing worked.
Thanks for any help!
Here's some code:
render version 1)

@Override
    public void render(float delta) {
        world.step(1f/60f, 6, 2);
        updateGameLogic();
        renderGame();
    }

render version 2:

static final float STEP_TIME = 1f/300f;
float accumulator = 0;
...
@Override
    public void render(float delta) {
        accumulator += Math.min(delta, 0.25f);

        while (accumulator >= STEP_TIME) {
            accumulator -= STEP_TIME;
            updateGameLogic();
            world.step(STEP_TIME, 6, 2);
        }
        renderGame();
    }

updateGameLogic()

private void updateGameLogic() {
        if (!actMenu && !actVending)
            position.lerp(new Vector2(player.getX(), player.getY()), 0.1f); // 0.1f - camera smoothing coefficient
        camera.position.set(position, 0);
        camera.update();

        up = Gdx.input.isKeyPressed(Input.Keys.W) || Gdx.input.isKeyPressed(Input.Keys.UP);
        down = Gdx.input.isKeyPressed(Input.Keys.S) || Gdx.input.isKeyPressed(Input.Keys.DOWN);
        left = Gdx.input.isKeyPressed(Input.Keys.A) || Gdx.input.isKeyPressed(Input.Keys.LEFT);
        right = Gdx.input.isKeyPressed(Input.Keys.D) || Gdx.input.isKeyPressed(Input.Keys.RIGHT);

        attack = Gdx.input.isKeyPressed(Input.Keys.SPACE) || Gdx.input.isKeyPressed(Input.Keys.ENTER);
        menu = Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE) || Gdx.input.isKeyJustPressed(Input.Keys.BACK);

        menuX = position.x - SCR_WIDTH / 4;
        menuY = position.y - SCR_HEIGHT / 3;

        vendingX = position.x - SCR_WIDTH / 4;
        vendingY = position.y - SCR_HEIGHT / 4;

        // touch handler

        touchHandler();

        // events

        if (deathTime == 0 && !actMenu && !actVending) {
            player.changePhase();
            changePhaseGhosts();
            changePhaseZombies();
            changePhaseWardens();
            changePhaseAnimatedObstacles();
            player.step(actJoystick);
            player.updateProjectiles();
            updateProjectilesWardens();
            //            player.updateMeleeRegion();
            //            meleeRegionUpdate();
            updateGhosts();
            updateZombies();
            updateWardens();
            updateDoors();
            updateCoins();
            updateChests();
            updateLevel();
            updateVending();
            updateButtons();
        }
    }

renderGame():

private void renderGame() {
        // draw
        ScreenUtils.clear(0, 0, 0, 0);
        //        debugRenderer.render(world, camera.combined);
        batch.setProjectionMatrix(camera.combined);
        rayHandler.setCombinedMatrix(camera.combined, camera.position.x, camera.position.y, camera.viewportWidth, camera.viewportHeight);
        camera.update();

        batch.begin();
        ...
        batch.end();

        playerLight.attachToBody(player.getBody());
        rayHandler.updateAndRender();
        destroyScheduledBodies();

touchHandler():

private void touchHandler() {
        actJoystick = false;
        actAttack = false;

        float playerSpeed = player.getSpeed();
        float playerDiagSpeed = playerSpeed / (float) Math.sqrt(2);

        ArrayList<Vector3> touches = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            touches.add(new Vector3());
        }

        for (int i = 0; i < 3; i++) {
            if (Gdx.input.isTouched(i)) {
                touches.get(i).set(Gdx.input.getX(i), Gdx.input.getY(i), 0);
                camera.unproject(touches.get(i));

                if (touches.get(i).x < player.getX()) {
                    actJoystick = true;
                    joystick.updateKnob(touches.get(i));
                    if (player.isShopping()) {
                        player.setShopping(false);
                    }
                } else if (touches.get(i).x > player.getX()) {
                    if (btnAttack.hit(touches.get(i).x, touches.get(i).y)) {
                        actAttack = true;
                    }
                }
            }
        }

        if (elevatorUseTime != 0 || actMenu || actVending) {
            actJoystick = false;
            actAttack = false;
        }

        if (actJoystick) {
            Vector2 directionVector = joystick.getDirectionVector();
            player.getBody().setLinearVelocity(
                directionVector.x * player.getSpeed(),
                directionVector.y * player.getSpeed()
            );

            if (Math.abs(directionVector.x) > Math.abs(directionVector.y)) {
                if (directionVector.x > 0) player.setDirection('r');
                else player.setDirection('l');
            } else {
                if (directionVector.y > 0) player.setDirection('u');
                else player.setDirection('d');
            }
        } else {
            joystick.resetKnob();
            player.getBody().setLinearVelocity(0, 0);
        }

        if (actAttack || attack) player.attack();
        if (menu) {
            actMenu = !actMenu;
            uiInput.setButtons(hudButtons);
            if (actMenu) uiInput.setButtons(menuButtons);
        }

        if (up) {
            player.setDirection('u');
            player.getBody().setLinearVelocity(
                player.getBody().getLinearVelocity().x,
                playerSpeed
            );
        } else if (down) {
            player.setDirection('d');
            player.getBody().setLinearVelocity(
                player.getBody().getLinearVelocity().x,
                -playerSpeed
            );
        } else if (right) {
            player.setDirection('r');
            player.getBody().setLinearVelocity(
                playerSpeed,
                player.getBody().getLinearVelocity().y
            );
        } else if (left) {
            player.setDirection('l');
            player.getBody().setLinearVelocity(
                -playerSpeed,
                player.getBody().getLinearVelocity().y
            );
        }
        if (up && right) {
            player.setDirection('r');
            player.getBody().setLinearVelocity(
                playerDiagSpeed,
                playerDiagSpeed
            );
        } else if (up && left) {
            player.setDirection('l');
            player.getBody().setLinearVelocity(
                -playerDiagSpeed,
                playerDiagSpeed
            );
        } else if (down && right) {
            player.setDirection('r');
            player.getBody().setLinearVelocity(
                playerDiagSpeed,
                -playerDiagSpeed
            );
        } else if (down && left) {
            player.setDirection('l');
            player.getBody().setLinearVelocity(
                -playerDiagSpeed,
                -playerDiagSpeed
            );
        }
    }

Entity:

public class Entity {
    private Body body;
    private float width, height;
    private float speed = 50f;
    protected int health, maxHealth;
    protected char direction = 'd';
    private int phase, nPhases;
    protected long timeLastPhase, timePhaseInterval, timeBasePhaseInterval;
    private boolean isAlive;
    protected boolean isBattle;

    public Entity(World world, float width, float height, float x, float y, int maxHealth, int nPhases, long timeBasePhaseInterval) {
        phase = 0;
        this.nPhases = nPhases;
        this.timeBasePhaseInterval = timeBasePhaseInterval; // except running animation!
        this.timePhaseInterval = timeBasePhaseInterval;
        this.width = width;
        this.height = height;
        this.health = this.maxHealth = maxHealth;
        this.isAlive = true;

        BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyDef.BodyType.DynamicBody;
        bodyDef.position.set(x, y);
        bodyDef.fixedRotation = true;

        body = world.createBody(bodyDef);

        PolygonShape shape = new PolygonShape();
        shape.setAsBox(this.width / 2, this.height / 2);

        FixtureDef fixtureDef = new FixtureDef();
        fixtureDef.shape = shape;
        fixtureDef.density = 1.0f;
        fixtureDef.friction = 0.4f;

        body.createFixture(fixtureDef);

        shape.dispose();
    }

    protected void changePhase(){
        if (isBattle) timePhaseInterval = timeBasePhaseInterval-250;
        else timePhaseInterval = timeBasePhaseInterval;

        if(TimeUtils.millis() > timeLastPhase+timePhaseInterval) {
            if (++phase == nPhases) phase = 0;
            timeLastPhase = TimeUtils.millis();
        }
    }

    public void hit(int damage) {
        health-=damage;
        if (health<=0) {
            isAlive = false;
            health = 0;
        }
    }
```