Unity 大地图高性能砍树顶点动画Shader


当前方案适合: 类饥荒2d生存类 大量的树动画

前言

在大地图中 成千上万的树 不可能给每一个树都用Spine来做,所以本文章分享了一种通过shader来实现的方案

在一些大的开放世界中会用 静态模型LOD+shader+动态替换被砍的那棵树来做动画 但是今天我们只讲在移动端如何去处理 大量的数

脚本中我用了MaterialPropertyBlock处理 不会打断合批 同时还能修改单个对象的属性

csharp 复制代码
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>
    /// 播放受击动画
    /// </summary>
    /// <param name="worldHitPos">砍中点 (世界坐标)</param>
    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 );
    }

}
c 复制代码
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 flashFactor : TEXCOORD1; 
            };

            fixed4 _Color;
            float _HitTime;
            float _HitAmp;
            float _HitFreq;
            float _HitDamp;
            fixed4 _FlashColor;
            float _FlashDuration;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                
                float t = _Time.y - _HitTime;
                float isValidTime = step(0, t) * step(t, 5.0);
                
                float decay = exp(-t * _HitDamp);
                float wave = decay * sin(t * _HitFreq);
                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;
                float flashProgress = t / _FlashDuration;
                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;
                
                c.rgb *= c.a;
                c.rgb += _FlashColor.rgb * IN.flashFactor * c.a;
                return c;
            }
        ENDCG
        }
    }
}

补充

关于这个树是如何通过定点来实现摇摆动画的, 其实很多文章都有哈, 原理和草是一样的, 鄙人有一点懒就不赘述了 如果想学习原理的 可以查一下类似 草shader相关 就出来了哈

相关推荐
avi91115 小时前
UnityProfiler游戏优化-举一个简单的Editor调试
游戏·unity·游戏引擎·aigc·vibe coding·editor扩展
学嵌入式的小杨同学5 小时前
C 语言实战:动态规划求解最长公共子串(连续),附完整实现与优化
数据结构·c++·算法·unity·游戏引擎·代理模式
学嵌入式的小杨同学8 小时前
顺序表(SqList)完整解析与实现(数据结构专栏版)
c++·算法·unity·游戏引擎·代理模式
程序猿多布8 小时前
HybridCLR热更打包后AOT泛型函数实例化缺失处理
unity·hybridclr·aot generic
平行云10 小时前
实时云渲染支持数字孪生智能工厂:迈向“零原型”制造
人工智能·unity·ue5·云计算·webrtc·制造·实时云渲染
dzj202110 小时前
Unity中使用LLMUnity遇到的问题(一)
unity·llm·llmunity
Howrun77710 小时前
虚幻引擎 C++ 制作“射击FPS游戏“
游戏·游戏引擎·虚幻
DowneyJoy10 小时前
【Unity通用工具类】列表扩展方法ListExtensions
unity·c#·交互
极客柒10 小时前
Unity 大地图 高性能路径引导Shader
unity·游戏引擎