7

I'm doing a game and I have a problem on some (strange) low end android devices (Galaxy S mini, Galaxy ACE ), with actually bad hardware. The FPS is on both devices 85+, and on other devices (HTC Desire, Sony Xperia Arc S, Samsung Galaxy S, HTC Wildfire ) the FPS is "normal" (around 60FPS). Also 60 FPS is showing on computer too. Obviously 85+ FPS is too much, because the game experience is different and it may cause unfair advantage to those players who are playing on 85FPS+.

I want to limit the FPS to MAX 60.

I've already searched this forum and I've found the exact question I'm asking right now. Limit FPS in Libgdx game with Thread.sleep() doesn't work

The (accepted) solution from user @daniel was this:

Thread.sleep((long)(1000/30-Gdx.graphics.getDeltaTime()));

But the thing is that doesn't work for me. If I use that code (30 replaced with 60 ), I get 30-33 FPS or if I use just the Daniel's code I get around 15-17 FPS. Why is that , and why is not working for me ? How to limit FPS on all devices to MAX 60 ? I tested this code on computer, and on devices also.

Any help HIGHLY appriciated. Thank you.

Community
  • 1
  • 1
rootpanthera
  • 2,731
  • 10
  • 33
  • 65
  • 1
    How are you measuring FPS? Are you timing each frame yourself at the beginning and end of the render method, or are you comparing from one frame to the next? – Tenfour04 May 28 '14 at 01:55
  • 1
    i seriously doubt having a higher FPS will give "advantage" ..... As a guy that support the idea of "PC gaming master race" , limiting FPS is what I hate the most. FPS should only affect the smoothness of the gameplay and nothing else, if one could argue having 85FPS is an "advantage" over 60, then why is 60 not an advantage over hardware that gives 30? That surely meant you have to limit FPS to 10 in order to be "fair". FPS shouldn't be limited unless it is tied to the game speed itself (like unit movement speed = 3 pixel per frame e.t.c.) which is a extremly bad design in the first place. – Jacky Cheng May 28 '14 at 03:45
  • That said, it SEEMS your FPS is half of the target value? maybe changing the value in the sleep equation to 120 will give you 60FPS? Also, I notice that you said "low end" hardware gices 85FPS and "higher end" device gives 60? are you sure you are looking at FPS but not delta time? delta time means the time between 2 frame update, a 60FPS will give you 16.6 millsecond of delta time. – Jacky Cheng May 28 '14 at 03:58
  • last but not least, be very careful of the sleep method you are using, there are high chance that sometime that equation will return negative value and crash you program. – Jacky Cheng May 28 '14 at 04:03
  • @Tenfour04. I'm measuring FPS with LibGDX method named "Gdx.graphics.getFramesPerSecond()". I'm printing this in the render method. JackyCheng. Thank you for your anwser. I've tried playing again on 85+ FPS. I said that player will have unfair advantage but as you said that's not true. Unfair advantage will have those who are running on lower fps. Because platforms are moving (at least visually) really really fast on high FPS devices, therefore is hard to jump from platform to platform. (But I didn't notice that from the start because game difficutly is easy once you start). – rootpanthera May 28 '14 at 08:06
  • 1
    "Obviously 85+ FPS is too much, because the game experience is different". That's not obvious at all. You can NEVER assume constant FPS. There will always be devices with less than 60FPS for example. You have to normalize your gameplay by multiplying with the frametime to get constant behaviour on all devices. – noone May 28 '14 at 08:18
  • @noone Huh. So for example, all moving platforms, characters, etc.. I need to multiply with the framerate? Or delta time ? Can you give me an example please? In my example I set linear velocity of the platform like that "setLinearVelocity(2.15f, 0);". But the behaviour is different on 60 or 85 FPS. How to change the linear velocity of the moving thing so it will have the same effect on all devices? – rootpanthera May 28 '14 at 08:29
  • With Box2D it gets more complicated. You should read this http://www.iforce2d.net/b2dtut/constant-speed. And how your physics world behaves strongly depends on how you do `World.step(...)`. – noone May 28 '14 at 08:34
  • I'm not entirely sure if this helps your FPS-specific question, but this is how I handled time in LibGDX: http://stackoverflow.com/questions/23995854/how-to-track-time-in-libgdxandroid/23996041#23996041 – EpicPandaForce Jul 07 '14 at 08:33

5 Answers5

12

In your launcher class;

AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
config.foregroundFPS = 60;
initialize(new MyGdxGame(), config);

I don't know if this is a new feature or an old one. Use this before trying to sleep the thread.

I highly recommend not limiting the frames by sleeping the thread, just to the keep the motion at the same pace for all the devices running fast or slow. Use something like this to move things on the screen.

x+= speed*Gdx.graphics.getDeltaTime();

Good luck on your adventure with libGDX, it seems that you are just started. Because when I was just started I was asking the same questions :)


EDIT: Here is a tip:

If you have collision in your game, and if the user sleeps the game by dragging the window etc. Gdx.graphics.getDeltaTime() function will return an unexpectedly high value. For example 2 seconds instead of 16 milliseconds (60fps) you were expecting. And in that case your character will jump, through walls and collision detection will fail. So here is what you can do:

  1. You can override that function and either take average of the last 10 frames and return that value (it is a little flawed in some cases but it is better in some other cases).
  2. You can return 30 milliseconds if the value is more than 30 seconds. So the character doesn't jump, go through walls... Even if the player sleeps it for a century.
  3. Combine both. (best) Don't count values more than 30 milliseconds when taking average.

I wrote a page about this subject, it is at my website in tutorials section. Check it out. nightlybuilddigital.ga

ossobuko
  • 851
  • 8
  • 25
7

You don't want to do that.

You need to make your game for any phone.

If you have a code like

player.x+=10f;

Your player will move faster on better devices.. You need to move your player by the delta time value.

player.x+=Gdx.graphics.getDeltaTime()*300; // this would be ~5f if your device is running with 60 fps
Boldijar Paul
  • 5,405
  • 9
  • 46
  • 94
  • Why 5f? It will be exactly +300/s – noone May 28 '14 at 09:25
  • 1
    This approach actually worked for me. I'm multiplying 2.15f (platform speed ) * deltaTime*50. On 60 FPS devices platform speed si around 1.4-1.8 but on 85FPS+ devices the platform speed is reduced to 1.1-1.4f. It kinda works for me because all platforms move with the same speed (visually) on all devices. But I'm curious if this is the right approach? But still I think it's better than the approach I had in mind first (limiting the FPS of the game). I will leave this answer opened a little bit, and I will accept the answer if nobody can provide me a better answer or confirm if this is the right – rootpanthera May 28 '14 at 09:46
  • @noone 1/60 * 300 = 5f , i mean that the player.x value will be incremented by 5 in one frame if the FPS is 60 – Boldijar Paul May 28 '14 at 10:47
  • @Paul Ah, you meant it will be a change of 5 per frame, not per second. Okay, that's correct. :) – noone May 28 '14 at 10:50
  • @rootpanthera You shouldn't do it this way when using Box2D. Velocity is already in m/s. Probably you are doing `World.step()` incorrectly, otherwise Box2D will normalize the velocity itself. – noone May 28 '14 at 10:52
  • @noone Thanks for trying to solve this problem with me :). I actually copied parameters for world step from some tutorials on the web. TIME_STEP = 1/60f; VELOCITY_ITERATIONS = 6; POSITION_ITERATIONS = 2; This should be correct, right? Or do you think something else is wrong? – rootpanthera May 28 '14 at 10:57
  • This is wrong in the simplest implementation. You define a constant timestep (1/60) there. This implies that you also have to call the `World.step(...)` exactly 60 times per second. Since you probably call it in every frame, it depends on the framerate of the device. If the device does only 30FPS, then you step your world only 30 times per second, which means it gets processed only for 0.5s instead of 1s as expected. Try either TIME_STEP = deltaTime, or see http://stackoverflow.com/questions/20848442/libgdx-speeding-up-a-whole-game-using-box2d for the best approach :) – noone May 28 '14 at 11:54
3

Why would you even pause the thread, when you can simply keep track of delta time and omit drawing/updating when it's too soon? You can limit the FPS programmatically by skipping rendering and logic updates when it's too soon.

Try something like this:

private static final float MIN_FRAME_LENGTH = 1f/60f;
private float timeSinceLastRender;

/** @param delta time since the last render call. */
public void render(float delta) {
      timeSinceLastRender += delta;
      if (timeSinceLastRender >= MIN_FRAME_LENGTH) {
           // Do the actual rendering, pass timeSinceLastRender as delta time.
           timeSinceLastRender = 0f;
      }
}
Czyzby
  • 2,999
  • 1
  • 22
  • 40
  • If your game is running at 67 fps (15 ms delta) instead of 60 fps (16.6 ms delta), 30 ms will pass between each render and the game will be running at 33 fps. Even sleeping the thread is a better option. – ossobuko Aug 19 '18 at 08:42
  • Use 1f/120f and the render thread will run the logic at 120fps but only draw 60 fps. – Michael N Oct 16 '21 at 22:44
2

As other people have said, if you code your game right, a higher fps shouldn't make a difference. take a look at this article here for a better way to do it. It wont translate directly to libgdx, but the concepts in there are all correct

Matt
  • 968
  • 2
  • 13
  • 22
0

Try this c:

 if (dt < (1f / max_frames)) {

        float sleepTime = (1f / max_frames) - dt;
        try {
            Thread.sleep((long) sleepTime);
        } catch (InterruptedException ex) {
            LogTool.logError(ex.getLocalizedMessage());
            ex.printStackTrace();
        }
    }
Maarten
  • 635
  • 1
  • 9
  • 29