Generate Predictable Random Numbers in Unity C#

Published

When doing procedural generation, it’s convenient to have reproducible results. This can be done by setting a state to a random number generator and it’s pretty easy to do for Unity and C#.

First of all, what random number generator can you use? There are 2 available out of the box in Unity:

  • System.Random, which is the default PRNG provided by C#
  • UnityEngine.Random, provided by unity and exposes some convenience functions

There are a few differences between these 2, but the main one is that, as mentioned in the documentation, UnityEngine.Random is a STATIC class, which means there is no instantiation; you do not get multiple instances of PRNG (pseudo random number generator), but references to a single one so state is shared.

The UnityEngine PRNG is therefore not threadsafe, and you would be better off using System.Random if you need several PRNG. Still, it’s good to mention that the UnityEngine PRNG exposes some very convenient functions to generate random Vector2 and Vector3, and it returns float by default, unlike System.Random which returns doubles which therefore need to be cast to float.

Once you’ve chosen your preferred PRNG, you can set the seed (aka the initial state of the PRNG) to always get the same sequence of numbers. To do this you would do Random.InitState(int seed) for the Unity PRNG, while System.Random takes a seed value in the constructor. In both case the input must be an integer.

So, how can we get an integer to seed our PRNG? Well, this is simple enough: everything in Mono implement the “GetHashCode” method, as it is defined as a method on the “object” class (see the discussion at https://forum.unity.com/threads/does-vector3-implement-gethashcode.362499/). This method returns a hash, aka a unique identifier that depends on object content / values, which is an integer in C#.

This means we can directly get the hash of pretty much any type that is defined by default in Unity and use it as input to initialize the state of our PRNG. This means you can technically use a player name or a given player input to initialize your world (a la Minecraft), or use grid coordinates when running a Poisson Disc Sampler to always get the same environment details.