208 lines
7.2 KiB
C#
208 lines
7.2 KiB
C#
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 物理系统以匹配敌人的 CapsuleCollider(3D)。
|
||
/// </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;
|
||
}
|
||
}
|
||
}
|
||
}
|