Realistic fire spread
WarArm team (The video game team I’m part of) is currently trying to improve the weapon’s gun experience that must be ergonomic for the player and must reflect a soldier who in war tries to aim right on target. We noticed over time that the soldier’s fire was too random in the scope of the target and the propagation of the projectiles took place in an uncontrolled manner. How to fix the problem? How to control the randomness of the bullets? Someone would just say to increase the accuracy and thus reduce the range of randomness, but so it becomes too easy for the player. We want more control of the weapon, it needs to shoot more in the center and less on the sides.
Let me explain with pictures
How to fix this problem?
My approach is to use the theory of probability and I therefore decided to help me with the wonderful gaussian function. Gaussian function appears as the density function of the normal distribution and we will use this.
We must consider this function with μ = 0 and σ = “weapon accuracy“. This values transform our “normal distribution” into a “normal standard distribution” density function. We have a density that has the following graph and has mean values at zero point. Zero point will be the center of our screen, where the shots should go. If at point x, the corresponding f(x) is high then there is more chance that the shot goes to that point.
It all seems complicated but …
This formula does not seem easy to apply. The computer fails to generate randomness of this type, because the random features available in the basic libraries of most languages are of uniform distribution. That is, uniform randomness over a range [a, b) such as the bullets distribution in the first image. Searching and searching I found a way that allows us to approximate normal distribution starting from two randomly uniformly distributed variables (which we have available with any function that generates a random number).
Here we are finally at the heart of the matter. Now we understand how to generate random numbers so as to simulate the will of the soldier to shoot tensely toward the center rather than at random in a spherical range. To get to a normal distribution we use the Box–Muller transform for generating standard, independent, normally distributed with zero expectation, unit variance (that we will change) random numbers, given a source of uniformly distributed random numbers.
This transformation get in input two-dimensional continuous uniform distribution to give in output a normal distribution. If x1 and x2 are our two random generated values between 0 and 1, we can use these random variables in this formula to get a normal distributed value:
Code example in C#
System.Random rand = new System.Random(); //Generate the object once because the shoots occur frequently
double RandomNormalDistribution(double mean = 0, double stdDev = 1f) //mean = 0 is the center of the screen
//uniform in (0,1] interval
double x1 = (1.0 - rand.NextDouble());
double x2 = (1.0 - rand.NextDouble());
double randStdNormal = mean + stdDev * (Math.Sqrt(-2.0f * Math.Log(x1)) * Math.Sin(2.0f * Math.PI * x2)); //normal(mean, stdDev)
This is an example of use:
//You can change the accuracy value (remember to leave the mean to zero, is the center of the screen)
double ErrorX = RandomNormalDistribution(stdDev: Accuracy);
double ErrorY = RandomNormalDistribution(stdDev: Accuracy);
The two error variables (ErrorX and ErrorY) will be used to define the point of the shot. Firing several shots you will notice that they will tend toward the center! It will no longer be a totally random shot. I wonder if the best games on the market will use a similar technique.
What do you think of this idea?
Polar form (computationally faster)
You can avoid the sine function and apply a formula in polar coordinates that returns a normal distributed random variable.
We need the usual two uniformly distributed random variables (u and v) and a variable s = u^2 + v^2 such that 0<s≤1 (These hypotheses are important).
Now we can get two normal variables (maybe one to cache for next use) with this formula:
Obviously we will multiply these variables for variance to change the accuracy.