using System.Collections.Generic; using System.Linq; using Data; using Managers; using UnityEngine; namespace Entity { /// /// 视野范围检测器组件。 /// 挂载到包含触发器碰撞体的GameObject上,用于检测视野范围内的Entity。 /// public class VisionRangeDetector : MonoBehaviour { // 允许在Inspector中手动指定Collider,或在Awake时自动获取。 [SerializeField] [Tooltip("用于检测视野范围的触发器碰撞体。如果未指定,将在Awake时自动获取。")] private Collider2D _collider; // 缓存所属的Entity组件,用于获取派系信息。 private Entity _ownerEntity; // 将List改为HashSet,优化添加和移除的性能。 private HashSet _entitiesInRange = new HashSet(); // 维护在视野范围内的Entity集合 private Transform _myTransform; // 缓存Transform组件以提高性能 // 【逻辑修改】用于记录上次清理的帧数,避免在同一帧内重复清理。 private int _lastCleanupFrame = -1; private void Awake() { _myTransform = transform; // 缓存自身Transform // 1. 检查并确保GameObject上存在触发器碰撞体 if (_collider == null) { _collider = GetComponent(); } if (_collider == null) { Debug.LogError( $"VisionRangeDetector on {gameObject.name} requires a Collider component. Please add one or assign it in the Inspector.", this); enabled = false; // 如果没有碰撞体,禁用此组件 return; } if (!_collider.isTrigger) { Debug.LogWarning( $"VisionRangeDetector on {gameObject.name} works best with a Trigger Collider. Setting isTrigger to true.", this); _collider.isTrigger = true; } // 2. 检查AffiliationManager是否存在 if (AffiliationManager.Instance == null) { Debug.LogError( "AffiliationManager.Instance is null. Please ensure an AffiliationManager exists in the scene.", this); enabled = false; // 如果没有派系管理器,禁用此组件 return; // 提前返回,避免后续操作依赖于AffiliationManager } // 3. 获取所属的Entity组件,并使用其affiliation。 _ownerEntity = GetComponentInParent(); if (_ownerEntity == null) { _ownerEntity = GetComponent(); } if (_ownerEntity == null) { Debug.LogError( $"VisionRangeDetector on {gameObject.name}: No Entity component found in parent or self. This component cannot determine its affiliation and will be disabled.", this); enabled = false; return; } } // 【逻辑修改】移除LateUpdate,清理逻辑移至获取方法中。 // private void LateUpdate() // { // _entitiesInRange.RemoveWhere(e => e == null); // } /// /// 【逻辑修改】在获取实体列表前,执行一次清理操作(每帧最多一次)。 /// private void CleanEntitiesInRange() { // 只有当当前帧与上次清理的帧不同时才执行清理 if (Time.frameCount != _lastCleanupFrame) { _entitiesInRange.RemoveWhere(e => e == null); _lastCleanupFrame = Time.frameCount; // 更新上次清理的帧数 // Debug.Log($"Cleaned _entitiesInRange in frame {Time.frameCount}. Current count: {_entitiesInRange.Count}"); } } /// /// 当有其他碰撞体进入触发器范围时调用。 /// /// 进入触发器的碰撞体。 private void OnTriggerEnter(Collider other) { Entity entity = other.GetComponent(); if (entity != null) { if (entity.gameObject == _ownerEntity.gameObject || entity.transform.IsChildOf(_ownerEntity.transform)) { return; } if (_entitiesInRange.Add(entity)) { // Debug.Log($"Entity '{entity.name}' (Affiliation: {entity.affiliation}) entered range. Total: {_entitiesInRange.Count}"); } } } /// /// 当有其他碰撞体离开触发器范围时调用。 /// /// 离开触发器的碰撞体。 private void OnTriggerExit(Collider other) { Entity entity = other.GetComponent(); if (entity != null) { if (_entitiesInRange.Remove(entity)) { // Debug.Log($"Entity '{entity.name}' (Affiliation: {entity.affiliation}) exited range. Total: {_entitiesInRange.Count}"); } } } /// /// 获取视野范围内最近的N个实体。 /// /// 要获取的实体数量。 /// 最近的N个实体列表。 public List GetNearestNEntities(int n) { if (n <= 0) return new List(); // 【逻辑修改】在获取前清理一次集合 CleanEntitiesInRange(); return _entitiesInRange .OrderBy(e => Vector3.Distance(_myTransform.position, e.transform.position)) .Take(n) .ToList(); } /// /// 获取视野范围内最近的N个指定关系的实体(敌对、友好、中立)。 /// /// 要获取的实体数量。 /// 目标派系关系。 /// 最近的N个指定关系的实体列表。 public List GetNearestNEntitiesByRelation(int n, Relation targetRelation) { if (AffiliationManager.Instance == null) { Debug.LogError( "AffiliationManager.Instance is null. Cannot get entities by relation. VisionRangeDetector should have been disabled if this was an issue.", this); return new List(); } if (n <= 0) return new List(); // 【逻辑修改】在获取前清理一次集合 CleanEntitiesInRange(); return _entitiesInRange .Where(e => AffiliationManager.Instance.GetRelation(_ownerEntity.affiliation, e.affiliation) == targetRelation) .OrderBy(e => Vector3.Distance(_myTransform.position, e.transform.position)) .Take(n) .ToList(); } } }