FPS (first-person shooter) games have been a staple in the video game industry ever since the explosion of Wolfenstein 3D back in 1992. Since then, the genre has been evolving with graphical upgrades, huge budgets, and an eSports ecosystem. But what about its core, the shooting mechanics? How have we progressed on that front? Why do some guns feel like it’s the real thing, while others feel like toys?
“How do bullets work in video games?”
Hitscan
In the earlier days, many games relied on a technique called raycasting to render 3D environments onto 2D images (your screen). Raycasting also allows the engine to determine the first object intersected by a ray. Developers then started to question, “What if that ray originated from the muzzle of a gun to mimic a bullet?” With this idea, hitscan was born.
Above: An example of raycasting
In most implementations of a hitscan weapon, when the player shoots a bullet, the physics engine will:
Figure out the direction the gun is pointing at,
Cast a ray from the muzzle of the gun until a defined range,
Use raycasting to determine if the ray hit an object.
If the engine determines that an object is in the line of fire, it will notify it with a message that it was “hit” with a bullet. The target then can do all the calculations needed to register the damage.
Above: From Unity. Point A represents a gun casts a ray until its maximum point B. The ray makes contact with the cube, which the engine will tell it has been hit.
Hitscan is simple at its core, but a lot of different modifications can be made to support other logic:
If we continue the ray past the first object that it hit, we can penetrate multiple objects in a line, like the railgun in Quake
Removing the maximum range of the ray means that we can shoot out a laser that will continue forever until we hit something
Programming certain surfaces to be reflective, to bounce bullets off of
Above: Overwatch. Genji’s deflect is an example of a reflective surface.
The main advantage of using raycasting is that it’s super fast. It’s quick to compute and does not need overhead memory or processing time to build a new physics object. That means the network engineering needed to keep many clients in sync is minimal since the server only needs to keep track of the direction of the ray. Recoil is simple to add, as the addition of a small perturbation in the aim of the gun will mimic the effect.
Thus, it’s no surprise that many games in the industry use hitscan for its shooting logic. Wolfenstein 3D and Doom are classic examples, but even recent games use this technology. Characters such as Soldier 76, McCree and Widowmaker from Overwatch have hitscan weapons, and most Call of Duty guns are hitscan as well.
Below: Examples from Overwatch, Call of Duty, Wolfenstein 3D
So why don’t all games use this method?
First, you may have noticed that rays have an infinite traveling velocity, thus reaching their destination instantly. There is no travel time after you fire a bullet and hit an object. This means it’s impossible to dodge a bullet if a ray is on target, even if the target is miles away.
Above: Halo. Notice how the muzzle flare and the hit effects on the ground show up at the same time.
Second, most implementations of hitscan use straight rays. This means it’s hard to account for wind, gravity, and other external factors that may affect the bullet once it leaves the gun. Programmers can add kinks and bends to the ray to help it mimic real rounds, but once the player shoots a ray, there is no real way to modify its path in the middle.
A lot of “casual” games end up using the hitscan method as it simplifies the learning curve for most beginner players. But what about games that aim to create an “immersive and realistic” shooting experience? They cannot achieve their goals within these constraints. We need to use an alternative method.
Projectile Ballistics
It sounds pretty fancy, but the high-level idea is straightforward. Every bullet or projectile shot out of a weapon creates a new physics object in the environment. It has its own mass, velocity, and hitbox that the engine will track.
Above: Max Payne 3
The advantages of using projectile ballistics shine in games where realism is the top priority. Since every projectile exists on its own, you can now factor in wind, friction, gravity, temperature; any force that should act on the bullet. Now that you can change the physics, players can now use weapons other than simple guns and lasers; you can now add grenade and rockets to your arsenal.
Since bullets under this system aren’t moving at the speed of light, you can also implement temporal features:
“Bullet-time” as seen in Max Payne, Sniper Elite or Superhot is feasible.
Travel time for projectiles, which means if you’re taking a long-distance shot (or shooting a slow-moving projectile), aiming ahead becomes crucial.
Delayed explosions on projectiles, like grenades
With these additional computations, the processing is more taxing relative to using hitscan. Servers will have to do a lot more work to make sure all the objects are in sync, and discrepancies or conflicts in logic across clients have to be resolved not to create inconsistent experiences for players on the same server.
Below: Examples from Superhot, Battlefield 1, Overwatch
There are many ways around this to squeeze out as much performance as possible. An example of engine optimization is to have a “pool” of objects loaded before playtime, and “warp in and enable” them when needed. Once it hits a surface, you can play a ballistics animation and disable the projectile, saving it for later. This method will reduce some computation and memory costs from creating and destroying objects over and over again.
There are also multiple ways to do the computations, but the high-level difference is where they decide to process a “tick” of a game, a unit of time measurement: