
shader
Shader "Custom/Sprite_TreeHit"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
// 参数保持不变
_HitTime ("Hit Time", Float) = -100
_HitAmp ("Hit Amplitude", Float) = 0.5
_HitFreq ("Hit Frequency", Float) = 15
_HitDamp ("Hit Damping", Float) = 3
_FlashColor ("Flash Color", Color) = (1,1,1,1)
_FlashDuration ("Flash Duration", Float) = 0.1
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ PIXELSNAP_ON
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
// 新增:专门传递闪白强度的插值器
// fixed精度在移动端足够(-2~2范围),节省带宽
fixed flashFactor : TEXCOORD1;
};
fixed4 _Color;
float _HitTime;
float _HitAmp;
float _HitFreq;
float _HitDamp;
fixed4 _FlashColor;
float _FlashDuration;
v2f vert(appdata_t IN)
{
v2f OUT;
// --- 1. 顶点形变 (Vertex Deformation) ---
float t = _Time.y - _HitTime;
// 优化:用 step 代替 if。
// 如果 t < 0,isValidTime 为 0,后续计算全归零,无消耗
// 这里的 5.0 是最大晃动时长保护
float isValidTime = step(0, t) * step(t, 5.0);
float decay = exp(-t * _HitDamp);
float wave = decay * sin(t * _HitFreq);
// 乘上 isValidTime,如果时间不对,偏移量直接为0
float bend = wave * _HitAmp * IN.texcoord.y * IN.texcoord.y * isValidTime;
IN.vertex.x += bend;
OUT.vertex = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
// --- 2. 闪白计算移至顶点 (Flash Calculation) ---
// 逻辑:强度 = 1 - (t / duration)
float flashProgress = t / _FlashDuration;
// saturate 自动截断到 0~1,step(0, t) 保证受击前不闪
// 结果:受击瞬间为1,随时间线性降为0
OUT.flashFactor = saturate(1.0 - flashProgress) * step(0, t);
#ifdef PIXELSNAP_ON
OUT.vertex = UnityPixelSnap (OUT.vertex);
#endif
return OUT;
}
sampler2D _MainTex;
fixed4 frag(v2f IN) : SV_Target
{
fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
// 预乘 Alpha 处理 (Premultiplied Alpha Fix)
c.rgb *= c.a;
// --- 3. 极简像素计算 ---
// 不再有 if,不再有时间计算
// 直接把从顶点传过来的 flashFactor 加上去
// 只要 flashFactor 为 0,这对 GPU 来说就是加 0,非常快
c.rgb += _FlashColor.rgb * IN.flashFactor * c.a;
return c;
}
ENDCG
}
}
}
控制脚本
using UnityEngine;
[DisallowMultipleComponent]
public class TreeHitFx : MonoBehaviour
{
static readonly int ID_HitTime = Shader.PropertyToID( "_HitTime" );
static readonly int ID_HitAmp = Shader.PropertyToID( "_HitAmp" );
const float FORCE = 3f;
SpriteRenderer sr;
MaterialPropertyBlock mpb;
private Vector3 CachePos = Vector3.zero;
public void Init( Vector3 Pos, SpriteRenderer icon )
{
CachePos = Pos;
sr = icon;
mpb = new MaterialPropertyBlock( );
// 初始化确保没残留
Clear( );
}
public void Clear( )
{
if ( null != mpb )
{
sr.GetPropertyBlock( mpb );
mpb.SetFloat( ID_HitTime, 0f );
mpb.SetFloat( ID_HitAmp, 0f );
sr.SetPropertyBlock( mpb );
}
}
/// <summary>
/// worldHitPos:砍中点(世界坐标)
/// </summary>
public void PlayHit( Vector2 worldHitPos )
{
sr.GetPropertyBlock( mpb );
mpb.SetFloat( ID_HitTime, Time.time );
mpb.SetFloat( ID_HitAmp, worldHitPos.x - CachePos.x > 0f ? FORCE : -FORCE );
sr.SetPropertyBlock( mpb );
}
}