Programming an FPS limiter for Unity - c#

First of all, I know that Application.targetFrameRate exists, and it does a good enough job, but I want something more accurate. For me, it limits the frame rate to around 60.3 when set to 60, and around 204 when set to 200. Btw these are measured in builds (not in the editor) using RTSS 7.2.
So I set out to create my custom frame limiter in Unity using low level timers, but it just doesn't work correctly for some reason. Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using System;
public class FrameLimiter : MonoBehaviour
{
private FrameLimiter m_Instance;
public FrameLimiter Instance { get { return m_Instance; } }
public double FPSLimit = 300.0;
private long lastTime = HighResolutionDateTime.UtcNow.Ticks;
void Awake()
{
m_Instance = this;
}
void OnDestroy()
{
m_Instance = null;
}
void Update()
{
if (FPSLimit == 0.0) return;
lastTime += TimeSpan.FromSeconds(1.0 / FPSLimit).Ticks;
var now = HighResolutionDateTime.UtcNow.Ticks;
if (now >= lastTime)
{
lastTime = now;
return;
}
else
{
SpinWait.SpinUntil(() => { return (HighResolutionDateTime.UtcNow.Ticks >= lastTime); });
}
}
}
Basically this is a singleton that's set to execute before any other script in the script execution order, and it blocks execution until the right time has been reached.
The way it works is it keeps track of the precise time when it last allowed a frame to be rendered (it obtains the current time from a very precise low level timer, here's more detail about that). Then in every call to its Update() function, it adds 1.0 / FPSLimit seconds to it to get the time when the next frame should be rendered, then if the current time is less than this time, it blocks execution until that timestamp has been reached.
SpinWait is basically just an efficient way to block execution without pinning the CPU like an empty while loop would. But just for the record, I did try an empty while loop too, and got the same results, except with much higher CPU usage.
So if you understand how this code is supposed to work, you should see that in theory this should lock the frame rate very precisely, especially given that this timer has a precision better than 1μs (0.001ms) according to Microsoft.
But despite all that, I get about 58.8 FPS when I set this to lock at 60, which I really don't understand. I'm running the build in exclusive full screen mode with V-sync disabled. Btw I'm getting way higher frame rates with no limiting, so the base performance of the game isn't the issue.
Any help would be appreciated!

The problem was weirder than I thought. It seems like this implementation of DateTime rounds the stored time internally to a whole number of milliseconds, so my frame limiter was pacing frames at 17ms instead of 16.6666ms when set to 60 FPS.
The solution was to alter the code for the timer I'm using, and just get the raw value returned by GetSystemTimePreciseAsFileTime() instead of encapsulating it in a DateTime object.
With this change, RTSS shows a perfect 60.0 FPS lock in the build, with occasional dips to 59.9, if the limiter is set to 60.
Here's what it looks like with a frametime graph as well. I was looking around the map at different things to try to excercise the frame limiter's consistency a bit. Safe to say, I made a much better frame limiter than Unity's own :)

If your program works perfectly and the spinwait finishes exactly at the calculated time you get exactly 60fps.
Nothing is perfect so it will take a bit more than the calculated time giving you a lower frame rate.

Related

Using C# async delays in Godot

I am currently experimenting with Godot C# making a basic shooter and for the gun's fire rate I have been experimenting with different delay systems. Node Timers work although I'm trying to make the script generic, and the Timer calls seem to only call functions in the parent script.
I'm now looking at C#'s Task.Delay method and it also seems to work, with it being an async action it does not look to be affected by the frame rate or slow down the game.
My question is, is there any known issue for using Task.Delay in game applications: like is it unreliable or can it crash if too many instances of the method are called?
Here's the code below although I don't think it’s important:
private void shoot() {
//if "canShoot" spawn bullet
ShootCooledDown();
}
private async void ShootCooledDown() {
TimeSpan span = TimeSpan.FromSeconds((double)(new decimal(shotDelay)));
canShoot = false;
await Task.Delay(span);
canShoot = true;
}
My question is, is there any known issue for using Task.Delay in game applications: like is it unreliable or can it crash if too many instances of the method are called?
Not per se. There is nothing in particular wrong with Task.Delay in games, nor too many instances of it.
However, what you are doing after Task.Delay can be a problem. If you execute await Task.Delay(span);, the code that comes after might run in a different thread, and thus it could cause a race condition. This is because of await, not because of Task.Delay.
For example, if after await Task.Delay(span); you will be adding a Node to the scene tree (e.g. a bullet), that will interfere with any other thread using the scene tree. And Godot will be using the scene tree every frame. A quick look at Thread-safe APIs will tell you that the scene tree is not thread-safe. By the way, the same happen with virtually any widget API out there.
The solution is use call_deferred (CallDeferred in C#) to interact with the scene tree. And, yes, that could offset the moment it happens to the next frame.
I'll give you a non threading alternative to do that.
There are method get_ticks_msec and get_ticks_usec (GetTicksMsec and GetTicksUsec in C#) on the OS class, that give you monotone time which you can use for time comparison.
So, if you make a queue with the times it should shoot (computed by taking the current time plus whatever interval you need). Then in your process or physics process callback, you can check the queue. Dequeue all the times that are overdue, and create those bullets.
If you don't want to solve this with Godot APIs, then start a Stopwatch at the start of the game, and use its elapsed time.
But perhaps that is not the mechanic you want anyway. If you want a good old cool-down, you can start the Stopwatch when you need the cool-down, and then compare the elapsed time with the cool-down duration you want to know if it is over.
I don't have any experience with Godot.. but my idea would be....
instead of using a timer, you could store the last shoottime in a variable/field. If you're trying to shoot within the lastTimeShot+coolDown, just ignore the shoot command.
For example:
private DateTime _lastShot = DateTime.MinValue;
private void shoot()
{
TimeSpan span = TimeSpan.FromSeconds((double)(new decimal(shotDelay)));
// if the time when the last shot has fire with the cooldown time
// is greater than the current time. You are still in the cooldown time.
if(_lastShot.Add(span) > DateTime.UtcNow)
return; // within cooldown, do nothing
//if "canShoot" spawn bullet
ShootCooledDown();
_lastShot = DateTime.UtcNow;
}
Due to a valid comment of Theodor, about changing the system time would lead bug-prone gameplay.
I wrote a second version.
private Stopwatch _shootingCooldownStopwatch = default;
private void shoot()
{
var shotDelayMs = shotDelay * 1000;
// if the _shootingCooldownStopwatch is ever started
// and the ElapsedMilliseconds are in the showDelay
// we're not allowed to fire again. So exit the method.
if (_shootingCooldownStopwatch?.ElapsedMilliseconds < shotDelayMs)
return;
_shootingCooldownStopwatch = Stopwatch.StartNew();
//if "canShoot" spawn bullet
ShootCooledDown();
}
I think this would be a better solution.
When you develop games in Godot or any other game engine, you shouldn't use any timer based in the computer clock, like the Stopwatch or Task.delay. Instead, you have to manage yourself the time elapsed using the delta time from the previous frame, which is received in the _Process(float delta) or _PhysicsProcess(float delta) methods. The reason are:
The time will be more accurate in case of frame-rate drop.
If you pause the game, timer will pause too.
That's the main reason Godot offers you a Timer component that you have to attach to the current scene in order to work with it.
If you don't want to add anything to the scene, which completely reasonable, you have to get the delta, storing the elapsed time in a variable and check if this variable reach some limit.
In my games, I use my own timers with this very simple class:
public class Timer {
public float Elapsed { get; private set; } = 0;
public bool Stopped { get; private set; } = true;
public float Alarm { get; private set; } = float.MaxValue;
public Timer Start() {
Stopped = false;
return this;
}
public Timer Stop() {
Stopped = true;
return this;
}
public Timer Reset() {
Elapsed = 0;
return this;
}
public Timer ClearAlarm() {
Alarm = float.MaxValue;
return this;
}
public Timer SetAlarm(float finish) {
Alarm = finish;
return this;
}
public bool IsAlarm() => Elapsed > Alarm;
public Timer Update(float delta) {
if (!Stopped) {
Elapsed += delta;
}
return this;
}
}
```
You have to Update the timer in every frame
I am no expert in Godot but I can tell that Task.Delay() is considered better than alternatives like Thread.Sleep() for example because being asynchronous i releases the thread to the thread pool and when the time has passed it continues execution, in contrast to the latter option that blocks the thread instead.
The problem I can see is that each web server can accept a max limit of concurrent requests, by using Task.Delay() in your code you can start accumulating requests "just waiting" due to the delay. So if your app starts receiving a big amount of requests coupled with a long Delay time that might be an issue with requests queued up (delay) or even denied.
If the delay is a number of seconds (significant time) then I would probably think about storing user in a cache (you can also store in a dictionary Dictionary<string, bool> where string is the userId but this solution will not scale out, that is why I suggest a distributed cache), and check (TryGetValue()) your cache if user is allowed to shoot. If delay is a couple of microseconds (affordable time) still not an ideal solution but it will probably be a problem.
In contrast to the answer by #Theraot and its approach via await Task.Delay(span) and according to my understanding, asynchronous does NOT equal to multi-threading. Using await Task.Delay(span) won't cause your code executing in another thread. So you don't really need to use CallDeferred in this case.
Reference:
What is the difference between asynchronous programming and multithreading?
Does Task delay create new Thread?
Does the use of async/await create a new thread?

What Is an IEnumerator In C# and what is it used for in Unity?

Recently I saw a tutorial of spawning objects using C# in Unity. In that the teacher used a function like this
public IEnumerator CallSpawner()
{
yield return new WaitForSeconds(0.5f);
SpawnObstacles();
}
I wanna ask what is the use of IEnumerator function. Can't we do this by this process
float diffTime = 0f;
private void Update()
{
if(Time.time - diffTime == 0.5f)
{
diffTime = Time.time;
SpawnObstacles();
}
}
I read the documentation but couldn't understand it..
what is the use of IEnumerator function
IEnumerator there isn't a function, it's a return type. C# doesn't have functions either (but I know what you mean) - in C# we call them methods.
IEnumerator being so called implies it is an interface, so any class that implements the IEnumerator interface can be returned by this method
In practice in this use it seems that it's actually more of a hack than intending to provide the true intent of an enumerator, which is to step-by-step rifle through(or generate) a collection of things.
When you use a yield return statement within a method "some magic happens" whereby it's not a return in the classic sense, but creates a facility whereby the code can resume from where it left off (calling for the next item out of the returned enumerator will cause the code to resume from after the yield, with all the state it had before, rather than starting over).
If you look at the MSDN example for yield:
public class PowersOf2
{
static void Main()
{
// Display powers of 2 up to the exponent of 8:
foreach (int i in Power(2, 8))
{
Console.Write("{0} ", i);
}
}
public static System.Collections.Generic.IEnumerable<int> Power(int number, int exponent)
{
int result = 1;
for (int i = 0; i < exponent; i++)
{
result = result * number;
yield return result;
}
}
// Output: 2 4 8 16 32 64 128 256
}
The loop is controlled by i; if this wasn't a yield return then this wouldn't function as intended (it couldn't return an enumerator for a start but we'll leave that out). Suppose it was just a normal return, the loop would never loop at all; the code would enter, start the loop, hit the return, and just return a number one time and all memory of where the loop was would be forgotten.
By making it a yield return, an enumerator is returned instead, and a small set of "saved state" is set up whereby the loop can remember the current value of i - each time you ask for the next value, the code resumes where it left off from (ie just after the yield), the loop goes round again and a different value is yielded. This continues up to the max of course.. at which point the returned enumerator says it has no more items
You could yield forever, too.. If the code can never escape the loop then it will yield/generate forever
In this case you have to use yield return new WaitForSeconds because that's how WaitForSeconds is intended to work. Yielding gives up an enumerator to the calling method, which is then free to enumerate it. From the docs it looks like this is deliberately done on the next frame, so using yield (perhaps repeatedly) is a way of arranging a block of code that occurs across several frames without having some sort of external state management that remembers where the process is up to and a wordy
if state = 1 then close the door and add 1 to the state,
else if state = 2 then light the torch and add 1
else if state = 3 ...".
You can just
yield,
close the door,
yield,
light the torch,
yield ..
Can't we do this by this process
Sure, looks reasonable; look at the clock 100 times a second and if 0.5 seconds have passed since you first looked at the clock, spawn the obstacles
I'd imagine (never used Unity; don't profess to know anything about it other than having read the docs for this one function) that your Update loop has a lot more to be getting on with, so handing a process off to a dedicated wait-then-do is more efficient than spending all your time looking at a clock and carrying out a potentially complicated calc to work out if you should do something; most things in life that start out as poll-every-x-milliseconds benefit from being switched to an "if the event occurs, react to it" way of working
The code you are looking at only exists in Unity3d and DotNet Framework < 4.0.
The reason you can't find any documentation on this is:
It is an evil hack (Pioneered by Jeffrey Richter) that abuses the IEnumerable state-machine generator feature of the C# compiler. See https://www.codeproject.com/Articles/39124/Learn-How-to-Simplify-the-Asynchronous-Programming for an example.
The Microsoft guys have long since added this hack into the dot net compiler, and they called this feature async/await.
Unfortunately, Unity3d is based on an old version of Mono, which does not support async/await.
Luckily most of the features of async/await can be done using this approach
There are a few reasons to go with Jeff's Power Threading Library approach.
Flow. You have a single method that sets up the wait, defines the wait time and executes the payload, and in that order.
Performance. Your suggested code will run many times per second, slowing down the system as you poll for the timeout to complete. Instead, the PTL approach supplied a callback event, which is scheduled in the future.
Clean up. Your code will continue to run, checking for 0.5 long after SpawnObstacles() ran once.
Fuzzy timing. If your PC is skipping frames due to lag, performance etc. You might have skipped the 0.5 frame completely. More-over, it is possible, that 0.5d will never occur (see Jon Skeet's Pony Fail post, you are doing a double to double comparison). The timer/callback would definitely fire at sometime after 0.5 seconds.
But the main reason would have to be Flow. In this very simple example, we won't easily be confused. However imagine the following effect.
New enemies will spawn every 10 seconds
Each spawning will follow the fibonacci sequence
At 10s 1 enemy will spawn
At 20s 1 enemy
At 30s 2 enemy
At 40s 3 enemy
At 50s 5 enemy
At 60s 8 enemy
etc
This would be pretty difficult to write with the update method.
However you can do it with the IEnumerable easily.
public IEnumerator CallSpawner()
{
int current = 1;
int last = 0;
while(true)
{
yield return new WaitForSeconds(10f);
SpawnEnemies(current);
var next = current + last;
last = current;
current = next;
}
}
As a bonus, you will notice that all the variables are nicely encapsulated in the Method, as opposed to in free floating fields.
Meaning you can run multiple CallSpawner() without them interfering.

How to optimise countdown timer?

Dear stackoverflow community,
I have countdown timer for my double points power up and now I have problem because my code works fine in game,but when timer is active game is lagging , not too much but any lag is not good for my game because the player needs to play smoothly without any unoptimised component..
I have this code and I bet the game is lagging because the code is in update method ( I tried to put it in game manager script but then timer won't countdown so that is not solution )
This is the code ( Thanks to stackoverflow user #siusiulala , who wrote me the working code)
but seems like it needs to be in another method or something because Update method running performance when has countdown inside.
private void Update(){
if (isDoublePoints)
{
// Countdown the timer with update time
powerUpTimer -= Time.deltaTime;
Debug.Log("TIMER ISS " + powerUpTimer);
if (powerUpTimer <= 0)
{
// End of power up time
isDoublePoints = false;
}
}
}
public void OnPickPowerUp(float buffTime)
{
powerUpTimer += buffTime;
}
I hope someone will give solution to lagg because I saw a lot of games that has power up systems without any laggs inside...
Thank you stackoverflow, without you my game would not ever come to end :)
what trollingchar's answer says about the Debug.Log is correct.
To use the [SerializeField] might be considered as a dirty and lazy hack by some people. Because it has the side-effect that it is now serialized, that means the value is stored in the assets. It's not bad but if you are exact it shouldn't be done with fields that will be changed on runtime anyway.
Instead you can simply go to the Inspector, open the context menu and set it to Debug Mode
this makes the Inspector not use the Custom EditorScripts but instead reveal all private fields (of Serializable types).
For example for the Transform component
However way more efficient than using the Update method with a flag at all would be to rather use a Coroutines.
Coroutines can be started and run parallel (every frame right after) the Update method but the advantage: when a coroutine is finished - it is finished and doesn't continue checking the bool flag every frame.
So whenever you pickup a PowerUp instead of setting the flag to true rather use
StartCoroutine(PowerUpRoutine());
and implement a routine like
private IEnumerator PowerUpRoutine()
{
isDoublePoints = true;
while(powerUpTimer > 0)
{
// Countdown the timer with update time
powerUpTimer -= Time.deltaTime;
//Debug.Log("TIMER ISS " + powerUpTimer);
// yield in simple words makes Unity "pause"
// the execution here, render this frame and continue from here
// in the next frame
yield return null;
}
// End of power up time
isDoublePoints = false;
}
public void OnPickPowerUp(float buffTime)
{
powerUpTimer += buffTime;
// avoid concurrent routines
if(!isDoublePoints) StartCoroutine(PowerUpRoutine());
}
In order to display it in your game you can use a Text or TextMeshPro and set the text like e.g.
[SerializeField] private Text _text;
private IEnumerator PowerUpRoutine()
{
isDoublePoints = true;
while(powerUpTimer > 0)
{
// Countdown the timer with update time
powerUpTimer -= Time.deltaTime;
//Debug.Log("TIMER ISS " + powerUpTimer);
// set the text of the Text component to display the value
// for the $ symbol google for "c# string interpolation"
_text.text = $"TIMER IS {powerUpTimer:00.00}";
// yield in simple words makes Unity "pause"
// the execution here, render this frame and continue from here
// in the next frame
yield return null;
}
// End of power up time
isDoublePoints = false;
}
From my experience, Debug.Log() is a very expensive method. It will cause lag when called every frame. My IDE even highlights usage of Debug.Log() in Update() as warning because of that. Use this method only for debugging, and then remove.
If you want to be able to see the timer value, add [SerializeField] attribute to your field and it will show up in the inspector.
You can use the profiler by selecting Window-Analysis-Profiler, assuming you are using Unity 2018.x. It records how much time processing takes, and helps locating bottlenecks.

C# Simple 2d game - making the basic game loop

Even though i have some experience in c#, this is my First game in C#. I am trying to set up the minimal skeleton of the game. I heard that Tick Event is a bad approarch for creating the main game loop.
This is the main concept of what I am trying to implement:
Program.cs
//Program.cs calls the Game Form.
Application.Run(new Game());
Game.cs
public partial class Game : Form
{
int TotalFramesCount = 0;
int TotalTimeElapsedInSeconds = 0;
public Game()
{
InitializeComponent();
GameStart();
}
public void GameStart()
{
GameInitialize();
while(true)
{
GameUpdate();
TotalFramesCount++;
CalculateTotalTimeElapsedInSeconds();
//Have a label to display FPS
label1.text = TotalFramesCount/TotalTimeElapsedInSeconds;
}
}
private void GameInitialize()
{
//Initializes variables to create the First frame.
}
private void GameUpdate()
{
// Creates the Next frame by making changes to the Previous frame
// depending on users inputs.
}
private void CalculateTotalTimeElapsedInSeconds()
{
// Calculates total time elapsed since program started
// so that i can calculate the FPS.
}
}
Now, this will not work because the while(true) loop blocks the Game Form from initializing. I found some solutions to this, by using System.Threading.Thread.Sleep(10); or Application.DoEvents();, but I didn't manage to make it work.
To explain why I want to implement this code here is an example of the above code in use:
Lets say I want my game to do the following:
Smoothly move a 100x100 Black colored Square from point (x1,y1) to (x2,y2) and backwards, in a loop and display the FPS in the label1 of the above code. With the above code in mind, I could possibly use TotalTimeElapsedInSeconds variable to set the speed of the movement to be relevant with the Time and not the Frames, as the Frames will differ on each machine.
// Example of fake code that moves a sqare on x axis with 20 pixels per second speed
private void GameUpdate()
{
int speed = 20;
MySquare.X = speed * TotalTimeElapsedInSeconds;
}
The reason i though of using a while(true) loop is that I will get the best FPS I can on each machine.
How could I implement my idea on actual code ? (just the basic skeleton is what i am looking for)
How could I set a max of, lets say 500 FPS to make the code "lighter" to run? instead of trying to produce as many frames as possible which I suspect will needlesly over-use the CPU(?)
Frame rate has nothing to do with smoothness. Even if you accomplish 500 frames/sec the movement will be choppy or worse. The trick is to synchronize with your monitor refresh rate. So for a monitor with 60Hz you need 60 frames/sec no more no less. You can't do that by using a loop in C#. You need DirectX or XNA. These frameworks can synchronize your drawings with the vertical scan of your monitor.
You need to make own thread to that while(true)-loop:
Thread thread = new Thread(new ThreadStart(GameStart));
thread.Priority = ThreadPriority.Lowest;
InitializeComponent();
thread.Start();
Check this blog post to get more coding intuition:
https://praybook2.blogspot.com/2020/04/this-now-advanced-stuff.html
Tough it loop fast. There are many disadvantages by using the threads, consider using some ready built game engine --like Godot; where all these kinds of small problems are fixed beforehand, use threads only when they are needed.

Strange stuttering issue with multithreading

I've modified my physics from the last post: Updating physics engine ina separate thread, is this wise? like so:
public void PhysicsThread()
{
int milliseconds = TimeSpan.FromTicks(333333).Milliseconds;
while(true)
{
System.Threading.Thread.Sleep(milliseconds);
world.Step(milliseconds / 1000.0f);
}
}
As before it's running in its own thread. I'm finding something odd which I'm not sure I quite understand, it's a bit difficult to explain so I made a video recording of it: http://www.youtube.com/watch?v=qFf6oSRfVt8
If you look carefully you can see the object being fired from the cannon occasionally stutters and appears to move backwards a set amount of distance. This is only noticable on fast moving objects (such as the projectile being fired).
This has baffled me completely. I've even created a system where I cache the position of bodies in the game logic thread so that as the physics thread may update bodies positions it won't affect the positions being read in by the game logic until an update function is called within the game logic thread which updates the physics bodies positions for the game logic to read.
Any ideas what aspect of multithreading may be causing this issue? I don't think it's the physics engine updating the bodies positions during game logic and drawing as I've alrady mentioned I cache that and it remains constant throughout...
My first guess is that you may have a classic race condition, where multiple threads are competing to update the object's position without locking or ordering guarantees.
You can check Wikipedia to learn more about race conditions, locking, and other fundamentals of multithreading/multiprocessing.
It's hard to say more without seeing more of your code, especially the part that does the update.
Edit: One thing you can do is to store DateTime.Now on each loop, and compare it with the previous value. If your time delays are inconsistent you'll see it.
Another thing to check is to see how long your world.Step() function is taking to execute (again using DateTime.Now and some logging, or whatever).
If both of these indicate consistent timing, then my suspicion would fall on the physics engine. Check the position of the object before and after calling world.Step(), and if you see any weird jumps that should tell you where to look.
This should probably be a comment, but it would be difficult to post code. If you're trying to do realtime then this code is prone to time jitter as there's no guarantee that Thread.Sleep will sleep for the perscribed time. I'd use a stopwatch to measure elapsed time and use that to drive your world.
public void PhysicsThread()
{
int milliseconds = TimeSpan.FromTicks(333333).Milliseconds;
var stopwatch=System.Diagnostics.Stopwatch.StartNew();
while(true)
{
System.Threading.Thread.Sleep(milliseconds );
world.Step(stopwatch.ElapsedTicks);
stopwatch.Restart();
}
}

Categories