Hello guys, I’m trying to find a good solution on how to use Mathematics.Random and ECS correctly.
I have a randomly generated world with a world seed, which means every randomly generated value in this game has to work with the same random struct instance.
My solution that I came up with, I did a simple random wrapper:
using Unity.Mathematics;
public class RandomWrapper {
public static RandomWrapper global;
private Random m_random;
public RandomWrapper(uint seed) {
m_random = new Random(seed);
}
public RandomWrapper(Random random) {
m_random = random;
}
public int Range(int from, int to) {
return m_random.NextInt(from, to);
}
public float Range(float from, float to) {
return m_random.NextFloat(from, to);
}
public ref Random GetRef() {
return ref m_random;
}
}
Also, I have save/load in the game, which means I have to save and load the same random instance
public void NewGame() {
SaveHelper.CleanUp();
//in game I create an entity with random data component to be able to store the random into this later
EntityManager.CreateEntity(typeof(RandomData));
//initialize wrapper with seed
RandomWrapper.global = new RandomWrapper(m_sessionSeed);
}
private void SaveGame(string fileName) {
//before saving the world, I have to store the random to the random data component
Entities.WithAll<RandomData>().ForEach((Entity entity, ref RandomData randomData) => {
randomData.random = RandomWrapper.global.GetRef();
});
SaveHelper.SaveGame(fileName);
}
private void LoadGame(string fileName) {
SaveHelper.LoadGame(fileName);
//after loading the world I have to restore wrapper with the same random
Entities.WithAll<RandomData>().ForEach((Entity entity, ref RandomData randomData) => {
RandomWrapper.global = new RandomWrapper(randomData.random);
});
}
protected override void OnCreate() {
base.OnCreate();
if (SaveHelper.IsQuickSaveExist()) {
LoadGame(SaveHelper.QuickSaveName);
} else {
NewGame();
}
}
when I need to get a random value I use RandomWrapper.global.Range(0, 100) and it changes the state of random
And this actually works, but I feel like I did something wrong, and it is not the ECS way how to use random correctly.
I can’t imagine how to use it differently because if in the system I need a random value related to other components it should look like that
Entities.WithAll<RandomData>().ForEach((Entity randomEntity, ref RandomData randomData) => {
Entities.WithAll<PlayerData>().ForEach((Entity playerEntity, ref PlayerData playerData) => {
//but I can't use a ref randomData inside of lambda expression
randomData.random.NextFloat(playerData.from, playerData.to);
});
});
But I cannot do that because I have to copy the random struct and work with the copy, which breaks random state
So, how would you implement this?