新增回声扩散特效

This commit is contained in:
2026-07-06 13:43:09 +08:00
parent 11f04b2ac8
commit 5771d9157e
5 changed files with 241 additions and 4 deletions
+127 -2
View File
@@ -50,16 +50,38 @@ namespace IndianOceanAssets.Engine2_5D
[Tooltip("描边颜色(默认白色,可自定义)")]
[SerializeField] private Color outlineColor = Color.white;
[Header("红圈视觉")]
[Tooltip("红圈颜色")]
[SerializeField] private Color ringColor = new Color(1f, 0.15f, 0.1f, 1f);
[Tooltip("红圈宽度(世界单位)")]
[SerializeField] private float ringVisualWidth = 2.5f;
[Tooltip("红圈离地高度(避免Z-fighting")]
[SerializeField] private float ringYOffset = 0.1f;
[Tooltip("红圈渐隐总时间(秒)。从释放瞬间开始线性渐隐,到 0 后隐藏。默认 4 秒")]
[SerializeField] private float ringFadeTime = 4f;
[Tooltip("红圈扩散速度(世界单位/秒),独立于波纹扩散速度")]
[SerializeField] private float ringExpandSpeed = 15f;
[Header("冷却")]
[Tooltip("回声冷却时间(秒)")]
[SerializeField] private float cooldown = 3f;
// 红圈视觉
private GameObject _ringVisual;
private Material _ringMaterial;
// 全局 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 static readonly int EchoRingAlphaID = Shader.PropertyToID("_EchoRingAlpha");
private static readonly int EchoRingRadiusID = Shader.PropertyToID("_EchoRingRadius");
private enum State { Idle, Expanding, Sustaining, Fading }
@@ -69,6 +91,8 @@ namespace IndianOceanAssets.Engine2_5D
private float _stateTimer;
private Vector2 _center;
private float _lastEchoTime = -999f;
private float _ringAlpha = 0f;
private float _ringRadius = 0f;
private void Start()
{
@@ -76,12 +100,58 @@ namespace IndianOceanAssets.Engine2_5D
Shader.SetGlobalFloat(EchoOutlineIntensityID, 0f);
Shader.SetGlobalColor(EchoOutlineColorID, outlineColor);
Shader.SetGlobalFloat(EchoWidthID, ringWidth);
Shader.SetGlobalFloat(EchoRingAlphaID, 0f);
CreateRingVisual();
}
/// <summary>
/// 创建红圈视觉:一个平铺在地面上的 Quad,使用 EchoRing shader。
/// shader 通过全局 _EchoCenter / _EchoRadius 自动计算圆环位置,
/// 无需每帧更新 Mesh —— 只需在释放时移动 Quad 到回声中心即可。
/// </summary>
private void CreateRingVisual()
{
_ringVisual = GameObject.CreatePrimitive(PrimitiveType.Quad);
_ringVisual.name = "EchoRing_Visual";
// 移除不需要的碰撞体
var col = _ringVisual.GetComponent<Collider>();
if (col != null) Destroy(col);
// 平铺在地面:绕 X 轴旋转 90°
_ringVisual.transform.rotation = Quaternion.Euler(90f, 0f, 0f);
// 缩放:覆盖 maxRadius * 2 的范围
float size = maxRadius * 2f;
_ringVisual.transform.localScale = new Vector3(size, size, 1f);
// 不挂载到玩家下——回声中心固定,不跟随玩家移动
// 创建材质(运行时)
var shader = Shader.Find("IndianOcean/EchoRing");
if (shader != null)
{
_ringMaterial = new Material(shader);
_ringMaterial.SetColor("_RingColor", ringColor);
_ringMaterial.SetFloat("_RingWidth", ringVisualWidth);
// 确保渲染在黑暗遮罩之上(黑暗遮罩 sortingOrder=9999, renderQueue=3100
_ringMaterial.renderQueue = 3250;
var renderer = _ringVisual.GetComponent<MeshRenderer>();
renderer.sortingOrder = 10001;
renderer.material = _ringMaterial;
}
else
{
Debug.LogError("[EchoSystem] 找不到 IndianOcean/EchoRing shader");
}
_ringVisual.SetActive(false);
}
private void Update()
{
// 触发
if (Input.GetKeyDown(echoKey) && _state == State.Idle && Time.time > _lastEchoTime + cooldown)
// 触发:CD 好即可释放,无需等状态机结束(StartEcho 会重置所有状态)
if (Input.GetKeyDown(echoKey) && Time.time > _lastEchoTime + cooldown)
{
StartEcho();
}
@@ -100,12 +170,19 @@ namespace IndianOceanAssets.Engine2_5D
Shader.SetGlobalFloat(EchoWidthID, ringWidth);
Shader.SetGlobalFloat(EchoOutlineIntensityID, _intensity);
Shader.SetGlobalColor(EchoOutlineColorID, outlineColor);
// 红圈独立渐隐与扩散
UpdateRingAlpha();
UpdateRingRadius();
Shader.SetGlobalFloat(EchoRingAlphaID, _ringAlpha);
Shader.SetGlobalFloat(EchoRingRadiusID, _ringRadius);
}
private void StartEcho()
{
_state = State.Expanding;
_radius = 0f;
_ringRadius = 0f;
_intensity = 1f;
_stateTimer = 0f;
_lastEchoTime = Time.time;
@@ -118,6 +195,13 @@ namespace IndianOceanAssets.Engine2_5D
Vector3 p = transform.position;
_center = new Vector2(p.x, p.z);
// 显示红圈:移动到回声中心位置
if (_ringVisual != null)
{
_ringVisual.transform.position = new Vector3(p.x, ringYOffset, p.z);
_ringVisual.SetActive(true);
}
// 通知订阅者(敌人聆听等)
OnEchoReleased?.Invoke(p);
}
@@ -156,6 +240,42 @@ namespace IndianOceanAssets.Engine2_5D
}
}
/// <summary>
/// 红圈独立渐隐逻辑:
/// 从释放瞬间开始线性渐隐(1 → 0),ringFadeTime 秒后完全消失。
/// </summary>
private void UpdateRingAlpha()
{
if (_state == State.Idle)
{
_ringAlpha = 0f;
return;
}
float elapsed = Time.time - _lastEchoTime;
_ringAlpha = Mathf.Clamp01(1f - elapsed / Mathf.Max(0.001f, ringFadeTime));
// 渐隐完成后隐藏红圈物体(节省渲染开销)
if (_ringAlpha <= 0f && _ringVisual != null)
_ringVisual.SetActive(false);
}
/// <summary>
/// 红圈独立扩散:按 ringExpandSpeed 扩散,封顶 maxRadius。
/// </summary>
private void UpdateRingRadius()
{
if (_state == State.Idle)
{
_ringRadius = 0f;
return;
}
_ringRadius += ringExpandSpeed * Time.deltaTime;
if (_ringRadius > maxRadius)
_ringRadius = maxRadius;
}
/// <summary>
/// 外部调用:强制停止回声
/// </summary>
@@ -163,7 +283,12 @@ namespace IndianOceanAssets.Engine2_5D
{
_state = State.Idle;
_intensity = 0f;
_ringAlpha = 0f;
_ringRadius = 0f;
Shader.SetGlobalFloat(EchoOutlineIntensityID, 0f);
Shader.SetGlobalFloat(EchoRingAlphaID, 0f);
if (_ringVisual != null)
_ringVisual.SetActive(false);
}
public bool IsActive => _state != State.Idle;