Gun firing faster at over 60 FPS

I have a simple function that pauses a weapon for it’s firerate.

function PauseWeapon(PauseTime, Weapon)
	local TimeToUnpause = tick() + PauseTime
	repeat runservice.Stepped:Wait()
	until
	tick() >= TimeToUnpause

	Weapon.CanShoot = true
	Weapon.IsShooting = false
end

However FPS unlocker seems to have a sightly less waiting time than typical 60 FPS, here’s a debug result, which recorded the tick() in between.
60 FPS:
image

~ 120 FPS:
image

How can I fix this? Even if I change it to using wait(time) the same result applies.

1 Like

You can use firerate * RunService.HeartBeat:Wait() which acts as the deltatime.

2 Likes

Well considering that the difference between the 120 FPS and the 60 FPS seems to be around the time a single frame takes wouldn’t this be expected? If it is a huge deal you could try subtracting the average time for a single frame from the TimeToUnpause.

1 Like

Roblox caps FPS at 60 though…
Im pretty sure its against ToS to use FPS unlockers

2 Likes

It’s not against ToS, they are allowable.
As stated here, on github at the Disclaimer section:

EDIT (August 11, 2019): At the engineering panel on day 1 of RDC 2019, Adam Miller, VP of Engineering & Technology at Roblox, made a personal guarantee that anyone using Roblox FPS Unlocker will not be banned from Roblox.

1 Like

Remember, this function Wait() doesn’t fire every nanosecond, it fires every frame.

So basically the resolution of this Wait() function is tied to the framerate.

It’s a bit hard to explain it, but if your “cooldown” for this gun is 0.2 seconds, the gun won’t fire every 0.2 seconds, it will fire the first frame that is activated after 0.2 seconds.

So at 60 FPS, i.e ~17 MS per frame: if frame 1 ends while the gun has 18 ms more of cooldown before it fires, then in frame 2, the gun will have 1 ms more of cooldown; it won’t fire. In frame 3, the gun will have -17ms cooldown and will fire - essentially once every 217 ms instead of once every 200 ms.

If your framerates are higher, this “resolution” is higher, so the “real” cooldown will be lower.

The solution to this is to incorporate some kind of interpolation; instead of firing at the start of the frame, you gotta fire at the start of the frame and move the bullet forward by some amount:

-- distance = velocity * time
distanceToOffsetBulletStart = bulletVelocity * (realTimeElapsed - bullet_rof)
-- distance = bullet velocity * (how long ago the gun should have fired in seconds)

and when the timing is right, you gotta fire two bullets in the same frame (in really high fire rates).

3 Likes

Can you explain how would this be done with the reference to my function?

The more frames the more accurate, sometimes your 60 fps can be 40 fps without knowing. But, if you unlock even more fps, it will never fire more than1 / PauseTime times a second, only less.

I think using repeat runservice.Stepped:Wait(1/60) fixes this problem but is it a good solution? I heard that using wait(1/60) is not a good approach.

1 Like

RBXScriptSignal:Wait() does not take any arguments, so it makes no difference. One of the ways to do it is to get the excess time waited and subtract it from the original wait time for the next shot. If that wait time is already less than 30ms, accumulate it and fire 2 shots at once, once the excess is equal to the regular wait time.

2 Likes

I don’t think there is a way around the problem I described. It’s a fundamental side affect of the code executing once per frame (tick). On the server, this is even more prevalent, since IIRC the server runs at 20 Hz (fps). Unless you’re really lucky with your frame timing (or really specific with your RenderStepped execution order and gun fire rate) you won’t get around this with a wait() or Wait() delay.

First off, the 0.01 seconds won’t be a large noticeable issue, like said before, your guns won’t fire faster than their firerate, only slower. But; it’s good you’re fixing this, as lower FPS players won’t be able to fire their gun at the advertised firerate without a fix for this.
Gun firerates and fps have been something I’ve been looking into for quite a while. I haven’t found anything simple, however there are a few approaches you can use.

  • 1
    Use a counter that counts how many times the gun has been fired. Check this every frame, since a person could be playing at 1fps and you’d then want to fire it enough times in that one frame. You change this every time you fire a shot, and if the shots fired in a certain deltaTime is lower than what should be fired, fire until you have enough. See this unity forum question.
  • 2
    Use a more input based system like this:
-- userInputService.InputBegan connection
if (Input.KeyCode == FireButton and os.clock() >= nextFire) then
    -- Fire bullet
    nextfire = os.clock() + 60/firerate -- convert x/min to x/s firerate.
end
  • 3
    There are quire a few good resources for unity that aren’t too hard to comprehend, here are a few I’ve found: reddit, devforum, unity forum.

Hopefully some of these can help you. Framerates and lag are quite a thing to wrap your head around and it’s easy to forget they exist. Good luck!
a few things to clear up some misinformation:
cc @ElusiveEpix: wait has issues with queuing up (can wait minutes in worst cases) and a minimum wait time of 1/30s. And tick() (or preferably, os.clock()) is ~1 us (or ns, I don’t fully remember) precision.

The server heartbeat runs at 60hz, replication runs at 20hz on both the server and clients.

2 Likes

There’s a reason for this:

RenderStepped fires every frame

  • Which means that if you do RenderStepped:Wait() with a higher fps, it will refresh faster.
  • Which means this is an inconsistent method of applying a cooldown.

To counteract this, you should use delay

local event = Instance.new("BindableEvent")

delay(PauseTime, function()
	event:Fire()
end)

event.Event:Wait()
event:Destroy()

This should effectively limit the cooldown of the gun because you don’t have to deal with the wait being run at different refresh rate. It always runs at 30hz.

Alternatively, if you want, you can handle cooldowns on the server. That should increase security and make it more consistent as well.