Original title: “BF4 shooting mechanics”, 19.10.2014
How does shooting work in BF4?
“Press LMB and soon a bullet is fired” said someone in the Algorithms thread. Sure, tell me more captain obvious. Kk, ez; “Keep LMB down and more bullets are fired at a rate of “RoF” rpm and at an interval of “60/RoF” seconds in between the shots”. These kinds of implicit assumptions about infinite temporal resolution have always been the basis of time-to-kill comparisons, spread-decrease optimized bursting rates, bullet flight time calculations, etc. We live in a ready world, eh?
Then many clues suggesting that the game might not work this way appeared in a very short time. Miffyli came up with theright-hand side of the recoil decrease equation that turned out to be evaluated frame-by-frame. NoctyrneSAGA said that the game logic loop runs at a fixed 30 Hz. In line with Noctyrne’s comment, G-Sync’d video captures clearly showed that in one 33 ms frame an unknown time after ‘LMB down’, there is a muzzle flash, bullet in air, and no recoil. Systematically in the frame right after this, essentially all V recoil kicks in (you did look at the previous SCAR-H shot frame-by-frame image didn’t you…). And really by far most importantly, Miffyli put together a tool for reading game variables directly from BF4 memory, which gave us a chance to get conclusive insight into in-game mechanics and how they relate to ‘LMB down’.
It’s not that tl;dr, read on
A note on Methods
I ran Miffyli’s memory probe with a wait time of 1 ms, which yielded samples at (known) 1-2 ms intervals. BF4 was run in Windowed mode, all graphics at ‘low’, at 59.95 Hz with FPS and screen refresh capped to 60 Hz. I struggled for days to identify the source of non-random jitters (and results not predicted by theory) in my recordings. It turned out that the game-logic loop does not run at 30 Hz but rather at 59.95/2 Hz and that the logic loop frames are affected by monitor refresh, which makes 85 Hz FPS fucking bad for this kind of stuff.
Have a look at in-game timings of ‘LMB down’ and V recoil-up-transients, and remember that before recoil-up, the preceding 33 ms constitute the frame where the shot is fired.
Here 11 mag-dumps were shot with naked SCAR-H and the V recoils (spikes) are visualized with LMB down states (red bar) so that the onset of the first-shot V recoil is at 0 ms and hence the first muzzle-flash/bullet-out frame is at ~-33 ms. There bursts are offset by multiples of 0.5 so that they all can be seen. You can easily see that there is always around 1-32 ms, i.e., less than one frame between LMB down and first-bullet out. This is what I have systematically seen also in other recordings so I think we can safely conclude that if the gun is ready to fire, the shot is out in the frame following LMB down, i.e. with an unpredictable < 33 ms lag.
However, what is really conspicuous in the recoil time series is that every burst follows the same temporal pattern and the bullet output does not correspond to 60/RoF of SCAR-H. So, bullet output is discretized into the game frames and occurs in the same sequence of frames in every burst.
Have a look at how the same looks like for AEK-971:
and for FAMAS (explains why dat FAMAS is loved by some, doesn’t it… )
Hence, all shooting timings are discretized into frames of 33 ms. There is really nothing spectacular in this, games need syncing and computers take finite times for doing things, BUT as you can see, there are some damn serious and interesting implications for our beloved RoFs, TTKs, and DPSs.
The implications of discretized shooting?
Let’s make an assumption that the bullet-out frame is always the one wherein the RoF would predict the shot to be fired. So, for example, if the game logic loop frame times (in seconds) are:
0-0.033 0.033-0.067 0.067-0.100 0.100-0.133 0.133-0.167
and the RoF-predicted bullet-out times for the first three shots of ACE 23:
0 0.078 0.156
then this model would predict the shots to be fired in frames 1, 3, and 5.
This turns out to be quite in line with in-game recordings with ACE 23 (RoF = 770 Hz):
Here the spikes are the recorded V recoils as in the previous post. The green and black squares above are the bullet-out and no-bullet, respectively, frames predicted by the model.
Picking a few example RoFs, this turns out to works in the same way for SCAR-H (620 Hz):
A-91 (800 Hz):
AEK-971 (900 Hz):
and finally, FAMAS (1000 Hz):
So, the model seems to hold. Below is a list of all possible RoFs in the game (for automatics) and the corresponding bullet-out frame sequences:
The table shows for example that 600 rpm guns spit out the first 2 bullets exactly as fast as the 900 rpm AEK. Likewise, for the first 3 bullets, M416 is as fast as AEK. Finally, up to 5 first shots, A-91 or MG4 at 800 rpm are identical to 900 rpm guns. The implication of this, of course, is that we need to re-adjust our ideas of how the RoF stat plays out in-game. These timings can be exploited for optimal bursting, but remembering that the time it takes for the gun to be able to shoot again is still dependent on RoF - in a way I have not figured out yet. Another implication is, obviously, that all analyses of effective TTKs and optimal mouse tapping rates for bursting need a complete revision, d’oh.
Disclaimer: this table is accurate for BF4 running at 60 Hz (and probably at 120 Hz as well). There are minor frame shifts for FPS = 85 Hz (and probably for 140 Hz as well) as there appears to be an interaction between the game logic loop (~30 Hz) and output FPS choice. I’m trying to check these out for the last post, but I’ll need a couple of days minimum for that.
So, what do you think? All feedback is welcome.
First of all, I think all recordings I have seen, and those shown later in this post (look at the relationship of red crosses in figures and the large V recoil spikes), support this shooting onset/offset rule:
A bullet is always fired at frame “i” when ‘LMB down’ is detected in any part of the frame “i-1” if the gun is capable of firing at frame “i”. If this condition is not met, automatic shooting is terminated.
Here the earliest frame at which the shot can go out is specifically defined by the bullet-out “frames” seen in the frame sequences (see post above). Note also that the sequence always starts from the beginning at the onset of a new burst IF there is no conflict with the previous sequence (too early shots), so no ‘cheating’ possible. I don’t know how this is implemented and whether it is intentional in the first place, but this is what the data show.
So, I did recordings with different kinds of bursting patterns. Let’s look at SCAR-H first and remember that the frame sequence for SCAR-H is x-o-x-o-o-x-o-o-x-o-o- so that the first two bullets are fired with an interval of only 66 ms.
In the plot below, red crosses indicate 1-2 ms samples where ‘LMB down’ = TRUE, blue lines show the V recoil transients, and green lines the total amount of spread. For V recoil, large upward spikes are the first shots, small ones the subsequent shots, and the negative spikes reflect recoil (under)compensation that I did to not hit the recoil ceiling. Vertical gridlines are spaced one frame apart.
The lowest row shows optimal 2-shot bursting: when the gun would be ready to fire three frames after the second shot, one empty frame resets the spread, and a new quick (66 ms interval) pair of bullets can be sent out.
The middle row shows the two classes of derps that can happen at intended steady 2-shot bursting: 1. If the LMB down comes too close to the previous, a new first-shot is triggered (see large V recoil) but without the spread reset and without the 66 ms interval (see the IF condition above!). 2. An LMB down coming too late introduces extra frames and slows down the effective RoF - no news here.
The top row shows an example of 3-shot bursting and the 3rd class of derp: a failure to hold LMB down long enough will terminate the burst prematurely, which will then mess up recoil compensation etc.
All in all, this preliminary analysis shows that 600-650 rpm guns (x-o-x-o-o-x-o-o-x-o-o- pattern) can be two-shot bursted at min spread by using a x-o-x-o-o--x-o-x-o-o- pattern with so that essentially the only cost to output RoF is the shift of each the third shot by the frame () used to reset spread. Ofc the extra FSM might not be desirable, but that’s another issue.