Files
gold_dolphin/unity/Assets/Light/scripts/EchoSystem.cs
2026-06-27 03:36:22 +08:00

156 lines
5.6 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 UnityEngine;
namespace IndianOceanAssets.Engine2_5D
{
/// <summary>
/// 回声系统 v2 —— 描边波纹版。
/// 按 E 键从玩家位置释放扩散波纹,波纹扫过的精灵(非光源物体)
/// 会显示外描边(默认白色,可自定义),维持一段时间后整体消散。
///
/// 与 v1 的区别:
/// - 不再在黑暗遮罩上画光波(那样会漏出地板)
/// - 改为通过全局 shader 参数控制精灵描边 shader (SpriteEchoOutline)
/// - 时间模型:扩散 → 维持(sustainTime) → 消散(fadeTime)
///
/// 挂载位置:玩家物体上(或跟随玩家的空物体)
/// 依赖:场景中需要有用 SpriteEchoOutline shader 的描边精灵
/// (由 EchoOutlineManager 自动生成)
/// </summary>
public class EchoSystem : MonoBehaviour
{
[Header("按键")]
[SerializeField] private KeyCode echoKey = KeyCode.E;
[Header("扩散参数")]
[Tooltip("波纹扩散速度(世界单位/秒)")]
[SerializeField] private float expandSpeed = 15f;
[Tooltip("最大扩散半径(到达后进入维持阶段)")]
[SerializeField] private float maxRadius = 30f;
[Tooltip("波纹前沿柔和宽度(精灵被扫到时的过渡宽度,越大越柔)")]
[SerializeField] private float ringWidth = 1.5f;
[Header("时间模型")]
[Tooltip("扩散到最大半径后,描边维持的时间(秒)")]
[SerializeField] private float sustainTime = 4f;
[Tooltip("维持结束后,描边消散的时间(秒)")]
[SerializeField] private float fadeTime = 1.5f;
[Header("描边外观")]
[Tooltip("描边颜色(默认白色,可自定义)")]
[SerializeField] private Color outlineColor = Color.white;
[Header("冷却")]
[Tooltip("回声冷却时间(秒)")]
[SerializeField] private float cooldown = 2f;
// 全局 shader 属性 ID通过 Shader.SetGlobalX 设置,所有描边精灵共享)
private static readonly int EchoCenterID = Shader.PropertyToID("_EchoCenter");
private static readonly int EchoRadiusID = Shader.PropertyToID("_EchoRadius");
private static readonly int EchoWidthID = Shader.PropertyToID("_EchoWidth");
private static readonly int EchoOutlineIntensityID = Shader.PropertyToID("_EchoOutlineIntensity");
private static readonly int EchoOutlineColorID = Shader.PropertyToID("_EchoOutlineColor");
private enum State { Idle, Expanding, Sustaining, Fading }
private State _state = State.Idle;
private float _radius;
private float _intensity;
private float _stateTimer;
private Vector2 _center;
private float _lastEchoTime = -999f;
private void Start()
{
// 初始化:描边关闭
Shader.SetGlobalFloat(EchoOutlineIntensityID, 0f);
Shader.SetGlobalColor(EchoOutlineColorID, outlineColor);
Shader.SetGlobalFloat(EchoWidthID, ringWidth);
}
private void Update()
{
// 触发
if (Input.GetKeyDown(echoKey) && _state == State.Idle && Time.time > _lastEchoTime + cooldown)
{
StartEcho();
}
// 状态机
switch (_state)
{
case State.Expanding: UpdateExpanding(); break;
case State.Sustaining: UpdateSustaining(); break;
case State.Fading: UpdateFading(); break;
}
// 每帧推送全局参数
Shader.SetGlobalVector(EchoCenterID, new Vector4(_center.x, _center.y, 0f, 0f));
Shader.SetGlobalFloat(EchoRadiusID, _radius);
Shader.SetGlobalFloat(EchoWidthID, ringWidth);
Shader.SetGlobalFloat(EchoOutlineIntensityID, _intensity);
Shader.SetGlobalColor(EchoOutlineColorID, outlineColor);
}
private void StartEcho()
{
_state = State.Expanding;
_radius = 0f;
_intensity = 1f;
_stateTimer = 0f;
_lastEchoTime = Time.time;
// 记录释放时刻的玩家位置(回声中心固定,不跟随移动)
Vector3 p = transform.position;
_center = new Vector2(p.x, p.z);
}
private void UpdateExpanding()
{
_radius += expandSpeed * Time.deltaTime;
if (_radius >= maxRadius)
{
_radius = maxRadius;
_state = State.Sustaining;
_stateTimer = 0f;
}
}
private void UpdateSustaining()
{
_stateTimer += Time.deltaTime;
_intensity = 1f; // 维持阶段满强度
if (_stateTimer >= sustainTime)
{
_state = State.Fading;
_stateTimer = 0f;
}
}
private void UpdateFading()
{
_stateTimer += Time.deltaTime;
// 消散阶段强度从 1 → 0
_intensity = Mathf.Clamp01(1f - _stateTimer / Mathf.Max(0.0001f, fadeTime));
if (_stateTimer >= fadeTime)
{
_state = State.Idle;
_intensity = 0f;
}
}
/// <summary>
/// 外部调用:强制停止回声
/// </summary>
public void StopEcho()
{
_state = State.Idle;
_intensity = 0f;
Shader.SetGlobalFloat(EchoOutlineIntensityID, 0f);
}
public bool IsActive => _state != State.Idle;
}
}