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

相关推荐
辻弋2013 天前
五年前的旅行视频糊成马赛克?Video2X用Real-ESRGAN逐帧重建细节,但只支持Windows、集显用户建议直接放弃
服务器·数据库·windows·游戏引擎·电脑
Martina_03214 天前
AI生成3D场景导入 Unity/Unreal 后,NPC 总串层?用6步检查导航高度与跨层连接
人工智能·游戏·数学建模·3d·unity·自然语言处理·aigc
小贺儿开发4 天前
Unity 局域网遥控幻灯片展示工具 3.0
科技·http·unity·工具·ppt·通信·网页
WarPigs6 天前
Unity对象不应该使用null传播的问题
unity
每日出拳老爷子7 天前
【VR】【Unity】抓走转放系列21|一天搭完门和抽屉工具槽:小场景按这个次序拼装
unity·vr·驱动·排错·snap zone
淡海水7 天前
02-02-原理篇-分代GC
算法·unity·c#·游戏引擎·.net·gc
小贺儿开发8 天前
Unity 文物新生 会讲故事的文物展墙
人工智能·科技·unity·ai·视频·互动·演示
淡海水8 天前
02-03-原理篇-大对象堆
unity·c#·游戏引擎·.net·gc
WarPigs9 天前
Unity在Create菜单添加创建自定义脚本模板选项
unity
郝学胜-神的一滴9 天前
C++ Templates 01:拨开迷雾,读懂C++模板经典著作
开发语言·c++·程序人生·游戏·游戏引擎·软件构建