The system I devised will, collect all adjacent Tile Grids to the original modified grid, then all adjacents of the adjacents. The smallest group will be for searching entities that might want to know about the new “waterable entity” and the larger group are all the potential targets of all the entities that are searching for entities in range.
The collected TileGrids are SharedComponentDatas, whose indexes are unknowable unless you use the hack I made, are then converted to a list of indexes, which is then used in chunk iteration to collect all relevant entities that will be checked for distance to one another.
using BovineLabs.Event.Containers;
using BovineLabs.Event.Systems;
using System.Collections.Generic;
using System.Linq;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Jobs.LowLevel.Unsafe;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Unity.Entities.Extension;
using System;
public class GridModifiedEventConsumer : ConsumeEventSystemBase<GridModifiedEvent> {
public abstract class GridDistanceCollectorGeneric<T> : GridDistanceCollector where T : struct, IBufferElementData {
}
private struct EntityPosition {
public Entity Entity;
public float3 Position;
//public float3 WorldPosition;
}
NativeHashSet<TileGrid> Grids;
NativeHashSet<TileGrid> Adjacent;
NativeHashMap<int, TileGrid> GridIndexes;
NativeHashMap<int, TileGrid> AdjacentIndexes;
NativeList<EntityPosition> Collectors;
NativeList<EntityPosition> Targets;
NativeMultiHashMap<Entity, Entity> InRange;
public GridDistanceCollector[] Processors;
EntityQuery TargetQuery;
public EntityQuery ExposedGetQuery(params ComponentType[] componentTypes) {
return GetEntityQuery(componentTypes);
}
protected override void Create() {
base.Create();
Grids = new NativeHashSet<TileGrid>(64, Allocator.Persistent);
Adjacent = new NativeHashSet<TileGrid>(512, Allocator.Persistent);
GridIndexes = new NativeHashMap<int, TileGrid>(64, Allocator.Persistent);
AdjacentIndexes = new NativeHashMap<int, TileGrid>(512, Allocator.Persistent);
Collectors = new NativeList<EntityPosition>(4096, Allocator.Persistent);
Targets = new NativeList<EntityPosition>(16384, Allocator.Persistent);
InRange = new NativeMultiHashMap<Entity, Entity>(16384, Allocator.Persistent);
Processors = Utils.GetAllClassesExtendingRecursive<GridDistanceCollector>("Unity", "Microsoft", "System", "Mono", "UMotion").
Where(T => !T.IsAbstract).Cast(T => (GridDistanceCollector)Activator.CreateInstance(T)).ToArray();
foreach (GridDistanceCollector Collector in Processors) {
Collector.CacheQuery = Collector.MakeQuery(this);
}
TargetQuery = GetEntityQuery(ComponentType.ReadOnly<TileGrid>(), ComponentType.ReadOnly<LocalToWorld>(), ComponentType.ReadOnly<TileAdjacency>());
}
protected override void Destroy() {
base.Destroy();
Grids.Dispose();
Adjacent.Dispose();
GridIndexes.Dispose();
AdjacentIndexes.Dispose();
Collectors.Dispose();
Targets.Dispose();
InRange.Dispose();
}
protected override void OnEventStream(ref NativeEventStream.Reader reader, int eventCount) {
if (eventCount == 0 || Processors.Length == 0) {
return;
}
Grids.Clear();
Adjacent.Clear();
GridIndexes.Clear();
AdjacentIndexes.Clear();
NativeHashSet<TileGrid> _Grids = Grids;
NativeHashSet<TileGrid> _Adjacent = Adjacent;
NativeHashMap<int, TileGrid> _GridIndexes = GridIndexes;
NativeHashMap<int, TileGrid> _AdjacentIndexes = AdjacentIndexes;
while (eventCount-- > 0) {
GridModifiedEvent GridModifiedEvent = reader.Read<GridModifiedEvent>();
_Grids.Add(GridModifiedEvent.TileGrid);
}
Job.WithCode(() => {
foreach (TileGrid Grid in _Grids) {
Grid.AdjacentForBurst(_Adjacent);
}
Utils.Swap(ref _Grids, ref _Adjacent);
_Adjacent.Clear();
foreach (TileGrid Grid in _Grids) {
Grid.AdjacentForBurst(_Adjacent);
_Adjacent.Add(Grid);
}
}).Run();
Job.WithCode(() => {
Utils.Swap(ref _Grids, ref _Adjacent);
foreach (TileGrid Grid in _Grids) {
int index = EntityManager.FindSharedComponentIndex(Grid);
if (index != -1) {
_GridIndexes.Add(index, Grid);
}
}
foreach (TileGrid Grid in _Adjacent) {
int index = EntityManager.FindSharedComponentIndex(Grid);
if (index != -1) {
_AdjacentIndexes.Add(index, Grid);
}
}
}).WithoutBurst().Run();
Collectors.Clear();
Targets.Clear();
CollectTargetEntities CollectTargetEntities = new CollectTargetEntities();
CollectTargetEntities.Filter = _GridIndexes;
CollectTargetEntities.Output = Targets.AsParallelWriter();
CollectTargetEntities.TileGrid = GetSharedComponentTypeHandle<TileGrid>();
CollectTargetEntities.LocalToWorld = GetComponentTypeHandle<LocalToWorld>();
CollectTargetEntities.EntityTypeHandle = GetEntityTypeHandle();
JobHandle TargetJob = CollectTargetEntities.ScheduleParallel(TargetQuery, Dependency);
foreach (GridDistanceCollector Collector in Processors) {
CollectTargetEntities = new CollectTargetEntities();
CollectTargetEntities.Filter = _AdjacentIndexes;
CollectTargetEntities.Output = Collectors.AsParallelWriter();
CollectTargetEntities.TileGrid = GetSharedComponentTypeHandle<TileGrid>();
CollectTargetEntities.LocalToWorld = GetComponentTypeHandle<LocalToWorld>();
CollectTargetEntities.EntityTypeHandle = GetEntityTypeHandle();
JobHandle CollectorJob = CollectTargetEntities.ScheduleParallel(Collector.CacheQuery, Dependency);
float distance = Collector.Distance() + 0.1f;
NativeList<EntityPosition> _Collectors = Collectors;
NativeList<EntityPosition> _Targets = Targets;
NativeMultiHashMap<Entity, Entity> _InRange = InRange;
_InRange.Clear();
Job.WithCode(() => {//This could be optimized by ordering all collectors and targets by their X and Z positions
for (int i = 0; i < _Collectors.Length; i++) {//But I can just do that later
for (int j = 0; j < _Targets.Length; j++) {
if (math.abs(_Collectors[i].Position.x - _Targets[j].Position.x) < distance) {
if (math.abs(_Collectors[i].Position.z - _Targets[j].Position.z) < distance) {
_InRange.Add(_Collectors[i].Entity, _Targets[j].Entity);
//Debug.DrawLine(_Collectors[i].WorldPosition, _Targets[j].WorldPosition, Color.blue, 5);
//} else {
// Debug.DrawLine(_Collectors[i].WorldPosition, _Targets[j].WorldPosition, Color.red, 5);
}
//} else {
// Debug.DrawLine(_Collectors[i].WorldPosition, _Targets[j].WorldPosition, Color.red, 5);
}
}
//DebugExtension.DebugPoint(_Collectors[i].WorldPosition, Color.red, 0.5f, 5);
}
//for (int j = 0; j < _Targets.Length; j++) {
// DebugExtension.DebugPoint(_Targets[j].WorldPosition, Color.green, 0.5f, 5);
//}
}).Schedule(JobHandle.CombineDependencies(TargetJob, CollectorJob)).Complete();
Collector.ProcessResult(EntityManager, _InRange);
}
}
public class SprinklerDistanceCollector : GridDistanceCollector {
public override int Distance() {
return 1;
}
public override void DoProcess(EntityManager EntityManager, Entity Key, int Lenght, NativeMultiHashMap<Entity, Entity>.Enumerator Vals) {
DynamicBuffer<SprinklerTargets> Buffer;
if (EntityManager.HasComponent<SprinklerTargets>(Key)) {
Buffer = EntityManager.GetBuffer<SprinklerTargets>(Key);
Buffer.Clear();
} else {
Buffer = EntityManager.AddBuffer<SprinklerTargets>(Key);
}
while (Lenght-- > 0) {
Vals.MoveNext();
Buffer.Add(Vals.Current);
}
}
public override ComponentType[] GetQuery() {
return Utils.A(ComponentType.ReadOnly<IsSprinkler>());
}
}
public abstract class GridDistanceCollector {
public EntityQuery CacheQuery;
public abstract int Distance();
public abstract ComponentType[] GetQuery();
public EntityQuery MakeQuery(GridModifiedEventConsumer source) {
return source.ExposedGetQuery(GetQuery().Concat(new ComponentType[] { ComponentType.ReadOnly<TileGrid>(),
ComponentType.ReadOnly<LocalToWorld>() }).ToArray());
}
public abstract void DoProcess(EntityManager EntityManager, Entity Key, int Lenght, NativeMultiHashMap<Entity, Entity>.Enumerator Vals);
public void ProcessResult(EntityManager EntityManager, NativeMultiHashMap<Entity, Entity> inRange) {
(NativeArray<Entity>, int) keys = inRange.GetUniqueKeyArray(Allocator.Temp);
int len = keys.Item2;
for (int i = 0; i < len; i++) {
Entity Key = keys.Item1[i];
int Len = inRange.CountValuesForKey(Key);
DoProcess(EntityManager, Key, Len, inRange.GetValuesForKey(Key));
}
keys.Item1.Dispose();
}
}
[BurstCompile]
struct CollectTargetEntities : IJobChunk {
[ReadOnly] public NativeHashMap<int, TileGrid> Filter;
[ReadOnly] public SharedComponentTypeHandle<TileGrid> TileGrid;
[ReadOnly] public ComponentTypeHandle<LocalToWorld> LocalToWorld;
[ReadOnly] public EntityTypeHandle EntityTypeHandle;
public NativeList<EntityPosition>.ParallelWriter Output;
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) {
if (Filter.TryGetValue(chunk.GetSharedComponentIndex(TileGrid), out TileGrid grid)) {
NativeArray<Entity> Entities = chunk.GetNativeArray(EntityTypeHandle);
NativeArray<LocalToWorld> LocalToWorld = chunk.GetNativeArray(this.LocalToWorld);
for (int i = 0; i < chunk.Count; i++) {
Output.AddNoResize(new EntityPosition() {
Entity = Entities[i],
Position = grid.InGrid(LocalToWorld[i].Position)
//,
//WorldPosition = LocalToWorld[i].Position
});
}
}
}
}
}