using System; using System.Collections.Generic; using UnityEngine; namespace IndianOceanAssets.Engine2_5D { /// /// 灵灯系统 —— 挂在玩家物体上。 /// 按 U 键在玩家当前位置放置灵灯,灵灯照亮周围区域, /// 怪物碰触灵灯会被扣 1 点血,灵灯消失并被回收。 /// 灵灯有存在时间,超时后自动回收。 /// public class SpiritLanternSystem : MonoBehaviour { [Header("灵灯数量")] [Tooltip("最大可持有灵灯数量")] [SerializeField] private int maxLanterns = 5; [Header("按键")] [SerializeField] private KeyCode lanternKey = KeyCode.U; [Header("冷却")] [Tooltip("放置灵灯的冷却时间(秒)")] [SerializeField] private float cooldown = 3f; [Header("灵灯预制体")] [SerializeField] private GameObject lanternPrefab; private int _remainingLanterns; private float _lastPlaceTime = -999f; /// 跟踪所有已放置的灵灯,用于回收时取消事件订阅,防止内存泄漏 private readonly List _activeLanterns = new List(); /// 当前可释放的灵灯数量(供 UI 使用) public int RemainingLanterns => _remainingLanterns; /// 冷却是否就绪 public bool IsCooldownReady => Time.time > _lastPlaceTime + cooldown; /// 冷却剩余时间(供 UI 使用) public float CooldownRemaining => Mathf.Max(0f, (_lastPlaceTime + cooldown) - Time.time); private void Start() { _remainingLanterns = maxLanterns; } private void Update() { // 按 U 且 CD 就绪 且 还有灵灯 → 放置 if (Input.GetKeyDown(lanternKey) && _remainingLanterns > 0 && Time.time > _lastPlaceTime + cooldown && lanternPrefab != null) { PlaceLantern(); } } /// /// 在玩家当前位置放置一个灵灯。 /// private void PlaceLantern() { _remainingLanterns--; _lastPlaceTime = Time.time; // 在玩家位置实例化灵灯 GameObject lanternObj = Instantiate(lanternPrefab, transform.position, Quaternion.identity); SpiritLantern lantern = lanternObj.GetComponent(); if (lantern != null) { // 设置所有者引用,用于回收时取消事件订阅 lantern.SetOwner(this); // 订阅灵灯回收事件,并跟踪该灵灯 lantern.OnLanternRecalled += OnLanternRecalled; _activeLanterns.Add(lantern); } } /// /// 灵灯被回收时调用(怪物碰触或超时)。 /// 清理已销毁的灵灯引用,防止内存泄漏。 /// private void OnLanternRecalled() { // 清理列表中已销毁的灵灯引用 _activeLanterns.RemoveAll(l => l == null); _remainingLanterns = Mathf.Min(_remainingLanterns + 1, maxLanterns); } /// /// 显式取消订阅指定灵灯的事件并从跟踪列表移除。 /// 由 SpiritLantern 在销毁前调用,确保事件订阅被正确释放。 /// public void UnregisterLantern(SpiritLantern lantern) { if (lantern != null) { lantern.OnLanternRecalled -= OnLanternRecalled; _activeLanterns.Remove(lantern); } } /// /// 安全网:玩家销毁或场景卸载时,清理所有灵灯的事件订阅。 /// private void OnDestroy() { foreach (var lantern in _activeLanterns) { if (lantern != null) lantern.OnLanternRecalled -= OnLanternRecalled; } _activeLanterns.Clear(); } // ===== 编辑器可视化 ===== private void OnDrawGizmosSelected() { // 绘制冷却指示 if (Time.time < _lastPlaceTime + cooldown) { float remaining = (_lastPlaceTime + cooldown) - Time.time; float ratio = remaining / cooldown; Gizmos.color = new Color(1f, 0.6f, 0f, 0.3f); Gizmos.DrawWireSphere(transform.position, 1f * ratio); } } } }