Hi all,
I’m struggling my way through ECS and slowly making progress, but am currently getting stuck with reading from a nativemultihashmap that is being written to by another system.
I’ve tried altering my setup so that I have two maps that are written to on alternate frames, and then reading from the one that isn’t being written that frame, but still receiving the same error.
This system writes each of my item entities into a grid:
protected override void OnUpdate()
{
var parallelHashMap = PrepareMapWrite().AsParallelWriter(); // Gets the map to be written to this frame as a parallel writer
Entities.WithAll<ItemTag>().ForEach((Entity entity, in Translation translation) => {
int key = GetHashKey(translation.Value.x, translation.Value.z);
parallelHashMap.Add(key, new ItemHashData
{
item = entity,
position = translation.Value
});
}).ScheduleParallel();
mapOne = !mapOne; // Boolean used to determine whether to write to map 1 or 2
}
And then this code attempts to read from the map so it can add the closest item to a dynamic buffer:
protected override void OnUpdate()
{
if(!Keyboard.current.fKey.wasPressedThisFrame)
{
return;
}
NativeMultiHashMap<int,ItemHashData> map = ItemHashSystem.GetMapRead(); // Gets the map that isn't being written to this frame
Entities.WithoutBurst().WithAll<InventoryComponent>().ForEach((DynamicBuffer<PrefabListComponent> inventory, Entity entity, int entityInQueryIndex, in Translation invLocation) => {
int hash = ItemHashSystem.GetHashKey(invLocation.Value.x, invLocation.Value.z);
float distance = ItemHashSystem.cellScale * 3; // Just get a number bigger than the allowed cell size
ItemHashData closest = new ItemHashData();
if (map.TryGetFirstValue(hash, out ItemHashData i, out NativeMultiHashMapIterator<int> iterator))
{
closest = i;
distance = math.distance(invLocation.Value, i.position);
while (map.TryGetNextValue(out i, ref iterator))
{
if(math.distance(invLocation.Value, i.position) < distance)
{
distance = math.distance(invLocation.Value, i.position);
closest = i;
}
}
// Add the item to inventory bufffer and disable it, disabled until read is working correctly.
/*if(!closest.Equals(default(ItemHashData)) && math.distance(closest.position, invLocation.Value) <= 2)
{
// we do the pick up
inventory.Add(new PrefabListComponent { Prefab = closest.item } );
EntityManager.SetEnabled(closest.item, false);
}*/
Debug.Log($"Closest entity is {i.ToString()}");
}
}).Run();
}
When the second system runs, it immediately throws the below error:
InvalidOperationException: The previously scheduled job ItemHashSystem:<>c__DisplayClass_OnUpdate_LambdaJob0 writes to the Unity.Collections.NativeMultiHashMap`2[System.Int32,Inventory.ItemHashData] <>c__DisplayClass_OnUpdate_LambdaJob0.JobData.parallelHashMap. You must call JobHandle.Complete() on the job ItemHashSystem:<>c__DisplayClass_OnUpdate_LambdaJob0, before you can read from the Unity.Collections.NativeMultiHashMap`2[System.Int32,Inventory.ItemHashData] safely.
I had hoped moving to two NMHMs would stop it, but no luck. Every example I can find that is reading from NMHM involves older non SystemBase systems, and seem to work fine.
Is there a way to do this using SystemBase/Entities.ForEach, or do I need to mix and match the way my systems work (and/or wait for new features)