Files
gold_dolphin/unity/.qoder/specs/灵灯碰触流程重构_task-ffd.md

6.4 KiB
Raw Permalink Blame History

灵灯碰触流程重构

Task 1: 灵灯只伤害第一个碰到的敌人

修改 Assets/eco/SpiritLantern.csOnTriggerEnter

private void OnTriggerEnter(Collider other)
{
    if (_recalled) return;  // 新增:已触发则忽略后续碰撞
    if (other.CompareTag("Player")) return;
    HealthSystem health = other.GetComponent<HealthSystem>();
    if (health != null)
    {
        health.Damage(damageOnContact);
        StartRecallSequence();  // 改名原 Recall(),改为协程流程
    }
}

Task 2: 灵灯 2s 渐隐消失 + 灯光同步消失

修改 Assets/eco/SpiritLantern.cs,将 Recall() 改为分阶段流程:

private void StartRecallSequence()
{
    if (_recalled) return;
    _recalled = true;
    // 1. 立即取消事件订阅 + 通知系统(回收灵灯名额)
    if (_ownerSystem != null) _ownerSystem.UnregisterLantern(this);
    OnLanternRecalled?.Invoke();
    OnLanternRecalled = null;
    // 2. 禁用碰撞体,开始 2s 渐隐
    GetComponent<Collider>().enabled = false;
    StartCoroutine(FadeOutAndDestroy(2f));
}

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 = 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);
}

同时修改超时回收(lifetime <= 0 时)也走 StartRecallSequence() 而非直接销毁。

Task 3: 敌人被灵灯杀死时的消失特效 + 魂灵掉落

修改 Assets/eco/SpiritLantern.csOnTriggerEnter,在调用 health.Damage() 之前读取敌人 maxHealth 并保存敌人位置:

private void OnTriggerEnter(Collider other)
{
    if (_recalled) return;
    if (other.CompareTag("Player")) return;
    HealthSystem health = other.GetComponent<HealthSystem>();
    if (health != null)
    {
        // 在伤害前记录信息(用于掉落)
        Vector3 deathPos = other.transform.position;
        int enemyMaxHp = GetMaxHealth(health);
        health.Damage(damageOnContact);
        // 如果敌人死亡(血量<=0),生成魂灵掉落
        // 注意:Die() 会 Destroy gameObject,所以位置已提前保存
        if (health health <= 0) // 需要用反射或公开属性判断
            SpawnSoulDrops(deathPos, enemyMaxHp);
        StartRecallSequence();
    }
}

由于 HealthSystem.health 是 private,需要在 HealthSystem 中添加一个公开属性:

// HealthSystem.cs 新增
public bool IsDead => _isDead;
public int MaxHealth => maxHealth;

然后改为:

health.Damage(damageOnContact);
if (health.IsDead)
    SpawnSoulDrops(deathPos, health.MaxHealth);

Task 4: 创建魂灵掉落物脚本 SoulDrop.cs

新建 Assets/eco/SoulDrop.cs

public class SoulDrop : MonoBehaviour
{
    [SerializeField] private float flyDuration = 0.8f;
    [SerializeField] private int scoreValue = 1;
    
    private RectTransform _targetUI; // HUD 魂灵图标位置
    
    public void Initialize(RectTransform targetUI)
    {
        _targetUI = targetUI;
        StartCoroutine(FlyToHUD());
    }
    
    private IEnumerator FlyToHUD()
    {
        Vector3 startPos = transform.position;
        // 获取目标屏幕位置
        Camera cam = Camera.main;
        Vector3 targetScreenPos = new Vector3(_targetUI.position.x, _targetUI.position.y, cam.WorldToScreenPoint(startPos).z);
        Vector3 targetWorldPos = cam.ScreenToWorldPoint(targetScreenPos);
        
        float elapsed = 0f;
        while (elapsed < flyDuration)
        {
            elapsed += Time.deltaTime;
            float t = elapsed / flyDuration;
            // 抛物线轨迹
            transform.position = Vector3.Lerp(startPos, targetWorldPos, t);
            transform.localScale = Vector3.one * (1f - t * 0.5f); // 缩小
            yield return null;
        }
        // 到达 HUD,加分
        if (ScoreManager.Instance != null)
            ScoreManager.Instance.AddScore(scoreValue);
        Destroy(gameObject);
    }
}

Task 5: 魂灵掉落物生成逻辑

SpiritLantern.cs 中添加:

[SerializeField] private GameObject soulDropPrefab;

private void SpawnSoulDrops(Vector3 position, int enemyMaxHp)
{
    int count = enemyMaxHp * Random.Range(2, 6); // 2~5倍
    RectTransform hudTarget = GameHUD.SoulIconRect; // 静态引用
    for (int i = 0; i < count; i++)
    {
        Vector3 offset = new Vector3(Random.Range(-0.3f, 0.3f), Random.Range(0f, 0.3f), 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);
    }
}

Task 6: GameHUD 暴露魂灵图标 RectTransform

修改 Assets/UI/GameHUD.cs,添加静态引用:

public static RectTransform SoulIconRect { get; private set; }

void Start()
{
    // ... 现有代码 ...
    if (soulIcon != null)
        SoulIconRect = soulIcon.rectTransform;
}

Task 7: 创建魂灵掉落物预制体

  • 创建一个小精灵 GameObjectSpriteRenderer + Collider2D trigger + SoulDrop 脚本)
  • 保存为 Assets/eco/Prefabs/SoulDrop.prefab
  • 将预制体引用赋给 SpiritLantern.prefab 的 soulDropPrefab 字段

Task 8: 创建魂灵掉落物 Sprite 资源

  • 使用 ImageGen 工具生成一个小光点/魂灵图标
  • 导入 Unity 并赋给 SoulDrop.prefab 的 SpriteRenderer

涉及文件汇总

文件 修改类型
Assets/eco/SpiritLantern.cs 重构碰撞+渐隐+掉落逻辑
Assets/2.5D Engine/Scripts/HealthSystem.cs 新增 IsDeadMaxHealth 公开属性
Assets/UI/GameHUD.cs 新增 SoulIconRect 静态属性
Assets/eco/SoulDrop.cs 新建 魂灵掉落物脚本
Assets/eco/Prefabs/SoulDrop.prefab 新建 魂灵掉落物预制体
Assets/eco/Prefabs/SpiritLantern.prefab 新增 soulDropPrefab 引用