From 5771d9157edbcc958348cd83755af6c354a0d320 Mon Sep 17 00:00:00 2001
From: ksh999000 <740612340@qq.com>
Date: Mon, 6 Jul 2026 13:43:09 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=9B=9E=E5=A3=B0=E6=89=A9?=
=?UTF-8?q?=E6=95=A3=E7=89=B9=E6=95=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
unity/Assets/2.5D Engine/Player.prefab | 5 +
unity/Assets/Light/shaders/EchoRing.shader | 92 +++++++++++++
.../Assets/Light/shaders/EchoRing.shader.meta | 9 ++
.../Scenes/res/player/Player_new.prefab | 10 +-
unity/Assets/eco/EchoSystem.cs | 129 +++++++++++++++++-
5 files changed, 241 insertions(+), 4 deletions(-)
create mode 100644 unity/Assets/Light/shaders/EchoRing.shader
create mode 100644 unity/Assets/Light/shaders/EchoRing.shader.meta
diff --git a/unity/Assets/2.5D Engine/Player.prefab b/unity/Assets/2.5D Engine/Player.prefab
index 4525b96..c3603a6 100644
--- a/unity/Assets/2.5D Engine/Player.prefab
+++ b/unity/Assets/2.5D Engine/Player.prefab
@@ -360,6 +360,7 @@ MonoBehaviour:
isPlayer: 1
dissolveMaterial: {fileID: 0}
fadeDuration: 2
+ damageInvincibleDuration: 2
--- !u!114 &4071397948156435190
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -436,6 +437,10 @@ MonoBehaviour:
sustainTime: 4
fadeTime: 1.5
outlineColor: {r: 1, g: 1, b: 1, a: 1}
+ ringColor: {r: 1, g: 0.15, b: 0.1, a: 1}
+ ringVisualWidth: 2.5
+ ringYOffset: 0.1
+ ringFadeTime: 3
cooldown: 3
--- !u!1001 &1156712605891213377
PrefabInstance:
diff --git a/unity/Assets/Light/shaders/EchoRing.shader b/unity/Assets/Light/shaders/EchoRing.shader
new file mode 100644
index 0000000..e05696a
--- /dev/null
+++ b/unity/Assets/Light/shaders/EchoRing.shader
@@ -0,0 +1,92 @@
+Shader "IndianOcean/EchoRing"
+{
+ // 回声红圈 Shader —— 配合 EchoSystem 使用
+ // 在地面(XZ平面)上绘制一个红色渐变圆环,圆环位置跟随全局 _EchoRadius
+ // 边缘使用 smoothstep 渐变,从圆环中心满透明度向两侧柔和消散
+ Properties
+ {
+ _RingColor ("Ring Color", Color) = (1, 0.15, 0.1, 1)
+ _RingWidth ("Ring Width (world units)", Float) = 2.5
+ }
+ SubShader
+ {
+ Tags
+ {
+ "RenderType" = "Transparent"
+ "Queue" = "Transparent+250"
+ "RenderPipeline" = "UniversalPipeline"
+ }
+ Blend SrcAlpha OneMinusSrcAlpha
+ ZWrite Off
+ ZTest Always
+ Cull Off
+
+ Pass
+ {
+ Name "EchoRing"
+ HLSLPROGRAM
+ #pragma vertex vert
+ #pragma fragment frag
+ #pragma multi_compile_instancing
+
+ #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
+
+ struct Attributes
+ {
+ float4 positionOS : POSITION;
+ UNITY_VERTEX_INPUT_INSTANCE_ID
+ };
+
+ struct Varyings
+ {
+ float4 positionCS : SV_POSITION;
+ float3 worldPos : TEXCOORD0;
+ UNITY_VERTEX_INPUT_INSTANCE_ID
+ };
+
+ CBUFFER_START(UnityPerMaterial)
+ float4 _RingColor;
+ float _RingWidth;
+ CBUFFER_END
+
+ // 全局回声参数(由 EchoSystem 通过 Shader.SetGlobalX 设置)
+ float4 _EchoCenter; // xy = 回声中心世界坐标 XZ
+ float _EchoRingRadius; // 红圈独立扩散半径
+ float _EchoRingAlpha; // 红圈独立透明度(独立于描边强度)
+
+ Varyings vert(Attributes input)
+ {
+ Varyings output;
+ UNITY_SETUP_INSTANCE_ID(input);
+ UNITY_TRANSFER_INSTANCE_ID(input, output);
+
+ output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
+ output.worldPos = TransformObjectToWorld(input.positionOS.xyz);
+ return output;
+ }
+
+ half4 frag(Varyings input) : SV_Target
+ {
+ // 世界空间 XZ 平面上的距离
+ float2 wp = input.worldPos.xz;
+ float dist = distance(wp, _EchoCenter.xy);
+
+ // 距离圆环中心的偏差
+ float distFromRing = abs(dist - _EchoRingRadius);
+
+ // 渐变:圆环中心满强度,两侧柔和消散
+ float halfW = max(0.001, _RingWidth * 0.5);
+ float alpha = 1.0 - smoothstep(0.0, halfW, distFromRing);
+
+ // 乘以红圈独立透明度(独立于描边消散时序)
+ alpha *= _EchoRingAlpha;
+
+ if (alpha < 0.001) discard;
+
+ return half4(_RingColor.rgb, alpha * _RingColor.a);
+ }
+ ENDHLSL
+ }
+ }
+ FallBack Off
+}
diff --git a/unity/Assets/Light/shaders/EchoRing.shader.meta b/unity/Assets/Light/shaders/EchoRing.shader.meta
new file mode 100644
index 0000000..29c0ad6
--- /dev/null
+++ b/unity/Assets/Light/shaders/EchoRing.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: CX1JsiL7AnkPMkp0FGGR15PogOhTjb8ArRIm08D4oVWvMTimFo71lmA=
+ShaderImporter:
+ externalObjects: {}
+ defaultTextures: []
+ nonModifiableTextures: []
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/unity/Assets/Scenes/res/player/Player_new.prefab b/unity/Assets/Scenes/res/player/Player_new.prefab
index ae6b0fc..82ec072 100644
--- a/unity/Assets/Scenes/res/player/Player_new.prefab
+++ b/unity/Assets/Scenes/res/player/Player_new.prefab
@@ -273,6 +273,7 @@ MonoBehaviour:
isPlayer: 1
dissolveMaterial: {fileID: 0}
fadeDuration: 2
+ damageInvincibleDuration: 2
--- !u!114 &4737232457870655947
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -346,10 +347,15 @@ MonoBehaviour:
expandSpeed: 15
maxRadius: 30
ringWidth: 2.5
- sustainTime: 4
+ sustainTime: 2.5
fadeTime: 1.5
outlineColor: {r: 1, g: 1, b: 1, a: 1}
- cooldown: 3
+ ringColor: {r: 0.9245283, g: 0.2703808, b: 0.43898734, a: 0.654902}
+ ringVisualWidth: 0.8
+ ringYOffset: 0.1
+ ringFadeTime: 2
+ ringExpandSpeed: 18
+ cooldown: 10
--- !u!114 &4208162987965796937
MonoBehaviour:
m_ObjectHideFlags: 0
diff --git a/unity/Assets/eco/EchoSystem.cs b/unity/Assets/eco/EchoSystem.cs
index fd1917a..4ac50bf 100644
--- a/unity/Assets/eco/EchoSystem.cs
+++ b/unity/Assets/eco/EchoSystem.cs
@@ -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();
+ }
+
+ ///
+ /// 创建红圈视觉:一个平铺在地面上的 Quad,使用 EchoRing shader。
+ /// shader 通过全局 _EchoCenter / _EchoRadius 自动计算圆环位置,
+ /// 无需每帧更新 Mesh —— 只需在释放时移动 Quad 到回声中心即可。
+ ///
+ private void CreateRingVisual()
+ {
+ _ringVisual = GameObject.CreatePrimitive(PrimitiveType.Quad);
+ _ringVisual.name = "EchoRing_Visual";
+
+ // 移除不需要的碰撞体
+ var col = _ringVisual.GetComponent();
+ 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();
+ 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
}
}
+ ///
+ /// 红圈独立渐隐逻辑:
+ /// 从释放瞬间开始线性渐隐(1 → 0),ringFadeTime 秒后完全消失。
+ ///
+ 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);
+ }
+
+ ///
+ /// 红圈独立扩散:按 ringExpandSpeed 扩散,封顶 maxRadius。
+ ///
+ private void UpdateRingRadius()
+ {
+ if (_state == State.Idle)
+ {
+ _ringRadius = 0f;
+ return;
+ }
+
+ _ringRadius += ringExpandSpeed * Time.deltaTime;
+ if (_ringRadius > maxRadius)
+ _ringRadius = maxRadius;
+ }
+
///
/// 外部调用:强制停止回声
///
@@ -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;