This is a partial C# implementation of UnityEngine.Random with (almost) 1-to-1 parity.
Unity uses Xorshift for psuedorandom number generation. In particular Xorshift128, which uses a state consisting of four unsigned 32-bit integer values. The state is initialized in UnityEngine.Random.InitState using a signed 32-bit integer seed, which is shuffled around with a technique similar to the way a Mersenne Twister is initialized.
This has been tested as far back as Unity 4.7.0f1, and as recent as Unity 2020.1.17f1.
- Huge thanks to MoatShrimp for figuring out how Unity initializes the Xorshift state parameters in InitState, and floating point generation.
- C# - As below. Values may differ in .NET 5.0, but thankfully Unity doesn't yet support that.
- JavaScript - Check out MoatShrimp's JS implemention (and neat Undermine loot lookup tool) here:
- Lua - I've translated this into Lua for use on MediaWiki wikis with the Scribunto extension installed. Note that this only works if Lua was compiled with
LUA_NUMBER = double, which should be the case for most wikis. In my case the initial goal was to evaluate random loot lists in Pillars of Eternity, for use on the wiki.- Module:Lootlist (might have to scroll down a bit)
- If you translate this into other languages, or have any improvements/insights, feel free to comment below!
- If you do translate this, be mindful of:
- How your language handles numbers. Most programming languages that only define one type for numbers will use a double-precision floating point value to represent all numbers.
- Integer overflow, and casting behaviours (particularly from uint to int)
- Differences in the
%operator (modulo vs remainder)
- How Unity initializes
UnityEngine.Randomwhen a seed is not provided. Probably seeded based on time, perhaps hourly? Need to do further testiong. - Floating point RPG is slightly inaccurate. Unity's implementation is likely done differently, though their exact method is unknown.
The following tests were performed using the C# implementation in Unity, initialized with: UnityEngine.Random.InitState(1234) and XORShift128.InitSeed(1234), generated sequentially (the state isn't reset between runs).
UnityEngine.Random.State.s3 and XORShift128.w are the last state parameter in Xorshift, the actual number that was generated in Xorshift. This value is a uint and is used as a basis for all other Random functions.
Uses UnityEngine.Random.Range(min, max) and XORShift128.NextIntRange(min, max)
0 to int.MaxValue
s3 / w |
Unity | XORShift |
|---|---|---|
| 3463400838 | 1315917191 | 1315917191 |
| 3496203776 | 1348720129 | 1348720129 |
| 3452947669 | 1305464022 | 1305464022 |
| 1278673611 | 1278673611 | 1278673611 |
| 4169168310 | 2021684663 | 2021684663 |
0 to int.MinValue (-2,147,483,648)
s3 / w |
Unity | XORShift |
|---|---|---|
| 916287344 | -916287344 | -916287344 |
| 2240259090 | -92775442 | -92775442 |
| 1901252403 | -1901252403 | -1901252403 |
| 2323917162 | -176433514 | -176433514 |
| 1472147877 | -1472147877 | -1472147877 |
int.MinValue to int.MaxValue
s3 / w |
Unity | XORShift |
|---|---|---|
| 4020283508 | 1872799860 | 1872799860 |
| 141347300 | -2006136348 | -2006136348 |
| 2735243002 | 587759354 | 587759354 |
| 227819815 | -1919663833 | -1919663833 |
| 3885870057 | 1738386409 | 1738386409 |
int.MaxValue to int.MinValue
s3 / w |
Unity | XORShift |
|---|---|---|
| 2312142103 | -164658456 | -164658456 |
| 1775189369 | 372294278 | 372294278 |
| 3338523678 | -1191040031 | -1191040031 |
| 3426086347 | -1278602700 | -1278602700 |
| 3322349983 | -1174866336 | -1174866336 |
int.MinValue to int.MinValue (error is caught and a suitable value is returned before a value is even generated, hence the non-changing state value)
s3 / w |
Unity | XORShift |
|---|---|---|
| 3322349983 | -2147483648 | -2147483648 |
| 3322349983 | -2147483648 | -2147483648 |
| 3322349983 | -2147483648 | -2147483648 |
| 3322349983 | -2147483648 | -2147483648 |
| 3322349983 | -2147483648 | -2147483648 |
Uses UnityEngine.Random.value and XORShift128.NextFloat()
s3 / w |
Unity | XORShift |
|---|---|---|
| 3593715923 | 0.4043221 | 0.404322 |
| 4266042159 | 0.551855 | 0.551855 |
| 2642301593 | 0.9868958 | 0.9868957 |
| 1674312536 | 0.593608 | 0.5936079 |
| 733387434 | 0.426595 | 0.426595 |
Uses UnityEngine.Random.Range(min, max) and XORShift128.NextFloatRange(min, max)
0.0 to 100.0
s3 / w |
Unity | XORShift |
|---|---|---|
| 3760331560 | 73.35463 | 73.35463 |
| 2410116911 | 69.16753 | 69.16753 |
| 3012185272 | 91.95337 | 91.95337 |
| 745993129 | 7.068896 | 7.068908 |
| 3699201620 | 2.080286 | 2.080297 |
0.0 to 100000.0
s3 / w |
Unity | XORShift |
|---|---|---|
| 1758944507 | 31747.49 | 31747.5 |
| 2312712917 | 30313.62 | 30313.62 |
| 313095164 | 67614.8 | 67614.8 |
| 609241099 | 37280.14 | 37280.14 |
| 4138924286 | 60177.63 | 60177.64 |
0.0 to float.MaxValue (3.402823E+38)
s3 / w |
Unity | XORShift |
|---|---|---|
| 3065852775 | 1.775827E+38 | 1.775827E+38 |
| 4023550175 | 1.209507E+38 | 1.209507E+38 |
| 1231330622 | 7.280383E+37 | 7.280387E+37 |
| 678488548 | 4.010639E+37 | 4.010643E+37 |
| 1997128070 | 3.143466E+38 | 3.143466E+38 |
0.0 to float.MinValue (-3.402823E+38)
s3 / w |
Unity | XORShift |
|---|---|---|
| 212231104 | -2.382252E+38 | -2.382252E+38 |
| 1632010759 | -1.528401E+38 | -1.528402E+38 |
| 3470161922 | -1.104089E+38 | -1.104089E+38 |
| 4159346095 | -5.693158E+37 | -5.693162E+37 |
| 3362607345 | -4.967007E+37 | -4.967012E+37 |
float.MinValue to float.MaxValue
s3 / w |
Unity | XORShift |
|---|---|---|
| 2641274177 | -2.480102E+38 | -2.480101E+38 |
| 3758475398 | 3.095331E+38 | 3.095331E+38 |
| 1138851524 | -1.78091E+38 | -1.780909E+38 |
| 3785966737 | 1.208649E+38 | 1.208649E+38 |
| 151368008 | 3.100158E+38 | 3.100158E+38 |
float.MaxValue to float.MinValue
s3 / w |
Unity | XORShift |
|---|---|---|
| 3347712278 | -2.869245E+38 | -2.869245E+38 |
| 2413123709 | 1.13493E+38 | 1.13493E+38 |
| 619907290 | 2.713464E+38 | 2.713463E+38 |
| 5796605 | 1.299941E+38 | 1.299941E+38 |
| 2534213977 | -2.709683E+38 | -2.709683E+38 |
float.MinValue to float.MinValue
s3 / w |
Unity | XORShift |
|---|---|---|
| 2990456181 | -3.402823E+38 | -3.402823E+38 |
| 238536240 | -3.402823E+38 | -3.402823E+38 |
| 3443233425 | -3.402823E+38 | -3.402823E+38 |
| 847449518 | -3.402823E+38 | -3.402823E+38 |
| 1964100510 | -3.402823E+38 | -3.402823E+38 |