Annoying Invoke() timing inconsistency

I have a gun with a 0.1 second cooldown time

The code is as following:

if(!isCoolingDown){
    isCoolingDown = true;
    fire();
    Invoke("CoolDownFinished", CoolDownTime);
}

function CoolDownFinished(){
    isCoolingDown = false;
}

So pretty straightforward, however I get inconsistent times at low cooldown values such as 0.1

I checked values by adding the following code:

function Update(){
if(isCoolingDown){
   timer += Time.deltaTime;
}
}

if(!isCoolingDown){
    isCoolingDown = true;
    fire();
    Invoke("CoolDownFinished", CoolDownTime);
}

function CoolDownFinished(){
  Debug.Log(timer);
  timer = 0;
    isCoolingDown = false;
}

Here is a sample of timer values that I get when setting CoolDownTime to 0.1

WHAT GIVES MAN!?!?

these inaccuracies causes the the occasional 2 bullets to be clumped together. It’s not affecting my game in a major way thankfully, BUT it still bothers me and it still technically makes the gameplay slightly different.

How would one implement a machine gun then with a high fire rate?

Consider changing your code structure.

I present to you the simplest way to implement fire rate :

var fireRate: float = 0.1;
private var nextFire : float;

function Update(){
if(Input.GetButton("Fire1") && Time.time > nextFire){ //you'll probably have to tweak the Input part
nextFire = Time.time + fireRate;
Fire(); // Call your Fire function
}

Unity actually have this in the docs under Input.GetButton.

If Time.time has any relation to Time.deltaTime then i doubt that approach will be more accurate because i have tested a Time.deltaTime timer and it was still innacurate.

BUT i will try Time.time timer and lets see what happens.

all events fire on a frame update, dosnt matter if its a coroutine, fired with a with a invoke, or triggered on a Update using a if statement or a timer.

invoke and coroutine will just find the frame to execute on that is closest to the time interval.

Why does accuracy greater than that matter for you?

Also using Time.time will actully give a worse result than adding to a timer with time.deltaTime.
Time.time is the time from game start so as the game runs for long amounts of time this number will run into floating point precision problems.

I didn’t think it would matter to me, but when i fire the gun at .1 cooldown time the stream of bullets is not spaced out consistently. You can SEE IT VISUALLY!
I’ve ignored the issue for a long time but now i’ve returned to update the game and I’m trying to see if it’s possible to fix it.

Here’s the bullet stream using 0.1 cooldown, as you can see the 2 bullets at the end are closer together than they’re supposed to be. This happens repeatedly while shooting.
2608171--182870--uneven.png

Here’s a pic using 0.15 cooldown, as you can see even just adding .05 to the cooldown time makes any inaccuracies visually unnoticeable.
2608171--182871--even.png

But i need my cooldown to be 0.1
My fixed timestep is .04 but i don’t understand how that would make a difference since I spawn and apply force to the bullet in FixedUpdate()

Nevertheless i even tried changin code to setting bullet velocity directly in FixedUpdate() and Update() but neither way removed the problem.
I also tried spawning the bullet in Update() before applying a force in FixedUpdate(), still no dice.

This is why im convinced it’s just an issue with the timer.

How do people create a consistent stream of bullets in Unity?
ANYONE?!?

function FixedUpdate() {
    if(canShoot){
        canShoot = false;
        shot = PoolManager.Pools["Bullets"].Spawn(bulletTypes[fireMode],bulletSpawn.position,ch_transform.rotation);
        shot.GetComponent.<Rigidbody2D>().AddForce(lookPos.normalized * shotForce);
      
    }  
}

You can fix that visual disparity for the low reload rates. when you spawn a bullet, also shift it in its forward direction based on the amount of time that was leftover in the timer value. so if your firing every 0.1f seconds and the timer reads 0.15f on the frame you spawn the bullet. spawn the bullet and then translate it forward equal to its speed * (0.15f-0.1f).

for really high attack rates (where you could spawn more than one bullet per frame) you would have to spawn multiple bullets and offset each of their transforms indivually based on how much time is left on the timer.

however making attack speeds that high is usually bad. first off there’s overhead to spawning something so fast. Object Pooling can reduce that overhead, but there’s still limits. having thousands of projectiles on the screen, even if object pooled, has quite a foot print. there’s also the fact that the player likely wouldn’t tell the difference between super high fire rate vs a slight lower fire rate with bullets that deal more damage (equal in DPS).

2 Likes

This is the most correct way to have your bullet evenly spread out regardless of framerate and timer inconsistency

Yea i guess that would work thanks,
but man it sure feels “hacky”

hmm doesn’t seem to be working, i think maybe im doing it wrong.

In my fixed update I tested quick dirty implementation like this:

        var timerOffset : float = timer - .1;
   
        shot = PoolManager.Pools["Bullets"].Spawn(bulletTypes[fireMode],bulletSpawn.position,ch_transform.rotation);
        shot.GetComponent.<Rigidbody2D>().AddForce(lookPos.normalized * shotForce);
        shot.GetComponent.<Rigidbody2D>().position += (shot.GetComponent.<Rigidbody2D>().velocity *  timerOffset);

From my understanding this should work.
Although i don’t like explicitly moving a rigidbody outside of the phyiscs engine, isn’t that horrible for performance?

EDIT:

just debugged position after translating it and I’m always getting the default starting position!
This is using this code:

        var timerOffset : float = timer - .1;
     Debug.Log("OFFSET:  " + timerOffset);
        shot = PoolManager.Pools["Bullets"].Spawn(bulletTypes[fireMode],bulletSpawn.position,ch_transform.rotation);
        shot.GetComponent.<Rigidbody2D>().AddForce(lookPos.normalized * shotForce);
        shot.GetComponent.<Rigidbody2D>().position += (shot.GetComponent.<Rigidbody2D>().velocity *  timerOffset);
Debug.Log("POSITION:  " + shot.GetComponent.<Rigidbody2D>().position);

EDIT2:

AHH crap its because velocity is still ZERO right after i apply force lol…
So i have to predetermine what speed the bullet should be after a force is applied? man that’s annoying…

EDIT 3:
ehhh i don’t know doesn’t seem to be workin right for me:
I figured out bullet speed to be 16,
changed code to say:
shot.GetComponent.().position += (lookPos.normalized * 16 * timerOffset);

Still getting clumped bullets,
tried making it only on positive offsets, still getting clumped bullets.

I’m just gonna set coolDown to .11 instead of .1, that’s all it takes to make the stream look consistent…

This is the answer. Anytime you need to do something this close to the frame rate you’ll need to do some weird tricks to compensate.

Welcome to game dev. It’s all smoke and mirrors and hacks.

lol dont i know it.