The Problem of variable time steps

I wanted to hear some opinions on the inconsistencies that arise within most game engines considering that they use variable frame-rates for game logic. If you ever tried to create a Gun with a high fire-rate in Unity you may have noticed these problems for all shots after the first one:

  1. Gun sound-intervals are not homogen, sometimes the audio distance between shots is longer because the frame-rate is different from the fire-rate Workaround Use specifically lengthened looping sounds for automatic fire or do some cumbersome audio programming for precise timing -which is pure pain and never works out, for me at least
  2. Your frame-rate will cap your fire-rate when using the traditional approach of shot instantiation
    Traditional Shot Instantiation
float fireRate = 50F;
float nextFire = 0F;
void Update(){
    if(Input.GetButton("Fire1")  Time.time > nextFire){
        nextFire = Time.time + (1F/fireRate);
        shot();
    }
}
  1. Instantiation is not homogen for shots after the first one if you don’t take precautions
    Interpolated Instantiation

The alternative is to create bullets that should have been fired between frames in an interpolated manner

private void InstantiationInterpolation(){
       
        int shotsMissed    = (int)(fireTimer/rofTime);    //# shot instantiations missed until this frame
        float smallestForeshot = fireTimer%rofTime;    //time difference from now to the time the LAST bullet should have been instantiated
        fireTimer -= shotsMissed * rofTime;            //subtract before shotsMissed gets decreased
       
        while(shotsMissed > 0){                        //until all missed shots are fired, oldest are fired first
            shotsMissed--;
            float foreshot = shotsMissed * rofTime + smallestForeshot;    //time that has passed after the bullet should have been instantiated
            float lerpFactor = foreshot/Time.deltaTime;                    //calc lerp factor: lerping FROM current TO Last Frame, change that if recoil is calced between shots
            InterpolatedInstantiation(foreshot, lerpFactor);            //call costum Instantiation
        }
    }
   
    private void InterpolatedInstantiation(float foreshot, float lerpFactor){
       
        if(GameMenu.instance.bulletInstantiationInterpolation){lerpFactor = 0F;}
        Vector3        position = Vector3.Lerp        (thisTransform.position, previousPosition, lerpFactor);//position at which this shot would have been instantiated if we hade continous time
        Quaternion    rotation = Quaternion.Slerp    (thisTransform.rotation, previousRotation, lerpFactor);//rotation at which this shot would have been instantiated if we hade continous time
           
        //foreshot is applied in Projectile.cs because missiles may modify startspeed   
        //Found out distances between shots on low frameRates finally worked when multiplying offset with 2, i dont know why...
        //Vector3        instPos  = rotation * Vector3.forward * lerpFactor * Time.deltaTime * speed * 2F;    //foreshot can be calculated with lerpfactor too
        //Vector3        instPos  = rotation * Vector3.forward * foreshot * speed * 2F;
    ...

So basically, every game that does not use a fixed-update-interval and ties it to a regular real-world-time-interval (which is every game after 2000?) becomes an indeterministic mess if viewed through the lens perfectionism, but well, we knew that didn’t we?

Note:

I’m curious why you need a weapon that fires more then 60 times per second. Simply solve the issue with game design. Almost all of your issues disappear if you drop your fire rate down to ten times per second or so.

According to google the fastest machine gun fires at 50 rounds per second. So 10 rounds per second is probably in the ball park for most weapons.

If you fire at steady firing rate, you expect that the bullets are evenly spaced. The problem arises when you firing rate is much higher than the frame rate. If you instantiate your bullets naively (without any correction), you are going to miss some bullet and they won’t be evenly space… so don’t do that.

Instead, you would need to apply some corrections to make a believable high firing rate. For instance you can keep track of the last fired bullet. Then at each frame, you just have to fill up the gap to that last bullet, that is, compute how much bullet should have been fired, instantiate and place them accordingly (you know their velocity, when they should have been fired, so you can compute their current position). This way no bullet is missed and they are correctly placed, given the illusion that they are fired at a high rate.

About the sound, you don’t have to play a distinct sound for each single fired bullet, rather play a looped sound that already contains several bullets firing, and if the firing rate is high enough, the player won’t notice if they are sync-ed or not.

1 Like

It is also inconsistent for lower fire rate when shoots have different amount of frames between them due to timing interferences, also lower fire rate only does not solve what happens when the frame rate goes down

That is what i proposed in my solutions within the Spoiler Tags. Also the sound fix, but i can tell you that player will notice, sync, ever played hawken? Maybe an additional side thought to that: Multiplayer: if you e.g. would deliberately create a framerate drop on your pc after firing in client side detection netcodes (like overwatch) you might instantiate all bullets instantly between two frames hitting the same rigidbody within one frame creating instant kills before the receiver had the chance to get the first bullet (enables cheating)

And what about the recoil for the inbetween-shots? Or recoil in general, e.g. i coded a physical based recoil system where guns have a mass and a PID-Controller that drives them back to the Aimpoint. I need to manually check by interpolation where the tip of the gun was pointing to at the moment the gun would have released a shot, or the recoil patterns would vary greatly with frame rate These are just things that cross my mind and require a lot of thought and the right implementation. I am not building an FPS, i just tackled a recoil implementation recently and these things bugged me.

If you want to check it out: http://marrt.elementfx.com/?page=7 (note: i unknowingly broke the target mini game for the current build)