新增回声扩散特效
This commit is contained in:
@@ -360,6 +360,7 @@ MonoBehaviour:
|
|||||||
isPlayer: 1
|
isPlayer: 1
|
||||||
dissolveMaterial: {fileID: 0}
|
dissolveMaterial: {fileID: 0}
|
||||||
fadeDuration: 2
|
fadeDuration: 2
|
||||||
|
damageInvincibleDuration: 2
|
||||||
--- !u!114 &4071397948156435190
|
--- !u!114 &4071397948156435190
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -436,6 +437,10 @@ MonoBehaviour:
|
|||||||
sustainTime: 4
|
sustainTime: 4
|
||||||
fadeTime: 1.5
|
fadeTime: 1.5
|
||||||
outlineColor: {r: 1, g: 1, b: 1, a: 1}
|
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
|
cooldown: 3
|
||||||
--- !u!1001 &1156712605891213377
|
--- !u!1001 &1156712605891213377
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: CX1JsiL7AnkPMkp0FGGR15PogOhTjb8ArRIm08D4oVWvMTimFo71lmA=
|
||||||
|
ShaderImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
defaultTextures: []
|
||||||
|
nonModifiableTextures: []
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -273,6 +273,7 @@ MonoBehaviour:
|
|||||||
isPlayer: 1
|
isPlayer: 1
|
||||||
dissolveMaterial: {fileID: 0}
|
dissolveMaterial: {fileID: 0}
|
||||||
fadeDuration: 2
|
fadeDuration: 2
|
||||||
|
damageInvincibleDuration: 2
|
||||||
--- !u!114 &4737232457870655947
|
--- !u!114 &4737232457870655947
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -346,10 +347,15 @@ MonoBehaviour:
|
|||||||
expandSpeed: 15
|
expandSpeed: 15
|
||||||
maxRadius: 30
|
maxRadius: 30
|
||||||
ringWidth: 2.5
|
ringWidth: 2.5
|
||||||
sustainTime: 4
|
sustainTime: 2.5
|
||||||
fadeTime: 1.5
|
fadeTime: 1.5
|
||||||
outlineColor: {r: 1, g: 1, b: 1, a: 1}
|
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
|
--- !u!114 &4208162987965796937
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -50,16 +50,38 @@ namespace IndianOceanAssets.Engine2_5D
|
|||||||
[Tooltip("描边颜色(默认白色,可自定义)")]
|
[Tooltip("描边颜色(默认白色,可自定义)")]
|
||||||
[SerializeField] private Color outlineColor = Color.white;
|
[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("冷却")]
|
[Header("冷却")]
|
||||||
[Tooltip("回声冷却时间(秒)")]
|
[Tooltip("回声冷却时间(秒)")]
|
||||||
[SerializeField] private float cooldown = 3f;
|
[SerializeField] private float cooldown = 3f;
|
||||||
|
|
||||||
|
// 红圈视觉
|
||||||
|
private GameObject _ringVisual;
|
||||||
|
private Material _ringMaterial;
|
||||||
|
|
||||||
// 全局 shader 属性 ID(通过 Shader.SetGlobalX 设置,所有描边精灵共享)
|
// 全局 shader 属性 ID(通过 Shader.SetGlobalX 设置,所有描边精灵共享)
|
||||||
private static readonly int EchoCenterID = Shader.PropertyToID("_EchoCenter");
|
private static readonly int EchoCenterID = Shader.PropertyToID("_EchoCenter");
|
||||||
private static readonly int EchoRadiusID = Shader.PropertyToID("_EchoRadius");
|
private static readonly int EchoRadiusID = Shader.PropertyToID("_EchoRadius");
|
||||||
private static readonly int EchoWidthID = Shader.PropertyToID("_EchoWidth");
|
private static readonly int EchoWidthID = Shader.PropertyToID("_EchoWidth");
|
||||||
private static readonly int EchoOutlineIntensityID = Shader.PropertyToID("_EchoOutlineIntensity");
|
private static readonly int EchoOutlineIntensityID = Shader.PropertyToID("_EchoOutlineIntensity");
|
||||||
private static readonly int EchoOutlineColorID = Shader.PropertyToID("_EchoOutlineColor");
|
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 }
|
private enum State { Idle, Expanding, Sustaining, Fading }
|
||||||
|
|
||||||
@@ -69,6 +91,8 @@ namespace IndianOceanAssets.Engine2_5D
|
|||||||
private float _stateTimer;
|
private float _stateTimer;
|
||||||
private Vector2 _center;
|
private Vector2 _center;
|
||||||
private float _lastEchoTime = -999f;
|
private float _lastEchoTime = -999f;
|
||||||
|
private float _ringAlpha = 0f;
|
||||||
|
private float _ringRadius = 0f;
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
@@ -76,12 +100,58 @@ namespace IndianOceanAssets.Engine2_5D
|
|||||||
Shader.SetGlobalFloat(EchoOutlineIntensityID, 0f);
|
Shader.SetGlobalFloat(EchoOutlineIntensityID, 0f);
|
||||||
Shader.SetGlobalColor(EchoOutlineColorID, outlineColor);
|
Shader.SetGlobalColor(EchoOutlineColorID, outlineColor);
|
||||||
Shader.SetGlobalFloat(EchoWidthID, ringWidth);
|
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()
|
private void Update()
|
||||||
{
|
{
|
||||||
// 触发
|
// 触发:CD 好即可释放,无需等状态机结束(StartEcho 会重置所有状态)
|
||||||
if (Input.GetKeyDown(echoKey) && _state == State.Idle && Time.time > _lastEchoTime + cooldown)
|
if (Input.GetKeyDown(echoKey) && Time.time > _lastEchoTime + cooldown)
|
||||||
{
|
{
|
||||||
StartEcho();
|
StartEcho();
|
||||||
}
|
}
|
||||||
@@ -100,12 +170,19 @@ namespace IndianOceanAssets.Engine2_5D
|
|||||||
Shader.SetGlobalFloat(EchoWidthID, ringWidth);
|
Shader.SetGlobalFloat(EchoWidthID, ringWidth);
|
||||||
Shader.SetGlobalFloat(EchoOutlineIntensityID, _intensity);
|
Shader.SetGlobalFloat(EchoOutlineIntensityID, _intensity);
|
||||||
Shader.SetGlobalColor(EchoOutlineColorID, outlineColor);
|
Shader.SetGlobalColor(EchoOutlineColorID, outlineColor);
|
||||||
|
|
||||||
|
// 红圈独立渐隐与扩散
|
||||||
|
UpdateRingAlpha();
|
||||||
|
UpdateRingRadius();
|
||||||
|
Shader.SetGlobalFloat(EchoRingAlphaID, _ringAlpha);
|
||||||
|
Shader.SetGlobalFloat(EchoRingRadiusID, _ringRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartEcho()
|
private void StartEcho()
|
||||||
{
|
{
|
||||||
_state = State.Expanding;
|
_state = State.Expanding;
|
||||||
_radius = 0f;
|
_radius = 0f;
|
||||||
|
_ringRadius = 0f;
|
||||||
_intensity = 1f;
|
_intensity = 1f;
|
||||||
_stateTimer = 0f;
|
_stateTimer = 0f;
|
||||||
_lastEchoTime = Time.time;
|
_lastEchoTime = Time.time;
|
||||||
@@ -118,6 +195,13 @@ namespace IndianOceanAssets.Engine2_5D
|
|||||||
Vector3 p = transform.position;
|
Vector3 p = transform.position;
|
||||||
_center = new Vector2(p.x, p.z);
|
_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);
|
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>
|
||||||
/// 外部调用:强制停止回声
|
/// 外部调用:强制停止回声
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -163,7 +283,12 @@ namespace IndianOceanAssets.Engine2_5D
|
|||||||
{
|
{
|
||||||
_state = State.Idle;
|
_state = State.Idle;
|
||||||
_intensity = 0f;
|
_intensity = 0f;
|
||||||
|
_ringAlpha = 0f;
|
||||||
|
_ringRadius = 0f;
|
||||||
Shader.SetGlobalFloat(EchoOutlineIntensityID, 0f);
|
Shader.SetGlobalFloat(EchoOutlineIntensityID, 0f);
|
||||||
|
Shader.SetGlobalFloat(EchoRingAlphaID, 0f);
|
||||||
|
if (_ringVisual != null)
|
||||||
|
_ringVisual.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsActive => _state != State.Idle;
|
public bool IsActive => _state != State.Idle;
|
||||||
|
|||||||
Reference in New Issue
Block a user