0

I am currently developing a 2D game using Swing components. Each time I run my game it stutters randomly at some points. This is my game loop code:

public class FixedTSGameLoop implements Runnable
{
    private MapPanel _gamePanel;

    public FixedTSGameLoop(MapPanel panel)
    {
        this._gamePanel = panel;
    }

    @Override
    public void run()
    {
        long lastTime = System.nanoTime(), now;
        double amountOfTicks = 60.0;
        double amountOfRenders = 120.0;
        double nsTick = 1000000000 / amountOfTicks;
        double nsRender = 1000000000 / amountOfRenders;
        double deltaTick = 0;
        double deltaRender = 0;
        while (this._gamePanel.isRunning())
        {
            now = System.nanoTime();
            deltaTick += (now - lastTime) / nsTick;
            deltaRender += (now - lastTime) / nsRender;
            lastTime = now;
            while (deltaTick >= 1)
            {
                tick();
                deltaTick--;
            }
            while (deltaRender >= 1)
            {
                render();
                deltaRender--;
            }
        }
    }

    private void tick()
    {
        /**
         * Logic goes here:
         */
        this._gamePanel.setLogic();
    }

    private void render()
    {
        /**
         * Rendering the map panel
         */
        this._gamePanel.repaint();
    }
}

I have tried multiple times to omit certain code parts, thinking that they cause lag, but I have found nothing that caused it particularly, so I think the problem lies within my game loop mechanism. Thank you for your help!

Ido Sofi
  • 5
  • 3
  • 1
    Any call to Swing methods (including `repaint`) from another thread need to be done on EDT using `SwingUtilities.invokeLater` method. – tsolakp Feb 01 '18 at 17:02
  • Hmm, can you give me an example regarding my code? Are the other aspects of the game loop okay? – Ido Sofi Feb 01 '18 at 17:09
  • `SwingUtilities.invokeLater( () -> _gamePanel.repaint() );` for one of them. You might also need to do the same for `_gamePanel.setLogic();` as well. – tsolakp Feb 01 '18 at 17:12
  • Same problem still occurs... – Ido Sofi Feb 01 '18 at 17:21
  • `Each time I run my game it stutters randomly at some points.` - you have a while loop that continually executes. The game should be played at some time interval so the CPU can have a chance to rest. So, you should be using a [Swing Timer](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) to schedule the game play at your desired interval. – camickr Feb 01 '18 at 17:42
  • By that you mean basically ditching my code and replacing it with something like this? Timer t = new Timer(1000/60, new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { _mapPanel.setLogic(); _mapPanel.repaint(); } }); t.start(); – Ido Sofi Feb 01 '18 at 17:51

2 Answers2

0

Your game loop must contain a "Thread.sleep" on order to sleep the amount of time needed to respect your target FPS.

The main loop is supposed to contain 1 tick() and 1 render().

Your current implementation is flooding the paint manager, slowdowns will appear when the underlying buffers will be full and when the garbage collector will do its job.

BluEOS
  • 576
  • 6
  • 13
  • But the code only calls the render method when the "deltaRender" variable reaches the value of 1, which means that 1000/120 ms have been passed since the last call (same with the logic setting but for 1000/60 ms). – Ido Sofi Feb 01 '18 at 19:03
  • sure BUT your main while is consuming the CPU core at 100% for nothing. Use sleep(). – BluEOS Feb 02 '18 at 12:58
  • Need more details? See http://gameprogrammingpatterns.com/game-loop.html . – BluEOS Feb 02 '18 at 12:59
  • I see. I simply called Thread.Sleep(1) at the end of the loop and it now works like a charm. Thanks! – Ido Sofi Feb 02 '18 at 13:23
0

While it's good that you've split your render and logic in two different methods, the problem lies in that they exist in the same thread.

What needs to happen to reduce the lag is to have them in separate threads. The render thread would request a snapshot of the current state from the logic thread (to prevent concurrent modification) and render that snapshot.

Right now, if one render takes too long, or if one logic step takes too long, the other check is going to have to wait for it to finish before it can begin working.

corsiKa
  • 81,495
  • 25
  • 153
  • 204