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相关 就出来了哈

相关推荐
ellis19707 小时前
Unity插件SafeArea Helper适配异形屏详解
unity
nnsix8 小时前
Unity Physics.Raycast的 QueryTriggerInteraction枚举作用
unity·游戏引擎
地狱为王8 小时前
Cesium for Unity叠加行政区划线
unity·gis·cesium
小贺儿开发17 小时前
Unity3D 八大菜系连连看
游戏·unity·互动·传统文化
在路上看风景17 小时前
25. 屏幕像素和纹理像素不匹配
unity
ۓ明哲ڪ18 小时前
Unity功能——创建新脚本时自动添加自定义头注释
unity·游戏引擎
熬夜敲代码的小N18 小时前
Unity大场景卡顿“急救包”:从诊断到落地的全栈优化方案
java·unity·游戏引擎
派葛穆20 小时前
Unity-realvirtual-S7通讯快速配置(未完结)
unity·游戏引擎
w-白兰地1 天前
【Addressable远端加载资源】
unity·addressable·资源加载
小张不爱写代码1 天前
[Unity 技巧] 如何自定义 Inspector 变量显示名称 (CustomLabel)
unity·游戏引擎