Search code examples
javamultithreadingperformancelibgdxgame-development

How to create a proper tick method in a lib gdx game


I just started with lib gdx and essentially gave up on using lwgjl for making a game as it is ridicoulosy overcomplicated. The problem is that lib gdx has a built in create() method run once at creation and a render() method run as many times as possible. So there is no built in tick method that I can use to update the game at a set time per second. I want to add one.

In my main class I create a new thread with the tick rate method and it works, but the problem is that I can't utitlize the render() method myself since that is handled already by lib gdx to be called as many times as possible. So I have the thread I create to tick the game and the render method is being run by lib gdx which is I believe run from a second thread. I don't think its a good idea to tick the game and render from different threads as I believe that the rendering could have issues with updating properly since the read write from the same variables in the object classes would be both read in the tick and render and modified in the tick only. Is this maybe a very good idea to do actually if I handle all the variables visible to the object render methods as volatile since they are only read by the thread? Or is this a terrible idea for performance? I am no sure about the set backs and difference of using a lot of volatile variables rather than regular variables. Here is my main class so far:

public class TanksII extends ApplicationAdapter implements Runnable {
    
    SpriteBatch batch;
    Texture img;
    
    private Thread thread;
    boolean isRunning;
    volatile int frames;
    
    @Override
    public void create() {
        batch = new SpriteBatch();
        img = new Texture("badlogic.jpg");
        
        start();
    }
    private void tick() {
        
    }
    @Override
    public void render() {
        ScreenUtils.clear(1, 0, 0, 1);
        batch.begin();
        batch.draw(img, 0, 0);
        batch.end();
        frames++;
    }
    @Override
    public void dispose() {
        isRunning = false;
        batch.dispose();
        img.dispose();
    }
    private synchronized void start() {
        if (isRunning)
            return;
        thread = new Thread(this);
        thread.start();
        isRunning = true;
    }
    private synchronized void stop() {
        if (!(isRunning))
            return;
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isRunning = false;
    }
    public void run() {
        long lastTime = System.nanoTime();
        double amountOfTicks = 60.0;
        double ns = 1000000000 / amountOfTicks;
        double delta = 0;
        long timer = System.currentTimeMillis();
        int updates = 0;
        while(isRunning) {
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;
            while(delta >= 1) {
                tick();
                updates++;
                delta--;
            }
            if(System.currentTimeMillis() - timer > 1000) {
                timer += 1000;
                System.out.println("FPS: " + frames + " TICKS: " + updates);
                frames = 0;
                updates = 0;
            }
        }
        stop();
    }
}

Solution

  • You don't need another thread for this and it is a bad idea to have one (as you pointed out yourself).

    If you can make your game-logic delta-time dependent, so that it doesn't matter how often render gets called:

    @Override
    public void render() {
        float delta = Gdx.graphics.getDeltaTime();
        // Use delta to update you game an appropriate amount
    }
    

    If your particular scenario won't work with that approach, you can instead just configure libGDX to call the render method as often as you like.

    In you launcher class you can configure the foreground FPS, so in the case of a desktop application that might look like this;

    public static void main (String[] arg) {
        Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
        config.setForegroundFPS(20); // Or what ever FPS you want.
        new Lwjgl3Application(new TanksII(), config);
    }