If not handled properly, the lower the player’s frame rate is, the slower the player will be able to fire weapons. This issue is prevalent throughout the whole game development industry, yet solutions remain scarce.
The Problem
Consider a weapon with an 800 rounds per minute (RPM) fire rate. A common approach to handling fire rate is as follows:
local fireRate = 800
local fireCooldown = 60 / fireRate
repeat
-- Fire (raycast, etc.)
task.wait(fireCooldown)
until fireInput == false
However, task.wait
may yield for a longer duration than the provided fireCooldown
, resulting in a slower fire rate. As a consequence, players running at 60 frames per second (FPS) will shoot at approximately 722 RPM. Oof, that’s 10% less than expected!
800 RPM at 60 FPS resulting in 722 RPM
The Solution
To account for the excess wait time, the previous approach can be adjusted as follows:
local fireRate = 800
local fireCooldown = 60 / fireRate
local excessWaitedTime = 0
repeat
-- Fire (raycast, etc.)
local timeToWait = fireCooldown - excessWaitedTime
local waitedTime = task.wait(timeToWait)
excessWaitedTime = waitedTime - timeToWait
until fireInput == false
This significantly narrows the gap between the actual and expected frame rates, and each iteration occurs at more consistent intervals.
800 RPM at 60 FPS resulting in 798 RPM using the solution
The solution can be explored in more detail in FireRateTest.rbxl (54.9 KB). To get started:
- Enter Play mode
- Select ReplicatedStorage and configure the attributes as desired
- Press and hold the left mouse button to keep firing