using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; using Cysharp.Threading.Tasks; using Unity.Mathematics; using UnityEngine.Jobs; using UnityEngine.Pool; using UnityEngine.Profiling; using Physics=UnityEngine.Physics; namespace BITKit.Sensors { public class RangeSensor : Sensor { [Header(Constant.Header.Settings)] [SerializeField] private float radius; [SerializeField] private float detectedHeightWeight=0.5f; [Header(Constant.Header.Optional)] [SerializeField] private Optional fov; [SerializeField] private Optional requireSight; [SerializeField] private Optional perceptionRadius; public float DetectedHeightWeight { get=>detectedHeightWeight; set=>detectedHeightWeight=value; } private readonly Collider[] colliders = new Collider[32]; private readonly RaycastHit[] hits = new RaycastHit[32]; private float _delta; public override IEnumerable Get() { if (!_detectedDoubleBuffer.TryGetRelease(out var newRelease)) return _detectedBuffer; Profiler.BeginSample("Release Detected Buffer"); _detectedBuffer = newRelease; Profiler.EndSample(); return _detectedBuffer; } private readonly DoubleBuffer> _detectedDoubleBuffer = new(); private IEnumerable _detectedBuffer=Array.Empty(); public override UniTask Execute(float delta) { _delta = delta; var length = Physics.OverlapSphereNonAlloc(Transform.position, radius, colliders, detectLayer); Profiler.BeginSample("Filter Detected Colliders"); var _newDetected = from x in colliders.Take(length) where IsValid(x) select x.transform; Profiler.EndSample(); _detectedDoubleBuffer.Release(_newDetected); return UniTask.CompletedTask; } public override bool IsValid(Collider _collider) { switch (_collider) { case var _ when Ignores.Contains(_collider.GetInstanceID()): case var _ when ignoreColliders.Contains(_collider): return false; } if (perceptionRadius.Allow) { var distance = Vector3.Distance(Transform.position, _collider.bounds.center); if(distance<=perceptionRadius.Value) return !requireSight.Allow || CheckSight(ref _collider); } switch (_collider) { case var _ when fov.Allow && CheckFov(ref _collider) is false: case var _ when requireSight.Allow && CheckSight(ref _collider) is false: return false; default: return true; } } public override float GetDistance() => radius; private bool CheckFov(ref Collider _collider) { var _dir = _collider.bounds.center - transform.position; if (_dir.sqrMagnitude <= 0) return false; var dir = Quaternion.LookRotation(_dir); return Vector3.Dot(transform.forward, _dir) > 0 && fov > Quaternion.Angle(transform.rotation, dir); } private bool CheckSight(ref Collider _collider) { var bounds = _collider.bounds; var position = bounds.center; position.y += bounds.size.y * detectedHeightWeight /2; var selfPosition = Transform.position; var length = Physics.RaycastNonAlloc( selfPosition, position - selfPosition, hits, Vector3.Distance(selfPosition, position), requireSight.Value ); switch (length) { case 0: Debug.DrawLine(selfPosition, position, Color.green,_delta); return true; case 1: return hits[0].collider == _collider; default: var collider1 = _collider; if (hits.Take(length).Any(Predicate)) { return false; } break; bool Predicate(RaycastHit x) { var result = ignoreColliders.Contains(x.collider) is false && x.collider != collider1; if (result) { Debug.DrawLine(selfPosition, x.point, Color.yellow,_delta); Debug.DrawLine(selfPosition, position, Color.red,_delta); } return result; } } return true; } } }