using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using Cysharp.Threading.Tasks; #if UNITY_EDITOR using UnityEditor; using UnityEditor.UIElements; #endif using UnityEngine; using UnityEngine.Accessibility; using UnityEngine.Profiling; using UnityEngine.UIElements; // ReSharper disable PossibleMultipleEnumeration namespace BITKit.Sensors { /// /// 智能目标传感器,根据条件,智能选择目标 /// public class SmartTargetSensor :MonoBehaviour,ISensor,IAction { /// /// 自动更新 /// [Header(Constant.Header.Settings)] [SerializeField] private bool autoUpdate; [SerializeField] private Optional ignoreTags; [SerializeField] private Optional optionalRetargetInterval; /// /// 主传感器 /// [Header(nameof(Sensor))] [SerializeField,SerializeReference,SubclassSelector] private ISensor sensor; [Header(Constant.Header.Debug)] [SerializeReference, ReadOnly] private int updateCount; public IEnumerable Get() =>CurrentTarget is not null ? new[] { CurrentTarget }:Enumerable.Empty(); bool ISensor.AutoUpdate => autoUpdate; internal StringBuilder reportBuilder; internal DoubleBuffer report=new(); public bool IsValid(Collider _collider) { if (!_collider) return false; if (ignoreTags.Allow) { if (_collider.TryGetComponent(out var iTags)) { var tags = iTags.GetTags(); foreach (var x in ignoreTags.Value) { if (tags.Contains(x)) { return false; } } } } return true; } public float GetDistance() => sensor.GetDistance(); public Transform CurrentTarget { get; private set; } private IEnumerable detected = ArraySegment.Empty; private Vector3 _currentPosition; private int Id; private CancellationTokenSource timeoutCancellationTokenSource=new(); private void OnEnable() { SensorQueue.Register(Id=GetInstanceID(),this); } private void OnDisable() { SensorQueue.UnRegister(Id); } public async UniTask Execute() { try { _currentPosition = transform.position; updateCount++; timeoutCancellationTokenSource?.Cancel(); timeoutCancellationTokenSource = new CancellationTokenSource(); await sensor.Execute(); reportBuilder?.AppendLine($"-----BEGIN------{updateCount}"); Profiler.BeginSample("Release Detected Buffer"); var newDetected = sensor.Get(); if (reportBuilder is not null) { reportBuilder.AppendLine($"Detected:{newDetected.Count()}"); foreach (var x in newDetected) { reportBuilder.AppendLine(x.name); } reportBuilder.AppendLine(); } Profiler.EndSample(); // ReSharper disable once PossibleMultipleEnumeration if (newDetected.Contains(CurrentTarget)) { if (optionalRetargetInterval.Allow && optionalRetargetInterval.Value.AllowUpdate) { reportBuilder?.AppendLine("Retarget Interval Catch,Search New Target"); } else { reportBuilder?.AppendLine("Current Target Detected,Keep Target"); return; } } Profiler.BeginSample("Filter Detected"); foreach (var x in newDetected.OrderBy(KeySelector)) { if(IsValid(x.GetComponent()) is false)continue; CurrentTarget = x; reportBuilder?.AppendLine($"New Target:{x.name},Is oder by distance"); break; } Profiler.EndSample(); reportBuilder?.AppendLine($"-----Complete------{updateCount}"); if(reportBuilder is not null) { report.Release(reportBuilder.ToString()); reportBuilder.Clear(); } else { report.Clear(); } } catch (Exception e) { BIT4Log.LogException(e); } } private float KeySelector(Transform x) { var distance = Vector3.Distance(_currentPosition, x.position); reportBuilder?.AppendLine($"Distance:{distance}@{x.name}"); return distance; } void IAction.Execute() { Execute().Forget(); } #if UNITY_EDITOR private void OnDrawGizmosSelected() { try { if (!CurrentTarget) return; Gizmos.DrawLine(transform.position,CurrentTarget.position); } catch (UnassignedReferenceException) { } } #endif } #if UNITY_EDITOR [CustomEditor(typeof(SmartTargetSensor))] public class SmartTargetSensorInspector:BITInspector { private ObjectField _objectField; private Label _reportLabel; public override VisualElement CreateInspectorGUI() { FillDefaultInspector(); CreateSubTitle("Editor Debug Field"); _objectField = root.Create(); _objectField.objectType = typeof(Transform); _objectField.SetEnabled(false); _reportLabel = root.Create