Creating weapons for a video game can be tricky. Weapons have to be efficient, fun to use and realistic, while keeping the performance cost down, so video games usually take the shortcuts of instant hit, radius damage and health points. This article explains how we created physically accurate anti-aircraft shells for Helium Rain.
We needed an effective anti-air gun to defend capital ships, specifically our Invader destroyer.
There are usually two options to destroy fighters : use more fighters, ore use longer range guns - the second option is obviously more interesting. Long range gun cannot be simple machine guns, because it's very difficult to score a direct hit on a fighter from a distance. The target is very small (a few meters), bullets take time to travel to the target, and they have imperfect precision, even in space.
In the real world, even before WW1 it was recognized that shells should explode in the air, near the target, in order to send a shower of metal fragment instead of trying to hit the target itself. At that time there were two ways of making a shell explode mid-air : timed fuzes (the shell explodes after a fixed time) and barometric fuzes (the shell explodes at a fixed altitude). Both required the shell to be configured prior to firing.
At the end of WW2, a new type of fuze appeared : the proximity fuse that explodes when the distance to the target becomes smaller than a predetermined value, making AA much more efficient. The technology was so good that Allied command limited its deployment to ensure that it would not wall into German hands. This is the technology that we will use in Helium Rain.
Unlike most games, we took the path of physical projectiles with accurate speed and behaviour, and implemented a very high-tech proximity fuze. Not only does it detonate when the distance to the target reaches a fixed threshold, or detonation distance ; it also does so when reaching the minimum distance to the target, even if the detonation distance was not reached. This works by using an activation distance : under this distance to the target, the fuze is armed. If the distance starts to increase, it means that the shell reached the minimum distance, and the fuze detonates. Our shell has an activation distance of 50m and a detonation distance of 3m.
The implementation of this logic is very tricky. The physics simulation in a game is done step by step, each step must simulate everything that happened between the current world state and the future world state. As the shell are very fast (about 800 m/s) it's not possible to simply check the target proximity at the end of each step. For example, the shell can be too far in the current time, and too far as well in the future time - with the target distance right between them. This is called the tunnel effect in video games, and usually causes high velocity objects to go through walls without colliding. Game physics are simply not accurate enough to simulate real-world bullets.
To fix the tunnel effect, we have to calculate the real minimum distance between the target and the shell trajectory, and the associated point on the trajectory. We then check if this point is on the trajectory segment that the current simulation step is computing.
Then, if the minimum distance is below the detonation distance, we detonate. The detonation location is not the minimum distance point D because the shell must always explode at the detonation distance, never any nearer. Detonating inside the ship would be a typical tunnel effect bug, causing unrealistic damage. To avoid this, we calculate the position where the distance to the target is equal to the detonation distance, and explode at this point. A fun consequence of this is that sometimes, if the ship is flying very fast towards the shell, it can be already too close at the beginning of the step, so we need to detonate the shell at a past position.
If we are below the activation distance and if the minimum distance is still in the future, the fuze is armed. In the following steps, if the shell was armed, and if distance starts climbing, the shell detonates.
In most games, shells just explode with radius damage and all enemies inside the fireball are hurt. Usually, they take more damage if they are near the center of the explosion. In reality, the fireball itself is mostly harmless, damage is caused by thousands of steel fragments flying outwards. The closer you are, the higher the probability of being hit. Our simulation is very close to reality.
In Helium Rain, ships don't have health. They are made of multiple components (sometimes more than 40), each component having a collision box, a damage status, and of course a role : power generation, weaponry, propulsion, etc. Firing on a RCS thruster will have an impact on steering, firing on a weapon can disable it.
Our AA shells simulate hundreds of fragments hitting the ship, to make the effect of the shell as realistic as possible. Hitting the port side of a ship will damage components on this side. The simplest way to do that would be to ray trace random fragments flying outwards like bullets, however it would be very CPU intensive and most rays would hit no target.
To increase the density of the fragment cloud without overloading the CPU, we find each target in a reasonable range (in red) and ignore all others. Then, for each selected target we compute the intersection of the bounding sphere of the target (in magenta) and the target distance radius around the shell (in yellow). The ratio between the surface of the intersection's spherical cap and the whole sphere gives us the number of fragments that we actually need to fire.
For example, if the angle is 25°, the ratio will be 0.011 (on a sphere, the ratio become quickly very low). If an AA shell fires 300 fragments, there are only 3 useful fragments to simulate. We cast the right amount of fragments only in the cone containing the target and ray trace fragments only for this ship. Each hitting fragment (in green) will generate damage on a specific component like a bullet. Here is the result in 3D.
Proximity fuzes are very dangerous to use. As they simulate a simple metal detector, there is no difference between an allied ship, a wreck, or an enemy ship. Firing on nearby enemies can cause a destroyer to receive fragments from its own shells. To avoid this, the fuze has a security timing system. When firing, the fuze is configured with a timing range to avoid exploding near the destroyer, or too far from the target.
That's it ! The video in the beginning of the article demonstrates the efficiency of these shells. Of course, there is much more to it - turrets also have a lot of logic to stay accurate and anticipate trajectories, AI pilots can try to pull evasive manoeuvers - but that is for another day.
As a bonus, here is a snippet of our C++ code, from the part that computes how much fragments will be fired.
float ApparentRadius = FMath::Sqrt(FMath::Square(CandidateDistance) + FMath::Square(CandidateSize));
float Angle = FMath::Acos(CandidateDistance/ApparentRadius);
float ExposedSurface = 2 * PI * ApparentRadius * (ApparentRadius - CandidateDistance);
float TotalSurface = 4 * PI * FMath::Square(ApparentRadius);
float ExposedSurfaceRatio = ExposedSurface / TotalSurface;
int FragmentCount = ShellDescription->GunCharacteristics.AmmoFragmentCount * ExposedSurfaceRatio;
Thank you for reading !