1

I am developing a tile base game. Most of the time the game "respects" the cap of 60fps, however when I intentionally set the fps to 10fps for 5 seconds (via debugging) to test that the game render and game logic work separated, what happens is that after I set back the game fps to 60fps, for a few seconds the game ignores the "cap" and runs at like 10000fps, then it goes back to the cap. The reason of all of this, is that I want that my game runs at variable fps but constant logic speed on all machines, however what I dont understand is why my game respect the cap most of the time and never goes higher than 60fps but when a slowdown or lag spike happens the game runs super faster for 1-2 seconds and then goes back to normal. No matter if the user is running a bad computer and the game runs at 5fps, what I want is ensure the game never goes higher than my hard cap.

Did I need to implement a "skipped frames" system or something? Just for info, my game logic is very light, but the Rendering is pretty heavy.

new Thread(){
        public void run(){
            long hintPoint = System.currentTimeMillis();
            long nexttickG = System.currentTimeMillis();
            long nexttickL = System.currentTimeMillis();
            long nexttickV = System.currentTimeMillis();
            long ticktimeV = 1000;
            long LogicCap = 60;
            long ticktimeL = 1000/LogicCap;
            short frames = 60;

            while(isRunning){

                if(Main.servbot){
                    try {
                        Thread.sleep(15);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                GLOBAL_FPS_CAP=(short)Utils.Clamp(GLOBAL_FPS_CAP,1, 333);

                short FramesCap = GLOBAL_FPS_CAP;
                long ticktimeG = 1000/FramesCap;

                if (System.currentTimeMillis()>nexttickG){
                    Render();
                    long elapsed = System.currentTimeMillis() - hintPoint;
                    frames=(short)((long)1000/(Math.max(elapsed,1)));
                    hintPoint = System.currentTimeMillis();
                    nexttickG+=ticktimeG;
                }

                if (System.currentTimeMillis()>nexttickL){
                    GameLoop();
                    nexttickL+=ticktimeL;
                }

                if (System.currentTimeMillis()>nexttickV){
                    GLOBAL_FPS_HINT = frames;

                    nexttickV+=ticktimeV;
                }

                Thread.yield();

            }

        }
    }.start();
}
CosmicGiant
  • 6,275
  • 5
  • 43
  • 58

2 Answers2

2

First, if at all possible, use .nanoTime() instead of .currentTimeMillis(), because the second is a lot more prone to errors.

And second, consider implementing a DELTA-TIME system, where FPS and Game-World Time are independent from each-other.

Delta-Time is the correct loop-pattern for things like animation and games (which are basically programmatic, systematic, and sometimes chaotic animations of game-world data), because it's the simplest pattern that avoids the time-drift bugs of most naive/simplistic animation-loop or game-loop implementations.

Here's an example of how to implement a Delta-Time game-loop in Java.

For a complete explanation of the problem with naive loop implementations, and how Delta-Time solves the time-drift problem (and thus, why it is used), take a look at this article and this article, which explain the differences in the game loops in detail.

Community
  • 1
  • 1
CosmicGiant
  • 6,275
  • 5
  • 43
  • 58
1

Could these changes help? (see code below, look for /*new*/ ).

Reasoning: Are you setting a breakpoint in your run() method? Which var(s) do you modify

How many milliseconds tick by in System.currentTimeMillis() while you are modifying the var(s) to change your frame rate? Seems like if it takes you 3 or 4 (maybe 12) seconds to make the change and resume execution, your nextTickL and nextTickG values will be pretty far behind System.currentTimeMillis() and might take LOTS of additions of += ticktimeG to catch up to the system clock.

new Thread(){
    public void run(){
        long hintPoint = System.currentTimeMillis();
        long nexttickG = System.currentTimeMillis();
        long nexttickL = System.currentTimeMillis();
        long nexttickV = System.currentTimeMillis();
        long ticktimeV = 1000;
        long LogicCap = 60;
        long ticktimeL = 1000/LogicCap;
        short frames = 60;

        while(isRunning){

            if(Main.servbot){
                try {
                    Thread.sleep(15);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            GLOBAL_FPS_CAP=(short)Utils.Clamp(GLOBAL_FPS_CAP,1, 333);

            short FramesCap = GLOBAL_FPS_CAP;
            long ticktimeG = 1000/FramesCap;

            if (System.currentTimeMillis()>nexttickG){
                Render();
                long elapsed = System.currentTimeMillis() - hintPoint;
                frames=(short)((long)1000/(Math.max(elapsed,1)));
                hintPoint = System.currentTimeMillis();
                nexttickG+=ticktimeG;
   /*new*/      if( nexttickG < System.currentTimeMillis() ) {
   /*new*/         // major clock skew somehow, reset.
   /*new*/         nexttickG = System.currentTimeMillis() + ticktimeG;
   /*new*/      }
            }

            if (System.currentTimeMillis()>nexttickL){
                GameLoop();
                nexttickL+=ticktimeL;
   /*new*/      if( nexttickL < System.currentTimeMillis() ) {
   /*new*/         // major clock skew somehow, reset.
   /*new*/         nexttickL = System.currentTimeMillis() + ticktimeL;
   /*new*/      }
            }

            if (System.currentTimeMillis()>nexttickV){
                GLOBAL_FPS_HINT = frames;

                nexttickV+=ticktimeV;
            }

            Thread.yield();

        }

    }
}.start();
jgreve
  • 1,225
  • 12
  • 17
  • YOU DONE IT! The thing is that the user can change the max fps cap in the options menu, so when going from 60fps to 30fps and then you go back to 60fps, the NextTick var goes behind, but with your fix, now the games no longer goes super fast trying to catch up to the current time! Thank you! – Lester Murillo Jan 02 '16 at 03:29
  • Cool. Just for fun if you publish would you put the name of the game here, maybe in a comment? Would be fun to see it some day. – jgreve Jan 02 '16 at 03:33
  • The closed beta will be soon, actually the game already have a lot of items, and content, its very playable right now, I am just doing some minor engine polishes to improve stability and performance. Thank you bro! – Lester Murillo Jan 02 '16 at 03:45