using System; using UnityEngine; using UnityEngine.UI; using System.Collections; using GameFramework; namespace IndianOceanAssets.Engine2_5D { /// /// 单个灵灯的行为组件 —— 挂在灵灯 Prefab 上。 /// 放置后照亮周围区域(通过 LightSource), /// 只伤害第一个碰触的怪物,触发 2s 渐隐消失流程, /// 怪物死亡后掉落魂灵飞向 HUD。 /// /// 【碰撞检测说明】 /// 灵灯自身持有 SphereCollider (isTrigger=true),是主动检测方。 /// 使用 3D 物理系统以匹配敌人的 CapsuleCollider(3D)。 /// public class SpiritLantern : MonoBehaviour { [Header("存在时间")] [Tooltip("灵灯放置后的存在时间(秒),到期自动回收")] [SerializeField] private float lifetime = 10f; [Header("伤害")] [Tooltip("怪物碰触灵灯时受到的伤害")] [SerializeField] private int damageOnContact = 1; [Header("渐隐")] [Tooltip("碰触后灵灯渐隐消失的时间(秒)")] [SerializeField] private float fadeOutDuration = 2f; [Header("魂灵掉落")] [Tooltip("魂灵掉落物预制体")] [SerializeField] private GameObject soulDropPrefab; /// 灵灯被回收时触发(被 SpiritLanternSystem 订阅) public event Action OnLanternRecalled; private bool _recalled; private SpiritLanternSystem _ownerSystem; /// /// 设置灵灯的所有者系统引用,用于回收时显式取消事件订阅。 /// 由 SpiritLanternSystem.PlaceLantern() 调用。 /// public void SetOwner(SpiritLanternSystem system) { _ownerSystem = system; } private void Awake() { // 确保 Rigidbody 为运动学模式,不受重力和物理影响 var rb = GetComponent(); if (rb != null) { rb.isKinematic = true; rb.useGravity = false; } } private void Update() { // 存在时间倒计时 lifetime -= Time.deltaTime; if (lifetime <= 0f) { StartRecallSequence(); } } /// /// 怪物碰触灵灯时触发:只伤害第一个碰到的敌人。 /// 已触发回收后不再响应后续碰撞。 /// private void OnTriggerEnter(Collider other) { // 已触发则忽略后续碰撞 if (_recalled) return; // 排除玩家 if (other.CompareTag("Player")) return; // 尝试获取怪物的 HealthSystem HealthSystem health = other.GetComponent(); if (health != null) { // 在伤害前记录信息(用于掉落,因为 Die() 会 Destroy gameObject) Vector3 deathPos = other.transform.position; int enemyMaxHp = health.MaxHealth; health.Damage(damageOnContact); // 如果敌人死亡,生成魂灵掉落 if (health.IsDead && soulDropPrefab != null) { Debug.Log($"[SpiritLantern] 敌人死亡! maxHp={enemyMaxHp}, 准备生成魂灵掉落"); SpawnSoulDrops(deathPos, enemyMaxHp); } else if (health.IsDead) { Debug.LogWarning("[SpiritLantern] 敌人死亡但 soulDropPrefab 未配置!"); } // 开始渐隐消失流程 StartRecallSequence(); } } /// /// 开始回收流程:立即通知系统回收名额 → 禁用碰撞 → 2s 渐隐 → 销毁。 /// private void StartRecallSequence() { if (_recalled) return; _recalled = true; // 1. 立即取消事件订阅 + 通知系统(回收灵灯名额) if (_ownerSystem != null) { _ownerSystem.UnregisterLantern(this); } OnLanternRecalled?.Invoke(); OnLanternRecalled = null; // 2. 禁用碰撞体,开始渐隐 var col = GetComponent(); if (col != null) col.enabled = false; StartCoroutine(FadeOutAndDestroy(fadeOutDuration)); } /// /// 渐隐协程:SpriteRenderer alpha 和 LightSource intensity 同步归零后销毁。 /// private IEnumerator FadeOutAndDestroy(float duration) { var sr = GetComponent(); var light = GetComponent(); float startAlpha = sr != null ? sr.color.a : 1f; float startIntensity = light != null ? light.Intensity : 1f; float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; float t = Mathf.Clamp01(elapsed / duration); if (sr != null) sr.color = new Color(sr.color.r, sr.color.g, sr.color.b, Mathf.Lerp(startAlpha, 0f, t)); if (light != null) light.SetIntensity(Mathf.Lerp(startIntensity, 0f, t)); yield return null; } Destroy(gameObject); } /// /// 在敌人死亡位置生成魂灵掉落物,飞向 HUD 魂灵计数。 /// 数量 = 敌人最大血量 × 2~5 倍。 /// private void SpawnSoulDrops(Vector3 position, int enemyMaxHp) { int count = enemyMaxHp * UnityEngine.Random.Range(2, 6); // 2~5 倍 RectTransform hudTarget = GameHUD.SoulIconRect; Debug.Log($"[SpiritLantern] SpawnSoulDrops: count={count}, hudTarget={(hudTarget != null ? hudTarget.name : "NULL")}"); for (int i = 0; i < count; i++) { Vector3 offset = new Vector3( UnityEngine.Random.Range(-0.3f, 0.3f), UnityEngine.Random.Range(0f, 0.3f), UnityEngine.Random.Range(-0.3f, 0.3f) ); GameObject drop = Instantiate(soulDropPrefab, position + offset, Quaternion.identity); SoulDrop soul = drop.GetComponent(); if (soul != null) soul.Initialize(hudTarget); } } /// /// 安全网:如果灵灯被非正常销毁(场景卸载、外部 Destroy 等), /// 确保事件仍然触发,避免 SpiritLanternSystem 的灵灯计数永久丢失。 /// private void OnDestroy() { if (!_recalled) { _recalled = true; // 先取消订阅,再触发通知 if (_ownerSystem != null) { _ownerSystem.UnregisterLantern(this); } OnLanternRecalled?.Invoke(); OnLanternRecalled = null; } } } }