using System; using UnityEngine; namespace IndianOceanAssets.Engine2_5D { /// /// 单个灵灯的行为组件 —— 挂在灵灯 Prefab 上。 /// 放置后照亮周围区域(通过 LightSource), /// 怪物碰触时扣 1 点血并自毁回收, /// 存在时间到期后自动回收。 /// /// 【碰撞检测说明】 /// 灵灯自身持有 CircleCollider2D (isTrigger=true),是主动检测方。 /// 当怪物的 Collider2D 进入灵灯的触发区域时,OnTriggerEnter2D 被调用。 /// 需确保 Physics2D 碰撞矩阵中,灵灯所在 Layer 与怪物 Layer 可互相触发。 /// 灵灯不依赖 whatIsEnemy LayerMask,而是直接用 Tag/Component 判定目标。 /// public class SpiritLantern : MonoBehaviour { [Header("存在时间")] [Tooltip("灵灯放置后的存在时间(秒),到期自动回收")] [SerializeField] private float lifetime = 10f; [Header("伤害")] [Tooltip("怪物碰触灵灯时受到的伤害")] [SerializeField] private int damageOnContact = 1; /// 灵灯被回收时触发(被 SpiritLanternSystem 订阅) public event Action OnLanternRecalled; private bool _recalled; private SpiritLanternSystem _ownerSystem; /// /// 设置灵灯的所有者系统引用,用于回收时显式取消事件订阅。 /// 由 SpiritLanternSystem.PlaceLantern() 调用。 /// public void SetOwner(SpiritLanternSystem system) { _ownerSystem = system; } private void Update() { // 存在时间倒计时 lifetime -= Time.deltaTime; if (lifetime <= 0f) { Recall(); } } /// /// 怪物碰触灵灯时触发:扣血 + 回收灵灯。 /// 灵灯的 Trigger 主动检测进入的碰撞体,排除玩家后对怪物造成伤害。 /// private void OnTriggerEnter2D(Collider2D other) { // 排除玩家(玩家 Tag 为 "Player"),避免放置时与玩家碰撞体误触发 if (other.CompareTag("Player")) return; // 尝试获取怪物的 HealthSystem HealthSystem health = other.GetComponent(); if (health != null) { health.Damage(damageOnContact); Recall(); } } /// /// 回收灵灯:显式取消事件订阅 → 触发通知 → 销毁 GameObject。 /// 使用 _recalled 标志防止重复触发。 /// private void Recall() { if (_recalled) return; _recalled = true; // 1. 先通过 System 显式取消事件订阅,防止内存泄漏 if (_ownerSystem != null) { _ownerSystem.UnregisterLantern(this); } // 2. 通知其他可能的订阅者(如 UI 等) OnLanternRecalled?.Invoke(); OnLanternRecalled = null; // 3. 销毁灵灯 GameObject Destroy(gameObject); } /// /// 安全网:如果灵灯被非正常销毁(场景卸载、外部 Destroy 等), /// 确保事件仍然触发,避免 SpiritLanternSystem 的灵灯计数永久丢失。 /// private void OnDestroy() { if (!_recalled) { _recalled = true; // 先取消订阅,再触发通知 if (_ownerSystem != null) { _ownerSystem.UnregisterLantern(this); } OnLanternRecalled?.Invoke(); OnLanternRecalled = null; } } } }