顶点动画:砍树shader


GIF 2026-1-4 11-28-33.gif


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 );
    }

}



顶点动画: 外发光 案例:掉落物品

Unity 2d Sprite血条shader

评 论