(EDIT: See my Edit below)
In order to reduce boilerplate while using ECS, I turned this code into that:
Before:
public class MySystem : JobComponentSystem
{
public struct Group
{
// A long list of different ComponentDataArray
// Eg.
public ComponentDataArray<Position> Positions;
public int Length;
}
[Inject] private Group _Group;
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
MyJob job = new Job()
{
DeltaTime = Time.deltaTime,
// That same long list of ComponentDataArray
};
return job.Schedule(_Group.Positions.Length, 64, inputDeps);
}
[BurstCompile]
struct MyJob : IJobParallelFor
{
public float DeltaTime;
// That same long list of ComponentDataArray AGAIN.
public void Execute(int i)
{
// MyJob code
}
}
}
After:
public class MySystem : JobComponentSystem
{
public struct Group
{
// A long list of different ComponentDataArray
// Eg.
public ComponentDataArray<Position> Positions;
public int Length;
}
[Inject] private Group _Group;
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
MyJob job = new Job()
{
DeltaTime = Time.deltaTime,
Group = _Group
};
return job.Schedule(_Group.Positions.Length, 64, inputDeps);
}
[BurstCompile]
struct MyJob : IJobParallelFor
{
public float DeltaTime;
public Group Group;
public void Execute(int i)
{
// MyJob code
}
}
}
Until then, everything worked fine. Until I tried to add a component to each entities once they were initialized.
public class MySystem: JobComponentSystem
{
public struct Group
{
// A long list of different ComponentDataArray
// Eg.
public ComponentDataArray<Position> Positions;
public SubtractiveComponent<Initialized> Initialized;
public int Length;
public EntityArray Entities;
}
[Inject] private Group _Group;
public class EntityBarrier : BarrierSystem { }
[Inject] private EntityBarrier _EntityBarrier;
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
MyJob job = new MyJob()
{
DeltaTime = Time.deltaTime,
Group = _Group,
CommandBuffer = _EntityBarrier.CreateCommandBuffer()
};
return job.Schedule(_Group.Positions.Length, 64, inputDeps);
}
[BurstCompile]
struct MyJob : IJobParallelFor
{
public float DeltaTime;
public Group Group;
[ReadOnly] public EntityCommandBuffer CommandBuffer;
public void Execute(int i)
{
CommandBuffer.AddComponent(Group.Entities[i], new Initialized());
// MyJob code
}
}
}
By which point it raised:
InvalidOperationException: The NativeArray MyJob.Group.Entities must be marked [ReadOnly] in the job MySystem:MyJob, because the container itself is marked read only.
But if I do change it to [ReadOnly]
, it complains I edit it when reaching the line CommandBuffer.AddComponent
.
Also which container is it mentioning being marked ReadOnly
?
Is there another way to feed the EntityArray
(or add a component to the entity of index i
) to the job without requiring all the boilerplate code seen in example 1?
EDIT: Still complains if I feed it manually like this:
public class MySystem : JobComponentSystem
{
public struct Group
{
// A long list of different ComponentDataArray
// Eg.
public ComponentDataArray<Position> Positions;
public SubtractiveComponent<Initialized> Initialized;
public int Length;
public EntityArray Entities;
}
[Inject] private Group _Group;
public class EntityBarrier : BarrierSystem { }
[Inject] private EntityBarrier _EntityBarrier;
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
MyJob job = new Job()
{
DeltaTime = Time.deltaTime,
Entities = _Group.Entities,
CommandBuffer = _EntityBarrier.CreateCommandBuffer(),
// That same long list of ComponentDataArray
};
return job.Schedule(_Group.Positions.Length, 64, inputDeps);
}
[BurstCompile]
struct MyJob : IJobParallelFor
{
public float DeltaTime;
public EntityArray Entities;
// That same long list of ComponentDataArray AGAIN.
public void Execute(int i)
{
CommandBuffer.AddComponent(Entities[i], new Initialized());
// MyJob code
}
}
}
Isn't it how we're supposed to do it? It's done here: https://forum.unity.com/threads/some-beginner-questions-about-pure-ecs.524700/#post-3449277
EDIT2: The only differences I spotted in his code are
[ReadOnly]public EntityArray Entities;
public EntityCommandBuffer CommandBuffer;
And using EndFrameBarrier
. I upgraded my code but receive:
InvalidOperationException: MyJob.CommandBuffer is not declared [ReadOnly] in a IJobParallelFor job. The container does not support parallel writing. Please use a more suitable container type.
Hence why I put it as ReadOnly
in the first place.
I found a way but I'm not sure it's safe or if it's the way to do it but:
// In your job
[NativeDisableParallelForRestriction] public EntityCommandBuffer CommandBuffer;
Found here: https://forum.unity.com/threads/to-make-it-clear-do-i-have-a-start-function-on-ecs.523943/#post-3441718
Regardless if you're using EndFrameBarrier
or inheriting it yourself. (I don't really know the difference)