IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> unity 序列帧动画 UGUI GPU版 -> 正文阅读

[游戏开发]unity 序列帧动画 UGUI GPU版

? ? ?之前的项目因序列帧数量量大,图片大,耗费不少的资源,想搞一版GPU版本的序列帧试试水,看看性能,初见成效,但是弊端也蛮明显,就是不能合并drawcall,不过还能凑合着用

? ? 流程大概就是把图片资源全部压进GPU,用Material传递索引数据_Index,shader根据不同的索引来显示不同的图片,这样就大大的解放了CPU端的计算,也算是一种性能的提升吧。不过缺点也挺明显,就是图片集,每个图片必须是2的幂次方,且长宽必须相等,这点来说,对于没那么严格的UI像素,想必问题不大。先上实例看下效果

?再上代码


using System;
using System.Collections;
using System.Collections.Generic;

using UnityEngine;
using UnityEngine.UI;

using UnityEngine.Events;
using UnityEngine.EventSystems;


//规范命名、添加注释、合理封装、限制访问权限、异常处理    
public class SequenceFrame : MonoBehaviour, IPointerClickHandler
{

  

    public enum State
    {
        idle,
        playing,
        pause
    }
    public enum State1
    {
        once,
        loop
    }
    //播放状态(默认、播放中、暂停)
    private State play_state;
    private RawImage _showImage;
    private bool _isSelect;
    private int index;
    private float tim;
    private float waittim;
    private bool isplay;
    private int _selectMax;
    private int _hightMax;
    private Material _curMaterial;
    private int _curMax;


    
    public float FrameNumber = 30;
 
    public State1 condition = State1.once;

  
    public bool Play_Awake = false;
    /// <summary>
    /// 点击后是否产生渐变效果
    /// </summary>
    public bool ISGradualChange = true;
    //回调事件
    public UnityEvent onCompleteEvent;
    public List<Texture2D> SelectTexture2Ds;
    public List<Texture2D> HighLighTexture2Ds;
    public Shader Shader;
    public event Action<SequenceFrame> ClickEvent;
    void Awake()
    {

      
        tim = 0;
        index = 0;
        waittim = 1 / FrameNumber;
        play_state = State.idle;
        isplay = false;
       
        if (SelectTexture2Ds.Count < 1)
        {
            Debug.LogError("SelectTexture2Ds数组为0,请给SelectTexture2Ds数组添加元素!!!");
        }
      
        if (Play_Awake)
        {
            Play();
        }


        _showImage = this.GetComponent<RawImage>();
        if (_showImage == null) throw new UnityException("没有找到ugui组件");


        _curMaterial = new Material(Shader);

        InitData();

        SetMat(_selectMax);
        _curMaterial.SetInt("_Index", 0);
        _curMaterial.SetFloat("_Convert",0);

       
    }

    private void SetMat(int depth)
    {
        _curMax =depth;
    }
    private void InitData()
    {
        var texs1 = GlobalSetting.LoadImage("_TexArr", SelectTexture2Ds);
        _curMaterial.SetTexture("_TexArr", texs1);
        _selectMax = texs1.depth;


        var texs2 = GlobalSetting.LoadImage("_HightArr", HighLighTexture2Ds);
        _curMaterial.SetTexture("_HightArr", texs2);
        _hightMax = texs2.depth;

        _showImage.material = _curMaterial;

    }
    void Update()
    {
        //测试
        if (Input.GetKeyDown(KeyCode.A))
        {
            Play();
        }
        if (Input.GetKeyDown(KeyCode.S))
        {
            Replay();
        }
        if (Input.GetKeyDown(KeyCode.D))
        {
            Stop();
        }
        if (Input.GetKeyDown(KeyCode.P))
        {
            Pause();
        }
        UpMove();

        
            _farmeCount++;
            if (_farmeCount >= 20)
            {
                _curMaterial.SetFloat("_Convert", 0);
            }
        

    }

    private void OnDestroy()
    {
        Debug.Log("Destroy textureArray");
        Destroy(_curMaterial);
        //删除图集,如果其他ui也用到,会导致其他UI的图片消失
        GlobalSetting.Dispose();
    }

    private void UpMove()
    {
        //单播
        if (condition == State1.once)
        {
            if (play_state == State.idle && isplay)
            {
                play_state = State.playing;
                index = 0;
                tim = 0;
            }
            if (play_state == State.pause && isplay)
            {
                play_state = State.playing;
                tim = 0;
            }
            if (play_state == State.playing && isplay)
            {
                tim += Time.deltaTime;
                if (tim >= waittim)
                {
                    tim = 0;
                    index++;
                    if (index >= _curMax)
                    {
                        index = 0;
                        //ShowImage.sprite = _curSelectList[index];
                        _curMaterial.SetInt("_Index", index);
                        isplay = false;
                        play_state = State.idle;
                        //此处可添加结束回调函数
                        if (onCompleteEvent != null)
                        {
                            onCompleteEvent.Invoke();
                            return;
                        }
                    }
                    // ShowImage.sprite = _curSelectList[index];
                    _curMaterial.SetInt("_Index", index);
                }
            }
        }
        //循环播放
        if (condition == State1.loop)
        {
            if (play_state == State.idle && isplay)
            {
                play_state = State.playing;
                index = 0;
                tim = 0;
            }
            if (play_state == State.pause && isplay)
            {
                play_state = State.playing;
                tim = 0;
            }
            if (play_state == State.playing && isplay)
            {
                tim += Time.deltaTime;
                if (tim >= waittim)
                {
                    tim = 0;
                    index++;
                    if (index >= _curMax)
                    {
                        index = 0;
                        //此处可添加结束回调函数
                    }
                    _curMaterial.SetInt("_Index", index);
                }
            }
        }
    }
    /// <summary>
    /// 播放
    /// </summary>
    public void Play()
    {
        isplay = true;
    }
    /// <summary>
    /// 暂停
    /// </summary>
    public void Pause()
    {
        isplay = false;
        play_state = State.pause;
    }
    /// <summary>
    /// 停止
    /// </summary>
    public void Stop()
    {
        isplay = false;
        play_state = State.idle;
        index = 0;
        tim = 0;
        if (_curMaterial == null)
        {
            Debug.LogWarning("Image为空,请赋值");
            return;
        }
        _curMaterial.SetInt("_Index", 0);
    }
    /// <summary>
    /// 重播
    /// </summary>
    public void Replay()
    {
        isplay = true;
        play_state = State.playing;
        index = 0;
        tim = 0;
    }

    /// <summary>
    /// 点击后改变图片效果
    /// </summary>
    /// <param name="isShow"></param>
    public void ChangeSperite(bool isShow)
    {
        if (ISGradualChange) return;

        if (isShow)
        {
            if ( _hightMax> 0)
            {
                SetMat(_hightMax);
               
            }
            _isSelect = true;
        }
        else
        {
         
            SetMat( _selectMax);
            _isSelect = false;

        }
    }


    private int _farmeCount = 0;
    private void ClickEffect()
    {


        if (_hightMax > 0)
        {
            _curMaterial.SetFloat("_Convert", 1);
        }
        _farmeCount = 0;


    }

    /// <summary>
    /// 是否过了点击的间隔时间,防止密密麻麻点击造成的崩溃
    /// </summary>
    private bool _isInterval = true;

    private Coroutine _coroutineInterval;


    private void OnEnable()
    {
        _isInterval = true;
    }
    /// <summary>
    /// 间隔点允许下一次点击的时间
    /// </summary>
    public float IntervalTime = 0.5f;
    public void OnPointerClick(PointerEventData eventData)
    {


        if (_isInterval)
        {
            _isInterval = false;
            _coroutineInterval = StartCoroutine(GlobalSetting.WaitTime(IntervalTime, (() =>
            {
                _isInterval = true;
                Debug.LogError("恢复点击");

            })));
        }
        else
        {
            Debug.LogError("不允许点击");
            
            return ;
        }
       

        if (ISGradualChange)
        {
            ClickEffect();
        }
        else
        {
            GradualChange();
        }



        if (ClickEvent != null)
            ClickEvent(this);

    }

    private void OnDisable()
    {
        SetMat( _selectMax);
    }
    private void GradualChange()
    {
        //ShowImage.DOColor(new Color(168 / 255f, 183 / 255f, 255f / 255f, 0.35f), 0.25f).OnComplete((() =>
        //{
        //    ShowImage.DOColor(new Color(255f / 255f, 255f / 255f, 255f / 255f, 1f), 0.25f).SetDelay(0.15f);
        //}));


    }
}

再来一个工具类

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Rendering;
using Object = UnityEngine.Object;

public static class GlobalSetting
{

    /// <summary>
    /// Texture2DArray 限制,长跟宽必须一致,并且符合2的幂次方
    /// </summary>
    public static int Size = 512;

    public static Dictionary<string,Texture2DArray> Texture2DArrayDic=new Dictionary<string, Texture2DArray>();
    public static Texture2DArray LoadImage(string key,List<Texture2D> texs)
    {
        if (Texture2DArrayDic.ContainsKey(key))
        {
            return Texture2DArrayDic[key];
        }

        Texture2DArray texture2DArray = null;
        if (texs.Count == 0)
        {
            string[] files = Directory.GetFiles(Application.streamingAssetsPath + "/Pictures");

            texs = new List<Texture2D>();
            foreach (string file in files)
            {
                if (file.Contains(".meta")) continue;

              

                byte[] bytes = File.ReadAllBytes(file);

                Texture2D tex = new Texture2D(1024, 1024);

                tex.LoadImage(bytes);

                tex.Apply();
               
                tex = Resize(tex, Size, Size);

                texs.Add(tex);


            }
            texture2DArray = SetTexToGpu(texs.ToArray());

           
        }
        else
        {
            List<Texture2D> temps = new List<Texture2D>();
            foreach (Texture2D texture2D in texs)
            {
                var tex = Resize(texture2D, Size, Size);
                temps.Add(tex);
            }
            texture2DArray = SetTexToGpu(temps.ToArray());

        
        }

        Texture2DArrayDic.Add(key, texture2DArray);
        return texture2DArray;
    }
    /// <summary>
    /// 缩略图片
    /// </summary>
    /// <param name="source"></param>
    /// <param name="newWidth"></param>
    /// <param name="newHeight"></param>
    /// <returns></returns>
    public static Texture2D Resize(Texture2D source, int newWidth, int newHeight)
    {
        source.filterMode = FilterMode.Point;
        RenderTexture rt = RenderTexture.GetTemporary(newWidth, newHeight);
        rt.filterMode = FilterMode.Point;
        RenderTexture.active = rt;
        Graphics.Blit(source, rt);
        var nTex = new Texture2D(newWidth, newHeight);
        nTex.ReadPixels(new Rect(0, 0, newWidth, newHeight), 0, 0);
        nTex.Apply();
        RenderTexture.active = null;
        Object.Destroy(rt);//及时删掉
        return nTex;
    }

    public static Texture2DArray SetTexToGpu(Texture2D[] texs)
    {
        if (texs == null || texs.Length == 0)
        {
            
            return null;
        }

        if (SystemInfo.copyTextureSupport == CopyTextureSupport.None ||
            !SystemInfo.supports2DArrayTextures)
        {
          
            return null;
        }

        Texture2DArray texArr = new Texture2DArray(texs[0].width, texs[0].width, texs.Length, texs[0].format, false, false);

        for (int i = 0; i < texs.Length; i++)
        {
            //拷贝的贴图必须符合2的幂次方
            Graphics.CopyTexture(texs[i], 0, 0, texArr, i, 0);
        }

        texArr.wrapMode = TextureWrapMode.Clamp;
        texArr.filterMode = FilterMode.Trilinear;
      for (int i = 0; i < texs.Length; i++)
      {
          Object.Destroy(texs[i]);
      }
      Resources.UnloadUnusedAssets();

      return texArr;
    }

    public static IEnumerator WaitTime(float time, Action action)
    {
        yield return new WaitForSeconds(time);
        if (action != null) action();
    }

    
    //慎重删除
    public static void Dispose()
    {
        foreach (KeyValuePair<string, Texture2DArray> pair in Texture2DArrayDic)
        {
            var temp = pair.Value;
            Object.Destroy(temp);
        }
        Texture2DArrayDic.Clear();


    }
}

其次就是Shader了

Shader "Unlit/SequenceFrame"
{
   Properties
	{
		
		 _MainTex("MainTex",2D)="white" {}  
		 _Speed("Speed",float)=0
	}

	SubShader
	{
		Tags{"LightMode" = "ForwardBase"  "IgnoreProjector" = "True" "RenderType" = "Transparent"  }
		LOD 100

		Pass
		{

		   //  关闭深度写入
           ZWrite Off
           //  开启混合模式,并设置混合因子为SrcAlpha和OneMinusSrcAlpha
           Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			  
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			#pragma multi_compile_instancing
		

			UNITY_DECLARE_TEX2DARRAY(_TexArr);
			UNITY_DECLARE_TEX2DARRAY(_HightArr);
			float _Convert;
			sampler2D _MainTex;
			float _Speed;
			struct appdata
			{
				float4 vertex : POSITION;
				float3 uv : TEXCOORD0;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float3 uv : TEXCOORD0;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};
			UNITY_INSTANCING_BUFFER_START(Props)
                UNITY_DEFINE_INSTANCED_PROP(int,_Index)
            UNITY_INSTANCING_BUFFER_END(Props)
			v2f vert (appdata v)
			{
				v2f o;
				UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o);
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
			   UNITY_SETUP_INSTANCE_ID(i);


			   
			   fixed4 texColor = UNITY_SAMPLE_TEX2DARRAY(_TexArr, float3(i.uv.xy, UNITY_ACCESS_INSTANCED_PROP(Props, _Index)));
			   fixed4 texColorHigh = UNITY_SAMPLE_TEX2DARRAY(_HightArr, float3(i.uv.xy, UNITY_ACCESS_INSTANCED_PROP(Props, _Index)));

			   fixed4 endCol = lerp(texColor,texColorHigh,_Convert);
			   return endCol;
				
			}
			ENDCG
		}
	}

	Fallback "VertexLit"

}

布局图

?

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-12-01 18:01:41  更:2021-12-01 18:02:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 23:56:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码