Scrolling Game Development Kit Forum
SGDK Version 2 => Help, Errors, FAQ => Topic started by: Tanja on 2008-01-22, 09:22:02 AM
-
how can i limit the overall frame rate to a specific value?
when someone plays my game at a slower computer, i can do nothing. but if someone plays it on a faster computer (what is very likely), many animations will run too fast. i want to keep the frame rate at 60fps, not faster.
-
I was under the impression that the base-line framerate already was capped at 60fps. At least, all of my trials have run at no higher than 60fps
-
no i've seen my game running at 85. and this was a older pc...
-
Well, I get games running at 200 FPS when running in Full-screen mode. If you have that problem, turn on your video-card's V-sync (Vertical-refresh synchronization) so that it runs at a maximum speed of the refresh rate of your monitor. There's currently no built-in way to limit the FPS, though. It is possible, though I don't know where it would be, to change/add code in the drawing section of the game loop to slow down the loop.
-
maybe bluemonk knows the solution?
-
In version one, I have a frame rate limit built into the engine, but I haven'[t provided one for SGDK2 yet. I think I used a high-frequency timer and after each frame I would wait until some time value was reached before proceeding. You could develop and put similar code almost anywhere in your SGDK2 project because most code is within the game loop -- just make sure it only executes once per loop. My suggestion would be to look at the end of the Run function in GameForm.cs. Add a loop around the "Application.DoEvents()" call that repeats that line of code until the high-frequency timer reaches the appropriate value.
I think .NET does not expose a high-frequency timer (VB6 didn't either) so in order to access it, you would have to declare external function (Windows API) references to get the timer values.
I searched Google for:
QueryPerformanceCounter C#
and found this page: http://www.thescripts.com/forum/thread251697.html (http://www.thescripts.com/forum/thread251697.html)
That shows you at least how to access the function I was using, I think. There's also QueryPerformanceFrequency that tells you how fast the counter runs, which you can use to calculate a value to set your frame rate as a fraction of a second. I don't have time to go into the details now, but hopefully one of you clever folks or experts could take it from there :).
-
Here's a Frame Rate Limiter I hacked together just now as a Custom Object.
All you need to do is call CustomObjects.FPSLimiter.RegisterLimit with an argument of how many FPS to limit to. This only needs to be called once, in an initialization loop, for example. However, if you do call it every frame, it will have no adverse effects.
Call CustomObjects.FPSLimiter.UnregisterLimit to remove the limit.
To change the FPS limit, you can either re-register with a new FPS (no need to unregister first) or set CustomObjects.FPSLimiter.FPS to whatever value you want to limit to.
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace CustomObjects
{
public class FPSLimiter
{
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
[DllImport("kernel32.dll", SetLastError=true)]
private static extern bool QueryPerformanceFrequency(out long frequency);
private static long freq = -1;
private static long pCount = -1;
private static long delay = -1;
private static uint framerate = 0;
private static GameForm.SimpleNotification waiter = null;
[Description("Call once to begin limiting the framerate to the specified value.")]
public static void RegisterLimit(uint FPS)
{
if (null == waiter)
{
waiter = new GameForm.SimpleNotification(Wait);
Project.GameWindow.OnBeforeBeginScene += waiter;
}
if (framerate != FPS || delay == -1)
FPSLimiter.FPS = FPS;
}
[Description("Call once to remove all framerate limits.")]
public static void UnregisterLimit()
{
Project.GameWindow.OnBeforeBeginScene -= waiter;
waiter = null;
}
public static uint FPS
{
get
{
return (null == waiter ? 0 : framerate);
}
set
{
if (value == 0)
throw new Exception ("Cannot limit framerate to 0 FPS");
if (value != framerate)
{
framerate = value;
init();
}
}
}
private static void init()
{
if (freq == -1)
{
if (!QueryPerformanceFrequency(out freq))
throw new Exception("Couldn't get performance frequency for frame rate");
}
delay = (long)(1f/(float)FPS*(float)freq);
if (pCount == -1)
if (!QueryPerformanceCounter(out pCount))
throw new Exception("Couldn't get performance count for frame rate");
else
return;
}
private static void Wait()
{
Project.GameWindow.debugText.Write(FPS.ToString());
long newCount = 0;
QueryPerformanceCounter(out newCount);
while (newCount < pCount + delay)
{
System.Windows.Forms.Application.DoEvents();
QueryPerformanceCounter(out newCount);
}
pCount = newCount;
}
}
}
--Edit--
I have submitted the file to sgdk2.enigmadream.com for distribution. By the by, BlueMonk, I think there needs to be an area for Code objects specifically, and also an easy way to view details about and download files from that website within the SGDK2 IDE.
-
your limiter works fine, but when i close the game window this error message appears:
-
1. How do you close the game window? Is it with the close button or with the Exit item in the menu, or calling the QuitGame function?
2. What does line 274 of your GameForm.cs look like? Mine looks like if (OnBeforeDrawOverlay != null) which I don't think could cause a NullReferenceException
3. Do you have anything else that you know of that is registered as an event to run during the game loop (like an OnBeforeDrawOverlay event)?
-
1. i close the game with the X-button.
2. GameDisplay.Device.BeginScene();
3. i don't know exactly what you mean.... i have changed some things on the code, e.g. the opacity change for layers and for drawcounterassprite.
-
The quick/easy fix is to change all references to "OnBeforeBeginScene" in the FPS limiter code to "OnFrameStart". The explanation of why the problem occurs is this:
The main game loop only checks if the window is closed after the one point where user events are allowed to be processed.
User events are processed during "Application.DoEvents" in the main loop in GameForm.cs.
OnBeforeBeginScene occurs after SGDK2 has performed this check.
The FPS limiter code executes "Application.DoEvents()" while it is waiting for the next frame, which allows more user events to be processed.
If this happens during OnBeforeBeginScene, that means more user events could be processed after SGDK2 has checked. It didn't expect the window to be closed during OnBeforeBeginScene.
So the quick fix was to use a different event "OnFrameStart", which happens about the same time that the existing Application.DoEvents happens. Then SGDK2 will check for the window being closed after the FPS Limiter code runs.
-
Huh... I tried it several times myself and never got the error, though. Very le strange.