Distribution | Usage |
---|---|
Normal Distribution | Used to model real world, as many real world processes are normal distributed, like height of people and many other natural processes. |
Discrete Distribution | New in 1.1.0 Custom discrete distribution where every choice can be given a probability. More performant alternative to the roulette distribution. |
Roulette Distribution | This distribution models a ball rolling down a roulette wheel, where each slot can have its own probability. Alternative to discrete distribution, which is faster but more restrictive. |
Custom Distribution (Rejection Sampling) | Models every custom distribution described by a AnimationCurve and sampled by rejection sampling. |
Fair Random Distribution | Will be in 1.2.0 A distribution that simulates a proc rate for RPG games by steadily increasing the probability. |
Rpg Dice Distribution | Will be in 1.2.0 A distribution inspired by the dice notation for RPG games. |
The methods NormalDistributionMarsaglia.NextNormal(ref <RNG>, mean, std)
are using the
Marsaglia polar method
to generate a random number following the normal distribution.
The methods NormalDistributionZiggurat.NextNormal(ref <RNG>, mean, std)
are using the Ziggurat algorithm which is
usually faster than the Marsaglia method, even when the implementation is much more complicated. It also needs
additional memory, as lookup tables are needed. In contrast to the marsaglia method, Ziggurat mostly doesn’t use
any complicated math function like sin, cos or exp.
Models a discrete distribution which allows every index to have its own probability. This is a (much) faster version of the roulette distribution, with the restrictions: All probabilities must sum up to one and no probability can be negative.
Example usage:
var prob = new NativeArray<uint>(101, Allocator.Temp);
// initialize probabilities
for (var i = 0; i < prob.Length; i++) {
// Assign probabilities to each index. This should be set to the desired probability
prob[i] = 1.0f / prob.Length;
}
// Generate weights for faster RNG generation
var alias = DiscreteDistribution.GenerateWeights(prob, Allocator.Temp, true);
// Sample the distribution
var val = DiscreteDistribution.Next(ref rng, alias);
The roulette distribution chooses a slot randomly between 0
and length
(excluding). Each slot has its own
probability. Probabilities can be specified in uint, float and double.
The individual probabilities are calculated as individual probability / sum over all values.
Using uints can sometimes be more accurate/faster, as float division or multiplication is avoided.
Example usage:
var prob = new NativeArray<uint>(101, Allocator.Temp);
// initialize probabilities
for (var i = 0; i < prob.Length; i++) {
// Assign probabilities to each index. This should be set to the desired probability
prob[i] = 1;
}
// Generate weights for faster RNG generation
var weights = RouletteDistribution.GenerateWeights(prob, Allocator.Temp);
// Sample the distribution
var val = RouletteDistribution.NextRoulette(ref rng, weights);
The individual probability is 1/100 ( = the sum of all values ).
Samples supplied distributions that are described by an animation curve. The Unity-AnimationCurve
must first be
converted to a temporary structure, that can be used from the job system JobAnimationCurve
. It’s expected that Unity
will provide a job compatible AnimationCurve
some time in the future.
The rejection sampling algorithm used is very simple and slow, but doesn’t have any restrictions on the
curve/distribution itself. This allows not-well-defined probability distributions to be sampled
(for example: distributions that do not have an area of 1 under the curve).
Example usage:
var jobAnimationCurve = animationCurve.ToJobsAnimationCurve();
unsafe {
var jac = UnsafeUtility.AddressOf(ref jobAnimationCurve);
SampleDistribution(jac);
}
[BurstCompile]
public unsafe static void SampleDistribution(void* jobAnimationCurve) {
var jac = UnsafeUtility.AsRef<JobAnimationCurve>(jobAnimationCurve);
var rng = new GRandom(1337);
var v = NaiveAlgorithms.NextFloatWithRejection(ref rng, ref jac);
// use generated value v
}
The fair random distribution is inspired by the critical hit rate in RPG games. It’s a distribution that steadily increases the probability of a success. The distribution is binary and has only the outcomes true and false. This probability of success is increased by a small amount every time the distribution is sampled and doesn’t result in true. The probability is reset to the initial value when the result is a success.
The fair random example gives a good overview of the distribution.
The fair random distribution needs an maxTriesUntilProc parameter, which specifies the absolute maximum tries until the
distribution should proc (result in a success). If instead a probability is needed, the maxTriesUntilProc can be
calculated with FairRandomHelper.FindMaxTriesUntilProcForProbability
.
Example usage:
var rng = new GRandom(1337);
// Optional: Calculate the maxTriesUntilProc from a given probability
var maxTriesUntilProc = FairRandomHelper.FindMaxTriesUntilProcForProbability(0.2f);
// Initialize status object
var frs = new FairRandomStatus<double>(maxTriesUntilProc);
// Sample the distribution
var hit = FairRandom.Next(ref rng, ref frs);
The rpg dice distribution is inspired by the Dice Notation for RPG games
and provides (almost) the same features. It allows to simulate various dice rolls, like 1d6
, 2d6+1
, 2d6+1d4
and so on.
Whereas 1d6
means to roll a standard dice once, and 2d6+1
means to roll a standard dice twice and add 1 to the result.
The notation is often used in pen and paper RPG games like Dungeons and Dragons.
Supported features:
AdX*M+C
: roll a dice with X
sides A
times, multiply the result with M
and add C
AdXkHYkLZ
: roll a dice with X
sides A
times, keep the Y
highest and Z
lowest results(AdX+AdX)*M+C
: Multiply the result of multiply dices by M
and add C
The expression can be created with a builder and is stack based. The expression is created from left to right and evaluated the same way.
To create a distribution for (AdX+BdX)*M+C
the following code can be used:
var builder = new RpgDiceBuilderMono();
builder.AddDice(A, X);
builder.AddDice(B, X);
builder.Expression(M, C);
var expr = builder.BuildMono(); // use .Build() for burst compatible version
var rng = new GRandom(1337);
// sample the distribution
var result = RpgDiceDistribution.Next(ref rng, ref expr);
The implementation is separated in a mono and burst compatible version. The mono version is more flexible and allows
to use the distribution in the editor. For that the RpgDiceInspector
has been created.
The burst compatible version is faster and can be used in jobs.
The RpgDice example is a good starting point to investigate the functionality of the distribution.