using SepCore.Debugger; using Unity.Collections; using Unity.Mathematics; using UnityEngine; namespace SepCore.Simulation { public sealed partial class SimulationWorld { public bool TryGetNearestEnemyEntityId(Vector3 origin, float maxSqrRange, out int enemyEntityId) { enemyEntityId = 0; if (maxSqrRange <= 0f || _enemies.Count == 0) { return false; } BuildEnemyTargetSpatialIndexIfNeeded(); float cellSize = GetTargetSelectionCellSize(); int centerCellX = SimulationCell.ToCell(origin.x, cellSize); int centerCellZ = SimulationCell.ToCell(origin.z, cellSize); float range = Mathf.Sqrt(maxSqrRange); int queryRange = Mathf.Max(1, Mathf.CeilToInt(range / cellSize)); float minSqrDistance = maxSqrRange; bool found = false; using (CustomProfilerMarker.TargetSelection_QueryNeighbors.Auto()) { for (int dx = -queryRange; dx <= queryRange; dx++) { for (int dz = -queryRange; dz <= queryRange; dz++) { long key = SimulationCell.Key(centerCellX + dx, centerCellZ + dz); if (!_enemyTargetBuckets.TryGetFirstValue(key, out int enemyIndex, out NativeParallelMultiHashMapIterator iterator)) { continue; } do { if (enemyIndex < 0 || enemyIndex >= _enemies.Count) { continue; } float3 enemyPosition = _enemies.Transform[enemyIndex].Position; float deltaX = enemyPosition.x - origin.x; float deltaZ = enemyPosition.z - origin.z; float sqrDistance = deltaX * deltaX + deltaZ * deltaZ; if (sqrDistance >= minSqrDistance) { continue; } minSqrDistance = sqrDistance; enemyEntityId = _enemies.Identity[enemyIndex].EntityId; found = true; } while (_enemyTargetBuckets.TryGetNextValue(out enemyIndex, ref iterator)); } } } return found; } private void InitializeEnemyTargetSpatialIndex() { if (_enemyTargetBuckets.IsCreated) { return; } _enemyTargetBuckets = new NativeParallelMultiHashMap(256, Allocator.Persistent); _enemyTargetBucketsDirty = true; } private void DisposeEnemyTargetSpatialIndex() { if (_enemyTargetBuckets.IsCreated) { _enemyTargetBuckets.Dispose(); } _enemyTargetBuckets = default; _enemyTargetBucketsDirty = true; } private void ClearEnemyTargetSpatialIndex() { if (_enemyTargetBuckets.IsCreated) { _enemyTargetBuckets.Clear(); } _enemyTargetBucketsDirty = true; } private void MarkEnemyTargetSpatialIndexDirty() { _enemyTargetBucketsDirty = true; } private void BuildEnemyTargetSpatialIndexIfNeeded() { InitializeEnemyTargetSpatialIndex(); if (!_enemyTargetBucketsDirty) { return; } using (CustomProfilerMarker.TargetSelection_BuildBuckets.Auto()) { int enemyCount = _enemies.Count; int desiredCapacity = Mathf.Max(256, enemyCount * 2 + 1); if (_enemyTargetBuckets.Capacity < desiredCapacity) { _enemyTargetBuckets.Capacity = desiredCapacity; } _enemyTargetBuckets.Clear(); float cellSize = GetTargetSelectionCellSize(); for (int i = 0; i < enemyCount; i++) { float3 enemyPosition = _enemies.Transform[i].Position; int cellX = SimulationCell.ToCell(enemyPosition.x, cellSize); int cellZ = SimulationCell.ToCell(enemyPosition.z, cellSize); _enemyTargetBuckets.Add(SimulationCell.Key(cellX, cellZ), i); } } _enemyTargetBucketsDirty = false; } private float GetTargetSelectionCellSize() { return Mathf.Max(0.1f, _targetSelectionCellSize); } } }