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

206 lines
6.4 KiB
Markdown
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.
# 灵灯碰触流程重构
## Task 1: 灵灯只伤害第一个碰到的敌人
修改 `Assets/eco/SpiritLantern.cs``OnTriggerEnter`
```csharp
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()` 改为分阶段流程:
```csharp
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.cs``OnTriggerEnter`,在调用 `health.Damage()` 之前读取敌人 maxHealth 并保存敌人位置:
```csharp
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 中添加一个公开属性:
```csharp
// HealthSystem.cs 新增
public bool IsDead => _isDead;
public int MaxHealth => maxHealth;
```
然后改为:
```csharp
health.Damage(damageOnContact);
if (health.IsDead)
SpawnSoulDrops(deathPos, health.MaxHealth);
```
## Task 4: 创建魂灵掉落物脚本 `SoulDrop.cs`
新建 `Assets/eco/SoulDrop.cs`
```csharp
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` 中添加:
```csharp
[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`,添加静态引用:
```csharp
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` | 新增 `IsDead``MaxHealth` 公开属性 |
| `Assets/UI/GameHUD.cs` | 新增 `SoulIconRect` 静态属性 |
| `Assets/eco/SoulDrop.cs` | **新建** 魂灵掉落物脚本 |
| `Assets/eco/Prefabs/SoulDrop.prefab` | **新建** 魂灵掉落物预制体 |
| `Assets/eco/Prefabs/SpiritLantern.prefab` | 新增 `soulDropPrefab` 引用 |