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 { /// /// 自动更新 /// [Header(Constant.Header.Settings)] [SerializeField] private bool autoUpdate; [SerializeField] private Optional ignoreTags; [SerializeField] private Optional optionalRetargetInterval; [SerializeField] private Optional lostTargetInterval; [SerializeField] private Optional detectedTime; [SerializeField] private Transform root; /// /// 主传感器 /// [Header(nameof(Sensor))] [SerializeField,SerializeReference,SubclassSelector] private ISensor sensor; [SerializeField] private AudioSensor audioSensor; [Header(Constant.Header.Debug)] [SerializeReference, ReadOnly] private int updateCount; public IEnumerable Get() =>CurrentTarget is not null ? new[] { CurrentTarget }:Enumerable.Empty(); public int Id { get;private set; } bool ISensor.AutoUpdate => autoUpdate; internal StringBuilder reportBuilder; internal readonly DoubleBuffer report=new(); internal readonly IntervalUpdate lostTargetIntervalUpdate = 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; } public ISensorTarget SensorTarget { get; private set; } public float TargetWeight { get => _targetWeight; set => _targetWeight = Mathf.Clamp(value, 0, 1); } private float _targetWeight; private Vector3 _currentPosition; private CancellationTokenSource timeoutCancellationTokenSource=new(); private void OnEnable() { lostTargetIntervalUpdate.Interval = lostTargetInterval; SensorQueue.Register(Id=GetInstanceID(),this); } private void OnDisable() { SensorQueue.UnRegister(Id); } public async UniTask Execute(float delta) { delta=Mathf.Clamp(delta,0.01f,2); try { _currentPosition = transform.position; updateCount++; timeoutCancellationTokenSource?.Cancel(); timeoutCancellationTokenSource = new CancellationTokenSource(); await sensor.Execute(delta); if (audioSensor) { await audioSensor.Execute(delta); } reportBuilder?.AppendLine($"-----BEGIN------{updateCount}"); Profiler.BeginSample("Release Detected Buffer"); if (audioSensor) { var heard = audioSensor.Get(); if(heard.Any()) { CurrentTarget = heard.First(); reportBuilder?.AppendLine($"Heard:{CurrentTarget.name}"); reportBuilder?.AppendLine($"-----Complete------{updateCount}"); return; } } 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)||newDetected.Contains(SensorTarget?.Transform)) { lostTargetIntervalUpdate.Reset(); if (optionalRetargetInterval.Allow && optionalRetargetInterval.Value.AllowUpdate) { reportBuilder?.AppendLine("Retarget Interval Catch,Search New Target"); } else { reportBuilder?.AppendLine("Current Target Detected,Keep Target"); reportBuilder?.AppendLine($"-----Complete------{updateCount}"); return; } } else if(lostTargetInterval.Allow && CurrentTarget is null) { if (lostTargetIntervalUpdate.AllowUpdateWithoutReset) { CurrentTarget = null; reportBuilder?.AppendLine("Lost Target,Search New Target"); } else if(CurrentTarget is null ) { SensorTarget?.Detected(TargetWeight-=delta, this, root?root:this); if (TargetWeight <= 0) { SensorTarget = null; } reportBuilder?.AppendLine($"Lost Target,Forget After {lostTargetIntervalUpdate.Remaining}s"); } } Profiler.BeginSample("Filter Detected"); foreach (var x in newDetected.OrderBy(KeySelector)) { if(IsValid(x.GetComponent()) is false)continue; SensorTarget = x.GetComponent(); if (detectedTime.Allow && CurrentTarget is null) { TargetWeight +=detectedTime.Value / delta; SensorTarget?.Detected(TargetWeight, this, root?root:this); reportBuilder?.AppendLine($"New Target See,Weight is:{TargetWeight}"); } if (detectedTime.Allow is false || TargetWeight >= 1 && CurrentTarget != x) { CurrentTarget = x; SensorTarget?.Detected(1, this, root?root:this); 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; } #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