#Issue with spawning Entities that each have a unique random generator

1 messages · Page 1 of 1 (latest)

nimble elk
#

I'm working on a small DOTS project to get the hang of it.

I'm trying to spawn X entities at the start, and each will move randomly across the screen. However, I don't want a singleton for the random generator, as it forces monothreaded behaviors. Instead, I want each entities to have their own Random component to which they can write.

Here's how I author my random:

public class RandomBaker : Baker<RandomAuthor>
{
    public override void Bake(RandomAuthor authoring)
    {
        AddComponent(new RandomGen()
        {
            Random = Random.CreateFromIndex((uint) GetEntity(authoring).Index)
        });
    }
}

Which works great !! As long as I place the Entity in the editor. When I try to spawn my X entities, things go wrong. They all get the same seed. I am under the impression, that when spawning entities from a prefab, the authoring is only done on the prefab, making the instances exact copies of the prefab.

Here's the code that spawns the entities:

        var ecb = new EntityCommandBuffer(Allocator.Temp);

        for (uint i = 0; i < spawner.NumberOfSpawn; i++)
        {
            var e = ecb.Instantiate(spawner.Prefab);

            UniformScaleTransform trans = new UniformScaleTransform()
            {
                Position = spawner.GetRandomPos(),
                Rotation = Quaternion.identity,
                Scale = spawner.PrefabScale,
            };

            ecb.SetComponent(e, new LocalToWorldTransform() { Value = trans });
        }
        
        ecb.Playback(state.EntityManager);
    }

As a way to try and fix my issue, I tried adding:

ecb.SetComponent(e, new RandomGen(){ Random = Random.CreateFromIndex((uint)e.Index)));

but it sent me an error saying that the index was equal to uint.MaxValue (??) so I'm not sure that even at that points it knows the future entity's index.

What's the proper way to approach this ?

nimble elk
#

OK I FOUND A SOLUTION

but it is not a pretty one.

The solution is to not use mathematics.Random, but instead use Mathematics.noise.

Simply have a component with a uint state, and feed that into a 2D noise map, along with the entity ID UnityChanCelebrate

That way, two different entities with the same state won't get the same results ! And this removes any need for a proper initialisation at all!

#

Should I close this issue ? I'm still interested to see if anyone has a better solution.

south thorn
#

I personally just use math.random(DateTime.Now..Millisecond + 1) for generating random values. Doesn't work in burst though if that's an issue.

nimble elk
#

Yeah, I try to use burst

#

The issue I have with using time is that it really makes randomness impossible to recreate when debugging

south thorn
#

Just use fixed values then. 12345 never fails.

#

I wouldn't recommend storing a unique random value on each entity. I prefer to use a single system-wide Random() variable then query it repeated. Or thread-specific. If you really want an easy in-thread random value, just chunk components. Not per entity.

nimble elk
#

that works as an initial seed when initial seed doesn't matter (such as my solution with noises), and yes it is what I do, but this doesn't address the issue of initialising different seeds for different random states when using Mathematics.Random

nimble elk
south thorn
#

Also, ECB instantates entities with "temporary" indices. So when you create an entity, it has an index of -1.

south thorn
nimble elk
south thorn
#

For something like what you're showing above, just use the EntityManager directly and not an ecb.

nimble elk
south thorn
#

If you want to do multithreaded (where you must use an ECB), create a math.Random.CreateFromIndex(ChunkIndex)

nimble elk
#

but so far, I'll stay with what I have

south thorn
#

Yea, it's not that big of a problem, just throwing out what I would do, but yea. To fix the error you originally had, just use EntityManager to instantly resolve entity indices.

nimble elk
#

I see

#

Also, how would I go to change a single value from a component on instantiation, without rewriting the whole component ?

south thorn
#

You can if you use a ComponentLookup<>()

#
ComponentLookup<LocalTransform> t1;
t1.GetRefRW(Entity.Null, false).ValueRW.Position = new(0, 0, 1);```
#

There's no way to in-place edit a component from a EntityManager nor ECB perspective. Unless you write some extension methods. It's something Unity specifically did not allow.

nimble elk
#

I see

#

I'll just add a component and a system dedicated to initialize stuff

pure sable
#

i've been considering using a linear congruential generator for setting up per entity seeds