Files
gold_dolphin/unity/Assets/eco/SpiritLantern.cs
T
2026-07-05 15:49:57 +08:00

208 lines
7.2 KiB
C#
Raw 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;
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using GameFramework;
namespace IndianOceanAssets.Engine2_5D
{
/// <summary>
/// 单个灵灯的行为组件 —— 挂在灵灯 Prefab 上。
/// 放置后照亮周围区域(通过 LightSource),
/// 只伤害第一个碰触的怪物,触发 2s 渐隐消失流程,
/// 怪物死亡后掉落魂灵飞向 HUD。
///
/// 【碰撞检测说明】
/// 灵灯自身持有 SphereCollider (isTrigger=true),是主动检测方。
/// 使用 3D 物理系统以匹配敌人的 CapsuleCollider3D)。
/// </summary>
public class SpiritLantern : MonoBehaviour
{
[Header("存在时间")]
[Tooltip("灵灯放置后的存在时间(秒),到期自动回收")]
[SerializeField] private float lifetime = 10f;
[Header("伤害")]
[Tooltip("怪物碰触灵灯时受到的伤害")]
[SerializeField] private int damageOnContact = 3;
[Header("渐隐")]
[Tooltip("碰触后灵灯渐隐消失的时间(秒)")]
[SerializeField] private float fadeOutDuration = 2f;
[Header("魂灵掉落")]
[Tooltip("魂灵掉落物预制体")]
[SerializeField] private GameObject soulDropPrefab;
/// <summary>灵灯被回收时触发(被 SpiritLanternSystem 订阅)</summary>
public event Action OnLanternRecalled;
private bool _recalled;
private SpiritLanternSystem _ownerSystem;
/// <summary>
/// 设置灵灯的所有者系统引用,用于回收时显式取消事件订阅。
/// 由 SpiritLanternSystem.PlaceLantern() 调用。
/// </summary>
public void SetOwner(SpiritLanternSystem system)
{
_ownerSystem = system;
}
private void Awake()
{
// 确保 Rigidbody 为运动学模式,不受重力和物理影响
var rb = GetComponent<Rigidbody>();
if (rb != null)
{
rb.isKinematic = true;
rb.useGravity = false;
}
}
private void Update()
{
// 存在时间倒计时
lifetime -= Time.deltaTime;
if (lifetime <= 0f)
{
StartRecallSequence();
}
}
/// <summary>
/// 怪物碰触灵灯时触发:只伤害第一个碰到的敌人。
/// 已触发回收后不再响应后续碰撞。
/// </summary>
private void OnTriggerEnter(Collider other)
{
// 已触发则忽略后续碰撞
if (_recalled) return;
// 排除玩家
if (other.CompareTag("Player")) return;
// 尝试获取怪物的 HealthSystem
HealthSystem health = other.GetComponent<HealthSystem>();
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();
}
}
/// <summary>
/// 开始回收流程:立即通知系统回收名额 → 禁用碰撞 → 2s 渐隐 → 销毁。
/// </summary>
private void StartRecallSequence()
{
if (_recalled) return;
_recalled = true;
// 1. 立即取消事件订阅 + 通知系统(回收灵灯名额)
if (_ownerSystem != null)
{
_ownerSystem.UnregisterLantern(this);
}
OnLanternRecalled?.Invoke();
OnLanternRecalled = null;
// 2. 禁用碰撞体,开始渐隐
var col = GetComponent<Collider>();
if (col != null) col.enabled = false;
StartCoroutine(FadeOutAndDestroy(fadeOutDuration));
}
/// <summary>
/// 渐隐协程:SpriteRenderer alpha 和 LightSource intensity 同步归零后销毁。
/// </summary>
private IEnumerator FadeOutAndDestroy(float duration)
{
var sr = GetComponent<SpriteRenderer>();
var light = GetComponent<LightSource>();
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);
}
/// <summary>
/// 在敌人死亡位置生成魂灵掉落物,飞向 HUD 魂灵计数。
/// 数量 = 敌人最大血量 × 2~5 倍。
/// </summary>
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<SoulDrop>();
if (soul != null)
soul.Initialize(hudTarget);
}
}
/// <summary>
/// 安全网:如果灵灯被非正常销毁(场景卸载、外部 Destroy 等),
/// 确保事件仍然触发,避免 SpiritLanternSystem 的灵灯计数永久丢失。
/// </summary>
private void OnDestroy()
{
if (!_recalled)
{
_recalled = true;
// 先取消订阅,再触发通知
if (_ownerSystem != null)
{
_ownerSystem.UnregisterLantern(this);
}
OnLanternRecalled?.Invoke();
OnLanternRecalled = null;
}
}
}
}