using System.Collections.Generic; using Cielonos.MainGame.Characters; using SLSUtilities.General; using SoftCircuits.Collections; using UnityEngine; namespace Cielonos.MainGame.Inventory { /// /// 光环子模块。持续捕获指定半径内的角色,并通过 Enter / Stay / Exit 事件通知订阅方。 /// 不使用 Data ScriptableObject,由道具在代码中直接构造。 /// public class AuraSubmodule : SubmoduleBase { /// /// 当前处于光环内的角色集合。 /// public HashSet charactersInAura = new HashSet(); /// /// 其他角色进入光环时触发,参数为进入的角色。 /// public OrderedDictionary> onOtherEnterAura = new(); /// /// 其他角色持续处于光环内时触发(按 stayInterval 间隔),参数为在光环内的角色。 /// public OrderedDictionary> onOtherStayAura = new(); /// /// 其他角色离开光环时触发,参数为离开的角色。 /// public OrderedDictionary> onOtherExitAura = new(); /// /// 光环半径。 /// public float auraRadius; /// /// Stay 计时器,到达 stayInterval 后对所有在光环内的角色触发 onOtherStayAura。 /// public Timer stayTimer; /// /// 物理检测的 LayerMask。 /// private readonly int _detectionLayerMask; private const int MaxDetectionCount = 32; private readonly Collider[] _overlapBuffer = new Collider[MaxDetectionCount]; /// /// 用于比较前后帧角色集合的临时缓冲。 /// private readonly HashSet _currentFrameCharacters = new HashSet(); /// /// 构造光环子模块。 /// /// 所属道具。 /// 光环半径。 /// Stay 事件触发间隔(秒),默认 0.5 秒。 public AuraSubmodule(ItemBase owner, float auraRadius, float stayInterval = 0.5f) : base(owner) { this.auraRadius = auraRadius; stayTimer = new Timer(stayInterval, isInfinite: true); stayTimer.onComplete.Add(new PrioritizedAction(OnStayTimerComplete)); _detectionLayerMask = LayerMask.GetMask("HurtBox"); } /// /// 每帧调用,驱动物理检测和事件派发。 /// public void Update(float deltaTime) { DetectCharacters(); stayTimer.Update(deltaTime); } /// /// 清理所有追踪状态,对仍在光环内的角色触发 Exit 事件。 /// public void Dispose() { foreach (var character in charactersInAura) { if (character != null) { onOtherExitAura.Invoke(character); } } charactersInAura.Clear(); } /// /// 执行球形物理检测,比较前后帧角色集合,触发 Enter / Exit 事件。 /// private void DetectCharacters() { _currentFrameCharacters.Clear(); Vector3 center = owner.player.transform.position; int hitCount = Physics.OverlapSphereNonAlloc(center, auraRadius, _overlapBuffer, _detectionLayerMask); for (int i = 0; i < hitCount; i++) { CharacterBase character = _overlapBuffer[i].GetComponentInParent(); if (character == null || character == owner.player) continue; _currentFrameCharacters.Add(character); } // 检测新进入光环的角色 foreach (var character in _currentFrameCharacters) { if (charactersInAura.Add(character)) { onOtherEnterAura.Invoke(character); } } // 检测离开光环的角色(从旧集合中移除不在当前帧的角色) charactersInAura.RemoveWhere(character => { if (_currentFrameCharacters.Contains(character)) return false; if (character != null) { onOtherExitAura.Invoke(character); } return true; }); } /// /// Stay 计时器到期时,对所有在光环内的角色触发 onOtherStayAura。 /// private void OnStayTimerComplete() { foreach (var character in charactersInAura) { if (character != null) { onOtherStayAura.Invoke(character); } } } } }