SOLVED Best way to target multiple FPS?

  • Thread starter GhostlyImprints
  • Start date
G

GhostlyImprints

Guest
So my game has been running under a set room speed: 30 for the longest time.
I decided I wanted to allow players to change the FPS at their will from 30 -> 144 if they wish.

To do this I've been literally changing every value related to time/velocity/etc. and multiplying it by a factor.

As an example:

baseFPS = 30;

// User wants the FPS to go to 60:
room_set_speed(rm_test, 60);

fpsFactor = baseFPS / room_get_speed(rm_test);

// Player wants to move to the right 2 pixels
x+=2 * fpsFactor;


I just updated to GMS2 and now I'm noticing you can no longer set room_speed for individual rooms. I noticed this option in Main Options:

View attachment 36914

I've also noticed room_set_speed() is gone and replaced with game_set_speed() ?

So my main question is: Is my code above correct? I'm not sure how to alter it now there's been so many changes it seems to room speed and FPS.

Is anyone able to explain how it affects my code now and if I need to make any changes to allow users to select their own FPS?
 
Last edited by a moderator:
G

GhostlyImprints

Guest
Look into delta timing. You won't have to worry about certain speeds or settings or anything, just let it run as fast as it can or cap it if you like.
So I'm a bit confused.

Is my code incorrect then?

It says in the manual you still need to actually multiple values related to time/speed with a multiplier:

speed = spd * (ot - delta_time);

So what exactly is wrong with my solution in my first post?

Let's take an example and assume game_get_speed(gamespeed_fps) is set to 60:

GML:
// obj_player Create Event
spd = 2; // <-- We Want the player to go 2 pixels per frame, our desired speed
// ---------------------------------------------------------------------------

//obj_player Step Event
x+=spd;
// ---------------------------------------------------------------------------
So let's assume this is the speed I want my player to go at in all different types of FPS values. This currently works perfectly at 60 FPS, but at 30 for example, they'll go slower. At 144, they'll go faster.

What would you do in this case with delta timing (I'm very unfamiliar with how it works in GameMaker now) to ensure that no matter what FPS we set the game to, the player will always go at the same speed? As opposed to my solution in my first post?

In this code, what would you change to allow this?
 

kburkhart84

Firehammer Games
If you are wanting to vary capped FPS, you need to change that speed variable. I tend to calculate things based on seconds in these cases. So if I want to move 100 pixels a second, the speed is 100/roomspeed, and as long as you set roomspeed correctly ahead of time, the speed will be accurate.

Delta time works the exact same way, except that every movement has that multiplication done on the fly instead of being set ahead of time. So you would set the speed to 100, and then multiply by the deltaTime(which is however many seconds passed since last frame). The seconds is typically a fraction of a second, so the actual movement amount ends up the same. Things will move less far, more often, at higher FPS, and at lower FPS, they will move more far, less often.
 
G

GhostlyImprints

Guest
If you are wanting to vary capped FPS, you need to change that speed variable. I tend to calculate things based on seconds in these cases. So if I want to move 100 pixels a second, the speed is 100/roomspeed, and as long as you set roomspeed correctly ahead of time, the speed will be accurate.

Delta time works the exact same way, except that every movement has that multiplication done on the fly instead of being set ahead of time. So you would set the speed to 100, and then multiply by the deltaTime(which is however many seconds passed since last frame). The seconds is typically a fraction of a second, so the actual movement amount ends up the same. Things will move less far, more often, at higher FPS, and at lower FPS, they will move more far, less often.
Yes but you need to constantly change the speed variable every time the user selects a new FPS right?

Is there anything incorrect about the way I'm doing it? (my first code) or can I continue doing it that way?
 

kburkhart84

Firehammer Games
Yes but you need to constantly change the speed variable every time the user selects a new FPS right?
If you are doing it with deltaTime, you don't change the speed variable because the deltaTime multiplication accounts for the frame-rate differences. If you are doing it with capped FPS/roomspeed, then yes, you will need to change the variable each time. That said, you could do it as a global variable and do it similar to multiplying for deltaTime except using that global variable and only changing it with framerate changes instead of constantly like deltaTime works.
 
G

GhostlyImprints

Guest
If you are doing it with deltaTime, you don't change the speed variable because the deltaTime multiplication accounts for the frame-rate differences. If you are doing it with capped FPS/roomspeed, then yes, you will need to change the variable each time. That said, you could do it as a global variable and do it similar to multiplying for deltaTime except using that global variable and only changing it with framerate changes instead of constantly like deltaTime works.
Ah okay so I was doing it correct, however the way I did it only worked with capped FPS rates, correct?

Case A - Capped FPS (specific FPS values):
So this code should be fine with specific FPS values correct?

GML:
baseFPS = 30;

// User wants the FPS to go to 60:
game_set_speed(gamespeed_fps, 60);
fpsFactor = baseFPS / game_get_speed(gamespeed_fps);

// Player wants to move to the right 2 pixels, Step event:
x+=2 * fpsFactor;
In this case, I simply multiply changes by fpsFactor

Case B - Uncapped FPS (undetermined FPS):

So with uncapped FPS, this is how it should be coded:

Code:
spd = spd * (previousFrameTime - delta_time);
x+=spd;
Is this correct? Would you change anything about the above codes? Just want to make absolutely sure those two above codes are correct for those cases.
 

kburkhart84

Firehammer Games
The first case seems fine. The second case, I'm not seeing it. The second one should actually look more like the first one, except that you calculate fpsFactor(or deltaTime, same thing) based real framerate(or time passed between frames). Gamemaker actually comes with a delta_time variable, the catch is that it is in microseconds, so you need to divide it by 1,000,000 to convert it to seconds. Maybe have the following code in the begin step of some controller object.

Code:
global.delta = delta_time / 1000000;
Then, in your objects, you define your speed variable as a speed per second. And the code that does the movement, you multiply the speed by the global.delta to get how far it should move on that given frame. Note that this would be doing the same thing as in your first case, except doing it with a real-time delta time instead of the set framerate.

Honestly, I'm not 100% convinced you need deltaTime or framerate changes for anything. The main reason to support different capped framerates is in cases where you are doing things on different platforms. HTML5 for example seems to like 30FPS better than 60. But if you are just going for PC exports, I think you are better off just using a capped 60FPS and then making sure your game is fast enough to keep that speed. If you do, you won't have any issues. Its only if the performance can't keep up that problems begin, but if you set the proper minimum requirements, that problem falls on the user instead of yourself.
 

Padouk

Member
It says in the manual you still need to actually multiple values related to time/speed with a multiplier:
If you are refering to the delta_time documentation. It's not saying you "need". It's saying you "can".

There are many ways to do things... Delta timing and Fixed Frames are two school of though.

I'm a bit old school and i'm biaised toward Fixed Frames...
To me.. fixed frame rate was a solution to delta timing issues in the 90s when cpu clock speed were drastically increasing every year.
In 2010, delta timing is a solution to fixed frames for high end computer with gpus and faster clock speed vs low end computers
And obivously there are a bunch of Hybrid solutions.

You know what they say... Old is the new New...


So my game has been running under a set room speed: 30 for the longest time.
I decided I wanted to allow players to change the FPS at their will from 30 -> 144 if they wish.

To do this I've been literally changing every value related to time/velocity/etc. and multiplying it by a factor.
To me, (again with my biais toward fixed frames) Delta Timing is a growing cancer, as soon as you start using it .. it spread every where. From Sprite speed, to collision box size to physics to vector length...
You have to multiply everything on every step.

What?! Wait! That's exactly what you are describing in your original post.
From your description you already decided to multiply with a factor on every steps,
What I call cancer, you already call solution and it's all right! It's a very good solution when you spend time to implement it.
I'm assuming you already multiply your Speed, Image_speed, Timeline_speed, Alarm[], etc... on every steps with a factor.
You probably already know more about delta_timing than you think.
If things are not clear for you... You have implemented Semi-fixed timestep and it's a fairly acceptable solution. I like this reading, (obviously biaised toward my religion): https://gafferongames.com/post/fix_your_timestep/

Is my code incorrect then?
Not at all... For your original question about maintaining 120fps, 60fps and 30fps.... the answer is yes you can use fpsFactor = baseFPS / game_get_speed(gamespeed_fps); with your multiplication everywhere in every steps.
I think everything else are recommendations on how to improve your way to address the issue...
Improvement 1) don't compute fpsFactor 3520985 times per frame... just compute it once in one controller at begin_step and recycle it's value using global.fpsFactor everywhere else.
Improvement 2) don't propose 120, 60, 30 fps.. Just run at 120fps.
Improvement 3) Use frame skipping or delta timing for slower computer who can't keep up with that 120fps... (based on your context)

A bit about
Case A - Capped FPS (specific FPS values):
Case B - Uncapped FPS (undetermined FPS):

Is this correct? Would you change anything about the above codes? Just want to make absolutely sure those two above codes are correct for those cases.
Your case B is a bit wrong actually.... You're missing the "2" pixels, your using previousTime, you are not taking your 30 baseFPS into account, etc..

Let's assume your new release runs at is 120fps (game_speed)
For Any computer running fast enough. Step will be called every 1000/120 = 8.33ms
Your game was designed with 1000/30 = 33.33ms in mind.
So you would constantly need to adapt your multiplyer using fpsFactor = (1000/120) / (1000/30) = 30/120 = baseFPS/game_get_speed(gamespeed_fps)
Code:
global.fpsFactor =  baseFPS/game_get_speed(gamespeed_fps);
--

Let's assume you don't want to offer the 120, 60 and 30fps choices to your player. And you don't care about determinism... You really just want to run the game logical at 120fps.
For other computer, a bit too slow, they would fail the 8.33ms time mark...
Some frames would be running slightly late (10ms) some other would be very late (35ms)
Is it 10ms? is it 35ms? Well that' where delta_time comes into play... delta_time keep the number of micro seconds between two steps.
For example time since last step would be delta_time = 25200 microseconds => delta_time/1000 = 25.2ms
Your game was still designed with 1000/30 = 33.33ms in mind.
So you would still need to adapt your multiplyer using fpsFactor = (delta_time/1000) / (1000/30) = delta_time*0.00003
Code:
global.fpsFactor =  delta_time*SPEED_FACTOR;
--

In both cases you end up using the same fpsFactor everywhere... Like you are probably already doing, so no real changes in your approach.
Code:
x+=2 * global.fpsFactor;
Obviously I'm skipping a lot of details here... should you account for time spend in previous step and expected game speed fpsFactor = (ot-delta_time), should you use a difference factor based on the room? fpsFactor = delta_time*ROOM_FACTOR, should you handle very slow edge cases if(delta_time > 33333)

Your final would probably look more like global.fpsFactor = max((ot-delta_time)*FACTOR), MIN_FACTOR)... Since that's not my religion I will let other advocate for it if needs be.


A bit about
The idea here is to Update the game at 120fs on all computers and disable rendering every few frames depending on the computer performances.


For High end computer... you Update every step and Draw every step. (aka 120hz for drawing, 120fps for update and physics)
For Average computer... you Update every step and Draw every 2 steps. (aka 60ps for drawing, 120fps for update and physics)
For Low end computer.. you Update every step and Draw every 4 steps. (aka 30 fps for drawing, 120fps for update and physics)

Code:
// Step disable drawing every 2 frames => 120 room speed, 60draw speed
draw_enable_drawevent(frameskip++ mod 2);

// Step disable drawing every 4 frames  => 120 room speed, 30draw speed
draw_enable_drawevent(frameskip++ mod 4);
 
Last edited:
G

GhostlyImprints

Guest
The first case seems fine. The second case, I'm not seeing it. The second one should actually look more like the first one, except that you calculate fpsFactor(or deltaTime, same thing) based real framerate(or time passed between frames). Gamemaker actually comes with a delta_time variable, the catch is that it is in microseconds, so you need to divide it by 1,000,000 to convert it to seconds. Maybe have the following code in the begin step of some controller object.

Code:
global.delta = delta_time / 1000000;
Then, in your objects, you define your speed variable as a speed per second. And the code that does the movement, you multiply the speed by the global.delta to get how far it should move on that given frame. Note that this would be doing the same thing as in your first case, except doing it with a real-time delta time instead of the set framerate.

Honestly, I'm not 100% convinced you need deltaTime or framerate changes for anything. The main reason to support different capped framerates is in cases where you are doing things on different platforms. HTML5 for example seems to like 30FPS better than 60. But if you are just going for PC exports, I think you are better off just using a capped 60FPS and then making sure your game is fast enough to keep that speed. If you do, you won't have any issues. Its only if the performance can't keep up that problems begin, but if you set the proper minimum requirements, that problem falls on the user instead of yourself.
So in your example, it doesn't require you to calculate the difference in delta time (previousFrameTime - delta_time).

I will most likely use a capped framerate but this is a good alternative if I choose to go back. Thank you.

If you are refering to the delta_time documentation. It's not saying you "need". It's saying you "can".

There are many ways to do things... Delta timing and Fixed Frames are two school of though.

I'm a bit old school and i'm biaised toward Fixed Frames...
To me.. fixed frame rate was a solution to delta timing issues in the 90s when cpu clock speed were drastically increasing every year.
In 2010, delta timing is a solution to fixed frames for high end computer with gpus and faster clock speed vs low end computers
And obivously there are a bunch of Hybrid solutions.

You know what they say... Old is the new New...




To me, (again with my biais toward fixed frames) Delta Timing is a growing cancer, as soon as you start using it .. it spread every where. From Sprite speed, to collision box size to physics to vector length...
You have to multiply everything on every step.

What?! Wait! That's exactly what you are describing in your original post.
From your description you already decided to multiply with a factor on every steps,
What I call cancer, you already call solution and it's all right! It's a very good solution when you spend time to implement it.
I'm assuming you already multiply your Speed, Image_speed, Timeline_speed, Alarm[], etc... on every steps with a factor.
You probably already know more about delta_timing than you think.
If things are not clear for you... You have implemented Semi-fixed timestep and it's a fairly acceptable solution. I like this reading, (obviously biaised toward my religion): https://gafferongames.com/post/fix_your_timestep/



Not at all... For your original question about maintaining 120fps, 60fps and 30fps.... the answer is yes you can use fpsFactor = baseFPS / game_get_speed(gamespeed_fps); with your multiplication everywhere in every steps.
I think everything else are recommendations on how to improve your way to address the issue...
Improvement 1) don't compute fpsFactor 3520985 times per frame... just compute it once in one controller at begin_step and recycle it's value using global.fpsFactor everywhere else.
Improvement 2) don't propose 120, 60, 30 fps.. Just run at 120fps.
Improvement 3) Use frame skipping or delta timing for slower computer who can't keep up with that 120fps... (based on your context)

A bit about
Your case B is a bit wrong actually.... You're missing the "2" pixels, your using previousTime, you are not taking your 30 baseFPS into account, etc..

Let's assume your new release runs at is 120fps (game_speed)
For Any computer running fast enough. Step will be called every 1000/120 = 8.33ms
Your game was designed with 1000/30 = 33.33ms in mind.
So you would constantly need to adapt your multiplyer using fpsFactor = (1000/120) / (1000/30) = 30/120 = baseFPS/game_get_speed(gamespeed_fps)
Code:
global.fpsFactor =  baseFPS/game_get_speed(gamespeed_fps);
--

Let's assume you don't want to offer the 120, 60 and 30fps choices to your player. And you don't care about determinism... You really just want to run the game logical at 120fps.
For other computer, a bit too slow, they would fail the 8.33ms time mark...
Some frames would be running slightly late (10ms) some other would be very late (35ms)
Is it 10ms? is it 35ms? Well that' where delta_time comes into play... delta_time keep the number of micro seconds between two steps.
For example time since last step would be delta_time = 25200 microseconds => delta_time/1000 = 25.2ms
Your game was still designed with 1000/30 = 33.33ms in mind.
So you would still need to adapt your multiplyer using fpsFactor = (delta_time/1000) / (1000/30) = delta_time*0.00003
Code:
global.fpsFactor =  delta_time*SPEED_FACTOR;
--

In both cases you end up using the same fpsFactor everywhere... Like you are probably already doing, so no real changes in your approach.
Code:
x+=2 * global.fpsFactor;
Obviously I'm skipping a lot of details here... should you account for time spend in previous step and expected game speed fpsFactor = (ot-delta_time), should you use a difference factor based on the room? fpsFactor = delta_time*ROOM_FACTOR, should you handle very slow edge cases if(delta_time > 33333)

Your final would probably look more like global.fpsFactor = max((ot-delta_time)*FACTOR), MIN_FACTOR)... Since that's not my religion I will let other advocate for it if needs be.


A bit about
The idea here is to Update the game at 120fs on all computers and disable rendering every few frames depending on the computer performances.

For High end computer... you Update every step and Draw every step. (aka 120ps for drawing, 120fps for update and physics)
For Average computer... you Update every step and Draw every 2 steps. (aka 60ps for drawing, 120fps for update and physics)
For Low end computer.. you Update every step and Draw every 4 steps. (aka 30 fps for drawing, 120fps for update and physics)

Code:
// Step disable drawing every 2 frames => 120 room speed, 60draw speed
application_surface_draw_enable(frameskip++ mod 2);

// Step disable drawing every 4 frames  => 120 room speed, 30draw speed
application_surface_draw_enable(frameskip++ mod 4);
Thank you for this incredibly well-made reply. I'll consider all of your solutions/improvements. I really like the idea of drawing only so every steps for lower end computers. I'm going to test that out on a blank game.

I have a few questions about your frame-skipping solution before I start testing it:

1. It doesn't require you to use deltas / multiplication factors for everything? Just enabling/disabling the application surface every so often?

2. I'm assuming that code goes in the Begin Step event? Or am I wrong? And frameskip would begin at 1?

Thank you!
 

kburkhart84

Firehammer Games
So in your example, it doesn't require you to calculate the difference in delta time (previousFrameTime - delta_time).
Nope, because Gamemaker already calculates it for you. You just need to multiply it by 1,000,000 to go from microseconds to seconds, and then use that value to multiply everywhere that needs something to be calculated by something per second, like movement.
 
G

GhostlyImprints

Guest
Nope, because Gamemaker already calculates it for you. You just need to multiply it by 1,000,000 to go from microseconds to seconds, and then use that value to multiply everywhere that needs something to be calculated by something per second, like movement.
Thanks!

At this point I'm a bit inclined to switch to a capped 60 FPS instead of multiplying everything.

I'm also inclined towards Padouk's solution with disabling/enabling drawing the application surface for a specific set of FPS values.
 

kburkhart84

Firehammer Games
At this point I'm a bit inclined to switch to a capped 60 FPS instead of multiplying everything.
This is what I currently do. As long as you make sure you are over 60FPS, it works great. Then you push a minimum requirement spec so that you know it works, and if it doesn't, that's on the end user.

Another disadvantage of using deltaTime is in the specific case of massive slowdown, you will get lots of jumpy movement, though it gets from A to B in the same time period. With capped framerate, if things slow down, the thing fully slows down, which means that it looks ugly, but at the least objects don't jump from here to there with no chance in between to change movement(imagine falling off a ledge because you were underperforming at the wrong time and couldn't jump).
 
To me.. fixed frame rate was a solution to delta timing issues in the 90s when cpu clock speed were drastically increasing every year.
In 2010, delta timing is a solution to fixed frames for high end computer with gpus and faster clock speed vs low end computers
And obivously there are a bunch of Hybrid solutions.

You know what they say... Old is the new New...
No; the timing issues in 90s PC games were not caused by delta timing. They were caused by an uncapped framerate with no synchronization. There was nothing telling the CPU to wait after a step was completed, it just pushed out frames as fast as they were processed. There were two solutions: limiting framerate, and delta timing. Delta timing was not a recent invention, it came onto the scene in the 90s with the advent of 3D games. Games like Quake, Duke Nukem, System Shock, Half-Life, Thief, Deus Ex—all utilized delta timing to some extent.

The idea here is to Update the game at 120fs on all computers and disable rendering every few frames depending on the computer performances.

For High end computer... you Update every step and Draw every step. (aka 120ps for drawing, 120fps for update and physics)
For Average computer... you Update every step and Draw every 2 steps. (aka 60ps for drawing, 120fps for update and physics)
For Low end computer.. you Update every step and Draw every 4 steps. (aka 30 fps for drawing, 120fps for update and physics)
Not a good idea. A lot of higher-end monitors are 144Hz, and running a game at 120fps on a 144Hz monitor produces a stuttery image. It's slightly better than running 60fps content on a 144hz monitor, but it's a bit ridiculous going to all that effort just to keep a fixed framerate. No need for a half-baked solution that still has problems. Not to mention a lot of games will have issues running physics and things like pathfinding at 120fps on potato hardware, rendering all that work pointless. At that point, just fix your game at 60 and make sure it works.
 
Last edited:

Padouk

Member
1. It doesn't require you to use deltas / multiplication factors for everything?
Like I said in my introduction, those are two techniques and you will have fanboys on both sides. pros and cons on both and key area and time where they make sense.

depending on how deterministic you need your simulation to be.
You "can" use delta_time and that multiplication factor everywhere. But you dont "need" to

Just enabling/disabling the application surface every so often?

Disabling all « drawing » or compute heavy events once in a while
Yeah.. thats the concept of frame skipping. Im talking about draw_enable_drawevent and application_surface_draw_enable not to confuse with
application_surface_enable

2. I'm assuming that code goes in the Begin Step event? Or am I wrong? And frameskip would begin at 1?
As long as it comes before the draw events
 
Last edited:
Top