- Home /
Questions about changes to UnityEngine.Random. Random.Seed deprecated
Previously, I've been using PlayerPrefs to store an integer from Random.seed to save the level and restore the integer from PlayerPrefs to Random.seed to restore the level.
The code to save the seed.
PlayerPrefs.SetInt("Seed", Random.seed);
PlayerPrefs.Save();
The code to restore the seed.
if (PlayerPrefs.HasKey("Seed"))
{
Random.seed = PlayerPrefs.GetInt("Seed");
}
Recently, however, Random.seed has been deprecated and replaced with Static Variable Random.state and Static Function Random.InitState.
Now I changed my restore code to look like this
if (PlayerPrefs.HasKey("Seed"))
{
//Random.seed = PlayerPrefs.GetInt("Seed");
Random.InitState(PlayerPrefs.GetInt("Seed"));
}
and that seems like a simple enough change and seems to work the same as before.
However Random.state does not return an Int. I think maybe it returns it's own data type of Random.State? If that's correct, then I don't know what to do with that to store it. I don't know how to display it either. I find it useful to use Debug.log to display the value from Random.seed so I know if the sequence is changing when it's not supposed to be.
I've read https://docs.unity3d.com/ScriptReference/Random-state.html and I see that I can store a Random.state to a variable of type Random.State, but that doesn't help me display it for debug purposes, and I'll basically have to write my own class to store and recall data from the disk just to be able to handle it.
Does anyone know why Unity made these changes? Does anyone know what the benefit is. Is there some central location where these changes are discussed that I can go to?
Good question. I wasn't even aware that Random.seed was deprecated - what version of Unity are you using?
Also the docs seem far from helpful: stating that the RNG state can be set either by seed or by explicitly using RandomState but the "effects are quite different", followed by a code listing that appears to demonstrate that the effects are in fact identical!
I'm using 5.4.2. I usually keep up to date, but I've been hesitant to update this time, and Random.seed being deprecated is one reason why. I've run into more than a few cases where I felt like the Unity documentation was lacking.
The example is missing an important detail which you can only see when you actually run the code and look at the output. Such examples should have the output as a comment next to the line that produces it so you can actually understand what happens without the need of running the code.
The point is that the numbers generated in step 5 and 6 are the same as those generated in step 3 and 4 since the state was reset to that point. Note that setting the seed again would completely reset the generator. That's why 7 and 8 are equal to 1 and 2.
If you're following this and you saw a notice that I answered my own question and then accepted it, that was a mistake. I was trying to write a comment and I had a moment.
Answer by Bunny83 · Mar 14, 2017 at 12:42 PM
They have deprecated the "seed" because if actually never allowed to restore the state of the Random generator. The seed never changed while producing random numbers. So storing and restoring the seed doesn't actually bring back the last state of the generator but just let it produce the same numbers again.
Now with Random.state
you can actually store the current state and restore it. However the state has more variables than a single int. It actually has 4 ints. That's why the State struct looks like this:
[Serializable]
public struct State
{
[SerializeField]
private int s0;
[SerializeField]
private int s1;
[SerializeField]
private int s2;
[SerializeField]
private int s3;
}
I don't get why they made the fields private ...
However since the struct as well as the fields are serializable you can use Unity's JsonUtility to serialize it and to restore the State object from a json string.
So if you want to use PlayerPrefs you would need to change your "int" into a "string" and store the serialized json
Wow, both you, @Bunny83, and @ceandros gave really good answers. Thank you. I decided to try using JsonUtility to serialize Random.state and then save it to and restore it from PlayerPrefs, as a string.
After reading the documentation , and some experimentation, this is what I came up with
To save Random.state:
PlayerPrefs.SetString("SeedJSON", JsonUtility.ToJson(Random.state));
PlayerPrefs.Save();
To load Random.state:
if (PlayerPrefs.Has$$anonymous$$ey("SeedJSON"))
{
Random.state = JsonUtility.FromJson<Random.State>(PlayerPrefs.GetString("SeedJSON"));
}
And it worked flawlessly, and solved a much bigger issue I was having that I haven't yet mentioned.
I was having trouble with Random.Range giving me different numbers, in the sequence, depending on whether I was calling it from a new scene or a scene that was already running. In other words, Random.seed would change even when it wasn't supposed to.
For example if the scene was loaded and the seed was set to "1234", the next seed number after Random.Range was called would be "4321", every single time. However, if the same code was run a second time, in the same scene and the seed was set to "1234" the next seed would inexplicably be "3412". These are fake numbers, but they give an example of how the numbers were changing when they weren't supposed to.
I don't know if my game was always having this issue and I just never noticed, or if it actually started more recently(maybe after Unity changed UnityEngine.Random?).
Using Random.state fixed that issue.
I do think that it makes a lot of sense, with procedural generation, to do what ceandros says. By using System.Random I could have multiple instances of random number generators with one dedicated to procedurally generating the level so it's guaranteed to be consistent and not affected by any other random number generation in other parts of the project.
For now, however, this is working for me, but in the future, if my procedural generation breaks again(as it's done before) I'll reconsider.
Answer by lgarczyn · Mar 14, 2017 at 12:10 PM
The Unity seed thing is bad practice. First there were changes over how it works, the entropy, and how many ints you needed to restore it, but it is also used by your entire project. If for any reason you get any random number anywhere during your re-generation of the level, you will completely change the outcome.
Simply use the C# System.Random library.
It allows you to create a Random object using a seed, and to easily get numbers in any range. Simply store that object in a static var or a singleton, and use it for every procedural generation script.
You will have to create extensions to the class to be able to save and load the random object in a string.
http://stackoverflow.com/questions/19512210/how-to-save-the-state-of-a-random-generator-in-c
@ceandros Thank you for your excellent answer.
I agree that using System.Random is probably the best option. I read a little about it here and here before posting my question, but I was unsure about it.
From what I understand, if I used System.Random I could use a new random number generation instance specifically for procedurally generating my levels . In that way I don't have to worry about other calls to Random breaking my levels, and I've broken my level generation a bunch of times, in the past. It's kind of fragile using UnityEngine.Random.
For now, I managed to get it functionally working with UnityEngine.Random.state. However, if using UnityEngine.Random causes any more breakages in the future, I am definitely switching.
I'm accepting Bunny83's answer, because it specifically answers the questions I had about why the Unity devs changed UnityEngine.Random and how to get everything working again with the new changes, even though using System.Random is probably the better solution for Procedurally generated levels.
Answer by koirat · Jun 27, 2018 at 09:03 PM
It might be useful if the added some State.ToText State.FromText
Right now I have to do this
Debug.Log(JsonUtility.ToJson(UnityEngine.Random.state));
and later
UnityEngine.Random.state = JsonUtility.FromJson<UnityEngine.Random.State>("{ \"s0\":-882691893,\"s1\":439609903,\"s2\":-186639039,\"s3\":586293905}");
When finding bugs for randomly generated stuff.
Your answer
Follow this Question
Related Questions
Random: When to set the seed ? 1 Answer
How to save a random seed to a playerpref 2 Answers
Having trouble with PlayerPrefs in my script. 1 Answer
Are there any major consequences when using Playerprefs? 0 Answers
Unity built-in seed? 2 Answers