Files
Cielonos/Assets/Scripts/MainGame/Items/Submodules/AuraSubmodule.cs
SoulliesOfficial b5cb6152ff MusicBeat
2026-05-26 00:21:27 -04:00

155 lines
5.3 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using Cielonos.MainGame.Characters;
using SLSUtilities.General;
using SoftCircuits.Collections;
using UnityEngine;
namespace Cielonos.MainGame.Inventory
{
/// <summary>
/// 光环子模块。持续捕获指定半径内的角色,并通过 Enter / Stay / Exit 事件通知订阅方。
/// 不使用 Data ScriptableObject由道具在代码中直接构造。
/// </summary>
public class AuraSubmodule : SubmoduleBase<ItemBase>
{
/// <summary>
/// 当前处于光环内的角色集合。
/// </summary>
public HashSet<CharacterBase> charactersInAura = new HashSet<CharacterBase>();
/// <summary>
/// 其他角色进入光环时触发,参数为进入的角色。
/// </summary>
public OrderedDictionary<string, PrioritizedAction<CharacterBase>> onOtherEnterAura = new();
/// <summary>
/// 其他角色持续处于光环内时触发(按 stayInterval 间隔),参数为在光环内的角色。
/// </summary>
public OrderedDictionary<string, PrioritizedAction<CharacterBase>> onOtherStayAura = new();
/// <summary>
/// 其他角色离开光环时触发,参数为离开的角色。
/// </summary>
public OrderedDictionary<string, PrioritizedAction<CharacterBase>> onOtherExitAura = new();
/// <summary>
/// 光环半径。
/// </summary>
public float auraRadius;
/// <summary>
/// Stay 计时器,到达 stayInterval 后对所有在光环内的角色触发 onOtherStayAura。
/// </summary>
public Timer stayTimer;
/// <summary>
/// 物理检测的 LayerMask。
/// </summary>
private readonly int _detectionLayerMask;
private const int MaxDetectionCount = 32;
private readonly Collider[] _overlapBuffer = new Collider[MaxDetectionCount];
/// <summary>
/// 用于比较前后帧角色集合的临时缓冲。
/// </summary>
private readonly HashSet<CharacterBase> _currentFrameCharacters = new HashSet<CharacterBase>();
/// <summary>
/// 构造光环子模块。
/// </summary>
/// <param name="owner">所属道具。</param>
/// <param name="auraRadius">光环半径。</param>
/// <param name="stayInterval">Stay 事件触发间隔(秒),默认 0.5 秒。</param>
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");
}
/// <summary>
/// 每帧调用,驱动物理检测和事件派发。
/// </summary>
public void Update(float deltaTime)
{
DetectCharacters();
stayTimer.Update(deltaTime);
}
/// <summary>
/// 清理所有追踪状态,对仍在光环内的角色触发 Exit 事件。
/// </summary>
public void Dispose()
{
foreach (var character in charactersInAura)
{
if (character != null)
{
onOtherExitAura.Invoke(character);
}
}
charactersInAura.Clear();
}
/// <summary>
/// 执行球形物理检测,比较前后帧角色集合,触发 Enter / Exit 事件。
/// </summary>
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<CharacterBase>();
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;
});
}
/// <summary>
/// Stay 计时器到期时,对所有在光环内的角色触发 onOtherStayAura。
/// </summary>
private void OnStayTimerComplete()
{
foreach (var character in charactersInAura)
{
if (character != null)
{
onOtherStayAura.Invoke(character);
}
}
}
}
}