- Home /
Framerate Independent Firing Rate (delta time?)
I am attempting to make a consistent firing rate.
I have done a lot of research on figuring out consistent rates that are independent of framerate and most of the articles about it use deltaTime. However, most timers do not. I have a timer that works as long as the interval is not less that "0.2". Any number less than that has inconsistent results depending on the framerate. Specifically, when I play the project in the maximized window, versus a small window.
Here is the code I am using. I know this has been beaten to death, so forgive me, please. I assume I must use some form of deltaTime multiplier somewhere.
using UnityEngine;
using System.Collections;
public class Shooter : MonoBehaviour {
// public GameObject bullet;
// public Transform cam;
public float interval;
public float lastFire = -9999;
public int shotsFired;
void Update ()
{
if(Time.time < 5)
{
if(CanFire ())
{
lastFire = Time.time;
// Instantiate(bullet, cam.position, cam.rotation);
shotsFired ++;
}
}
}
bool CanFire()
{
return Time.time >= lastFire + interval;
}
}
I have toggled out the bullet references so that it can be easily tested. The reason I know it is inconsistent is because I implemented a test that lasts for a specific amount of time. I realize my test may be faulty and could be the actual problem. If so, please let me know.
I really appreciate any and all help.
Thanks, and God bless!
Howey
Why is there an
if(Time.time < 5)
This will prevent you from firing anything after 5 seconds of ingame time.
And btw: Update is fired whenever there is time to draw something. If you want framerate independent code, you must put it into FixedUpdate ins$$anonymous$$d.
When your framerate goes down, Update() may not be called for a while. The FixedUpdate calls are (within reasonable limits) guaranteed to be called to create the effect of a fixed framerate for your updates.
for extremely simple timers in Unity, use InvokeRepeating. Search here or unityGE$$anonymous$$S.com
Inputs are only evaluated once per frame. So inputs will always be varying and always be frame rate dependent. There is not much you can do about that.
However, if your frame rate swings that badly that it is a problem, then no player is going to play your game anyway. Optimize for frame rates of 10 and more and you should be fine.
If you want to fire at least one salvo when the user hits the fire button, then use the following method:
To gather the input use the Update() method, which is called before the FixedUpdate() method is called. Note that if your frame rate is high, then FixedUpdate may not be called at all between two calls to the Update method. So on Update() set a flag that records that the user presses the fire button and then act on that flag while in FixedUpdate() and clear the flag after you successfully fired.
If your flag is actually the frame-number, then you are guaranteed to fire the whole time between two updates:
int fireFrame;
void Update()
{
if (isFireButtonPressed())
{
fireFrame = Time.frameCount;
}
else
{
fireFrame = -1;
}
}
void FixedUpdate()
{
if (fireFrame == Time.frameCount)
{
FireYourGuns();
}
}
Thanks for the info Rabbit, I got it to work by just putting the Input reception in Update and the rest in FixedUpdate.
Answer by Howey-Do-It · May 11, 2013 at 07:47 PM
"if(Time.time < 5)" is used to create a consistent test of firing rate between varying framerates.
Here are the results of the tests of all of the recommendations you guys gave me: This tests the number of shots fired in a 5 second period with slower and faster framerates and a 0.01 time interval between shots:
Coroutine: Slow = 93 shots, Fast = 147 shots
Invoke: Slow = 130, Fast = 193
Original: Slow = 135, Fast = 293
FixedUpdate Original: Slow = 250, Fast = 250
// This is the Original test
void OriginalTest()
{
if(CanFire ())
{
lastFire = Time.time;
shotsFired ++;
}
}
bool CanFire()
{
return Time.time >= lastFire + interval;
}
// This is the Invoke test
void InvokeTest()
{
if(canFire)
{
canFire = false;
shotsFired ++;
Invoke ("invoke", interval);
}
}
void invoke()
{
canFire = true;
}
// This is the Coroutine test
void CoroutineTest()
{
if(canFire)
{
canFire = false;
shotsFired ++;
StartCoroutine(Wait(interval));
}
}
IEnumerator Wait(float waitTime)
{
yield return new WaitForSeconds(waitTime);
canFire = true;
}
}
Results:
The FixedUpdate method appears to be the only one that works independent of the framerate. However, that creates another issue, which is that FixedUpdate tends to completely miss Input sometimes.
Is there a solution for that problem?
I do want it to fire exactly when the player presses the fire button.
Thanks again, I really appreciate all of your responses.
Thanks Rabbit-Stew I figured out the Input, generally I am using GetButton rather than GetButtonDown. That way there is no way to miss the Input.
Thank you all, God bless!
Howey
Answer by eyee · May 11, 2013 at 10:51 AM
Hello!
It would be much easier to use a coroutine. Set CanShoot to false when the Player is shooting and start a Coroutine with the amount of time.
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
void Shoot(){
if(CanShoot){
CanShoot = false;
print("Shooting");
StartCoroutine(Wait(0.1F));
}
}
IEnumerator Wait(float waitTime) {
yield return new WaitForSeconds(waitTime);
CanShoot = true;
}
}
I hope this helps!