C# implementation of UnityEngine.Random with (almost) 1-to-1 parity
source link: https://gist.github.com/macklinb/a00be6b616cbf20fa95e4227575fe50b
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
C# implementation of UnityEngine.Random with (almost) 1-to-1 parity · GitHub
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.
Notes
- 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)
Unknowns
- How Unity initializes
UnityEngine.Random
when 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.
Tests
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.
Integer
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 |
Floating point
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 |
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK