Usage of RefRW<> props in Unity Dots Jobs?

Hey guys I’m working with unity dots, and I thought that These type of references work with dots?
The problem seems to be the RefRW<> Counter, when modifying the Activeentities in the IJobEntity…
Does anyone know a solution to this? what i want to achieve, is a ball-spawning mechanism that spawns entities (balls) as long as the ActiveEntities is less then the MaxEntities

I have the following code example:

[BurstCompile]
public partial struct SpawnBall_System : ISystem
{
    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        var entity = state.EntityManager.CreateEntity();
        state.EntityManager.AddComponentData(entity, new EntityCounterComponent
        {
            ActiveEntities = 0,
            MaxEntities = 2500
        });
    }

    [BurstCompile]
    public void OnDestroy(ref SystemState state)
    {

    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        float deltaTime = SystemAPI.Time.DeltaTime;

        var ecb = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>().CreateCommandBuffer(state.WorldUnmanaged);

        // Schedule a job that runs the spawning logic in parallel
        new BallSpawnerJob
        {
            DeltaTime = deltaTime,
            ECB = ecb,
            Counter = SystemAPI.GetSingletonRW<EntityCounterComponent>()
        }.Schedule();
    }
}

[BurstCompile]
public partial struct BallSpawnerJob : IJobEntity
{
    public float DeltaTime;
    public EntityCommandBuffer ECB;
    public RefRW<EntityCounterComponent> Counter;

    // This executes for each entity that has a SpawnerProperties and BallSpawnTimer.
    public void Execute(BallSpawnerAspect aspect, [EntityIndexInQuery] int entityInQueryIndex)
    {
        if (Counter.ValueRO.ActiveEntities >= Counter.ValueRO.MaxEntities) return;

        if (aspect.CanSpawnEntities() == false) { return; }

        if (aspect.SpawnCooldown(DeltaTime) == false) { return; }

        var entity = ECB.Instantiate(aspect.GetEntity());

        ECB.AddComponent(entity, new BallTag { });

        ECB.AddComponent(entity, new BallStats
        {
            MoneyValue = aspect.ReadProperties.ValueRO.BallValue
        });
        
        Counter.ValueRW.ActiveEntities++;
    }
}

I need to keep an updated count of the entities i spawned and of entities that are active so i can limit them and stop the spawning when the limit is reached.

I’ve tried a separate method working with monobehaviours and static ints to keep the count, but then unity throws a baker-error so i changed it to the system above.

Anyone can help with this?

Thanks in advance!

I think you can just pass in an integer variable to the job, increment it, then assign the result to your singleton once the job is complete. I have done something similar with a NativeList, but not sure if an int will be passed by reference.

What kind of issue are you getting from that RefRW usage?

You can try passing a pointer:

state.CompleteDependency();
fixed(EntityCounterComponent* ptr = &SystemAPI.GetSingletonRW<EntityCounterComponent>().ValueRW)
{
    new BallSpawnerJob
    {
        DeltaTime = deltaTime,
        ECB = ecb.AsParallelWriter(),
        Counter = reference.GetUnsafePtr()
    }.ScheduleParallel();
    state.CompleteDependency();
}

Another way is to use a NativeReference<T>.

var reference = new NativeReference<EntityCounterComponent>(state.WorldUpdateAllocator);
reference.Value = SystemAPI.GetSingleton<EntityCounterComponent>();
new BallSpawnerJob
{
    DeltaTime = deltaTime,
    ECB = ecb.AsParallelWriter(),
    Counter = reference.GetUnsafePtr()
}.ScheduleParallel();
var jobHandle = new WriteSingletonJob<EntityCounterComponent>
    {
        SingletonEntity = SystemAPI.GetSingletonEntity<EntityCounterComponent>(),
        ComponentLookup = SystemAPI.GetComponentLookup<EntityCounterComponent>(),
        Reference = reference,
    }.Schedule(state.Dependency);
state.Dependency = reference.Dispose(jobHandle);

[BurstCompile]
struct WriteSingletonJob<T> : IJob where T : unmanaged, IComponentData
{
    public Entity SingletonEntity;
    public ComponentLookup<T> ComponentLookup;
    [ReadOnly] public NativeReference<T> Reference;

    public void Execute() => ComponentLookup[SingletonEntity] = Refernce.Value;
}

Then work on the shared instance in parallel.

[BurstCompile]
public partial struct BallSpawnerJob : IJobEntity
{
    public float DeltaTime;
    public EntityCommandBuffer.ParallelWriter ECB;
    [NativeDisableUnsafePtrRestriction] public unsafe EntityCounterComponent* Counter;

    public unsafe void Execute(BallSpawnerAspect aspect, [ChunkIndexInQuery] int chunkIndexInQuery)
    {
        if (!aspect.CanSpawnEntities()) { return; }
        if (!aspect.SpawnCooldown(DeltaTime)) { return; }
        ref var counter = ref *Counter;
        if (Interlocked.Increment(ref counter.ActiveEntities) > counter.MaxEntities)
        {
            Interlocked.Decrement(ref counter.ActiveEntities);
            return;
        }
        ECB.Instantiate(chunkIndexInQuery, aspect.GetEntity());
        ECB.AddComponent(chunkIndexInQuery, new BallTag { });   
    }

Thanks for the help and sorry for the late answer, wasn’t at home…
I get a unsafe pointer error using the RefRW<>
Now that I’m home i’ll try to change the code with your help, I’ll keep ya updated

You should be able to correct this by adding [NativeDisableUnsafePtrRestriction] to the RefRW job field.