Caliber


Physically accurate weapons and Ballistics

Caliber is my name for an exploration of a physically based firearm system, with simulated recoil, customizable components that influence firing mechanics, and realistic bullet physics.


This project was created in parallel with the school curriculum.

Firearms


Recoil, Weight, and more

Parameters


This project started when I was playing Red Dead Redemption 2, and was shooting with a Repeater. These firearms work in such a way that they can hold a magazine, but they need to be manually "cocked" to load a bullet into the chamber, to expel the empty case shot before it, and/or to pull the hammer back. This had me wondering about how one might be able to classify firearms for a game context.

My findings are that there are essentially three kinds.

  • Manual: Wherein the Action of the firearm has to be manually moved with force, either directly or by some lever/pump.
  • Semi-Automatic: Wherein pulling the trigger shoots once, and the excess force is used to ready the gun for firing again.
  • Automatic: Wherein holding the trigger will repeatedly fire the weapon.

The latter two categories can be further subdivided by the question of whether it needs initial cocking. This is true for most non-manual firearms. In that case, the gun needs to be cocked once, after which simply firing will cock the gun.

A different, and somewhat reductive way to say this is that the Action can be Single or Double. In reality, guns can be both, but that is not relevant in this case.

Of course, further subdivisions can be made related to the trigger, but for the most part, this is not relevant for the scope of a video game.

Pictured above is the editor menu for the Firearm Params.

  • Chamber capacity: Some firearms, such as Double Barrelled Shotguns, can house two in practice. This is where that would be specified
  • Accuracy: is the average (normally distributed) deviation in arcminutes. This is practically irrelevant, but I wanted to include it anyway.
  • The muzzle flash can be overridden in any barrel attachment.
  • Weight: Relevant in so far as it changes how recoil is felt. All attachments also have a weight.
  • Bullet Speed: How fast the bullet escapes the barrel. Should be changed to impulse so that different weighing bullets get different speeds.

Attachments


The first thing I started working on in the project was an elaborate system for weapon attachments. I wanted to make it such that any weapon (given that one had fitting attachments) would be able to accept attachments, and that these would realistically change the way the firearm worked.

This was inspired by Escape from Tarkov, a game I have not personally played, but one whose firearm system I admire a lot.

The most felt difference between attachments, as the system works now, is in the recoil. I used a PID controller to counteract the recoil, with what I call a Contact Point being the thing of primary importance.

A Contact Point is simply a point in space, bound to the attachment or firearm base. It contains a set of PID controller coefficients that are added to the total of the "hand" that is used to pull the trigger, and to hold the firearm in place. In addition, the hand has a basic "randomness" value, so the recoil makes the firearm less accurate in both axes. A negative value in the attachments' recoil randomness would decrease the randomness.

In addition, the weighted average of the contact points' locations and the hand give the pivot point for the firearm as a whole.

Another type of weapon attachment is the various kinds of adapters. Sometimes, a suppressor can not be put onto the firearm as is. In that case a barrel adapter would be required. Similarly, to put a scope or a red dot sight on, one would need to attach a rail first.

*In these cases, the firing speed of this MP5 was reduced from 13.3 to 6.0 to illustrate the difference as clearly as possible.

Ballistics


Ricocheting, penetrating, and falling

Overview


One of the main things I wanted to be able to showcase with this project was realistic and dynamic bullet physics, or ballistics.

Bullets travel fast. So fast indeed, that a real bullet may well collide with multiple objects in a single frame. Many games choose not to bother with this, and simply stop the bullet at its first point of collision.

I have at points been frustrated by this in video games. If I shoot at wood, I do not expect the person behind the wood to go unharmed, and if I shoot at metal at a grazing angle, I expect the bullet to ricochet.

The following system of ballistics is my solution to those frustrations. The system features a per-object defined collision response. Some surfaces, for example wood, would basically never have a bullet ricochet off of it, and different surfaces should hinder the bullet to different degrees.

I employed a physically based model for bullet ricocheting and penetrating based mostly on actual physical phenomena, adapted for optimal performance.

The essential frame logic is this:

Where Move() will subtract from DeltaSeconds depending on how far the bullet manages to go. If it does not collide with anything, Move() will only run once in the frame.

The advantage of this approach is that it allows for multiple interactions within a single frame. The bullet may penetrate, exit, ricochet, and keep going, all in a single frame.

Penetration


To determine if the object will penetrate or not, one needs only take the Reflectance calculated in the following section, and subtract it from 1. This means that every bullet that does not ricochet will penetrate. Now, for the sake of optimization, some objects, like the ground, should not let the bullet penetrate at all, and should just destroy the bullet. For other objects, the user should be able to easily define how the penetration takes place. For a thin plane, one might simply want a probability of penetrating, and for object of non-uniform thickness, an actual simulation may be better. Below you can see how the user defines these.

In a game context, this physical method may seem overkill, and in most cases it may be, but it is quite performant, and for its draw backs in terms of system weight, I believe it makes up in terms of usability.

In the case of a bullet hitting a player or an NPC, the momentum the bullet transfers to the player can be used to decide how much damage it does, and the various parameters that are supplied to the player can be used to give visual cues like a smaller entry wound and a larger exit wound and so on.

The impact depth in the physical case is approximated using Newton's Approximation of Impact Depth. This is not exactly ideal for all use cases, but one may freely play with the density to control the outcome.

Where D=Depth, L=Bullet Length, A=Density of bullet, and B=Density of Target.

Below is a demonstration of this formula applied in game.

*In both of these, the size of the bullet has been scaled up 5x to be more visible. The physics have not been altered.

*In the top video, playback has been slowed down 100x.

Pictured above is a case where the bullet penetrates, and then sometimes exits, all within one single frame. In some cases, the bullet is slowed down considerably. In the very beginning one bullet is slowed down so much that it actually has time to ricochet off the floor.

*The bullets are set to not collide with each other here. One could easily let them collide with each other as well, but that seemed far overkill.

Ricocheting


The logic for impact handling can be found in the object that is hit. This is because different surfaces should be able to react very differently to being shot. Most surfaces probably will not even allow for ricocheting, but it was something I thought was quite important for a realistic ballistics system.

Determining if the bullet should ricochet, I came up with my own function that feels right, where the user defines the angle at which half of bullets should ricochet, and another value for how quickly it should drop off. A graph of the ricochet probability, or Reflectance can be found below, with arbitrary coefficients.

Where the y-axis represents the probability, and the x-axis the angle of incidence. This is not necessarily physically accurate, but it is not glaringly unrealistic, which is usually enough for games.

Then, I use this value, the Reflectance, to determine how much the path of the bullet ought to be perturbed by the collision. This is also definable per surface. A very "shallow" angle should result in a lesser random deviation, and a "deeper" angle a greater one.

I then use a Gaussian Random, rather than a uniform one, to apply the deviation. This results in a realistic spray pattern after ricocheting.

The spray pattern on the wall after ricocheting

Trail


The trail is not technically part of the physics of the bullet, but it is evaluated along with the physics, so I have chosen to display it here. For the trail of the bullet, every time the bullet changes location, its location is stored in an array along with the time stamp of when it was saved. Then, at the end of each frame, this list of past locations is pruned such that it only takes up a certain user defined time. Currently, this trail is only displayed as a debug line, but it would not be too hard to replace this with splines. It was simply out of scope.

A GIF of different terrain seeds

For the interested, the code above prunes the trail.

Improvements


As a complete firearm system goes, this definitely has a way to go. Part of the reason why I did not go further with some aspects, such as aiming, is that I know it would take considerable time, and I thought that as long as I build a system that can easily accommodate expansion, that is worth something.

For a very technical improvement, the force corresponding to the I term in the PID controller should not be evaluated at runtime. While firing, there should essentially be a constant counteractive force. With the current runtime force, the gun will start off by firing a little bit above the target, and then slowly find its target after a second or so.

While some games, like Counter Strike, make this recoil a feature (so you have to purposefully aim lower), the option should be there to automatically counteract the recoil better.

Beyond this, there are countless improvements that could truly turn this from an experiment to something interesting and actually useful.

Another improvement would be to replace the debug lines for the trail with an actual spline mesh. This would greatly level up the visuals.

I would also like to do sound implementation.


Since this was quite fast, taking 3 weeks to complete, I can imagine that with more time I could expand it a lot.