之前的项目因序列帧数量量大,图片大,耗费不少的资源,想搞一版GPU版本的序列帧试试水,看看性能,初见成效,但是弊端也蛮明显,就是不能合并drawcall,显存这块,因为全部一股脑压进GPU,会导致显存相对较高,这也是相对于序列帧图片较大的情况。序列帧图片较小的话影响就比较小了。不过还能凑合着用,显然,需要优化和加深的地方还有很多,这里只是给个基础流程,发现搞好一套功能真的不是一件易事,只能是针不同的项目进行优化开发。

流程大概就是把图片资源全部压进GPU,用Material传递索引数据_Index,shader根据不同的索引来显示不同的图片,这样就大大的解放了CPU端的计算,也算是一种性能的提升吧。不过缺点也挺明显,就是图片集,每个图片必须是2的幂次方,且长宽必须相等,这点来说,对于没那么严格的UI像素,想必问题不大。而且对于源图片文件集,不要求长宽必须一致,也不要求符合2的幂次方。这一步已经在代码里帮你设置好了,需要需要修改尺寸,改源码即可。先上实例看下效果

再上代码


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;//正常的序列帧private PictureInfo pictureInfo;/// <summary>/// 高亮的序列帧/// </summary>private PictureInfo PictureHighLighInfo;/// <summary>/// 序列帧的名字/// </summary>public string TexName;/// <summary>/// 高亮序列帧的名字/// </summary>public string HighTexName;public float FrameNumber = 30;public State1 condition = State1.once;public bool Play_Awake = false;/// <summary>/// 点击后是否产生渐变效果/// </summary>public bool ISGradualChange = true;//回调事件public UnityEvent onCompleteEvent;public Shader Shader;public event Action<SequenceFrame> ClickEvent;void Start(){tim = 0;index = 0;waittim = 1 / FrameNumber;play_state = State.idle;isplay = false;_showImage = this.GetComponent<RawImage>();if (_showImage == null) throw new UnityException("没有找到ugui组件");InitData();SetMat(_selectMax);_curMaterial.SetInt("_Index", 0);_curMaterial.SetFloat("_Convert",0);if (Play_Awake){Play();}}private void SetMat(int depth){_curMax =depth;}private void InitData(){_curMaterial = new Material(Shader);pictureInfo = PictureManager.Instance.GeTPictureInfo(TexName);_curMaterial.SetTexture("_TexArr", pictureInfo.Texture2DArray);_selectMax = pictureInfo.Texture2DArray.depth;if (HighTexName != null){PictureHighLighInfo = PictureManager.Instance.GeTPictureInfo(HighTexName);if(PictureHighLighInfo==null)Debug.LogWarning("没有加载高亮序列帧");else{_curMaterial.SetTexture("_HightArr", PictureHighLighInfo.Texture2DArray);_hightMax = PictureHighLighInfo.Texture2DArray.depth;}}_showImage.material = _curMaterial;//自动匹配序列帧的大小_showImage.rectTransform.sizeDelta = pictureInfo.Size;}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);}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 (PictureHighLighInfo != null){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;/// <summary>/// 存放序列帧文件夹的路径,不需要的序列帧文件及时删掉,否则占用内存/// </summary>public static string SequenceFramePath =Application.streamingAssetsPath+ "/SequenceFrame";/// <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,TextureFormat.RGBA32,false);//TextureFormat.RGBA32格式,经测试,此格式耗费显存相对较低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++){//拷贝的贴图必须长宽一致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();}//得到最接近 into 且大于into的二次方数public static int Get2PowHigh(int into){--into;//避免正好输入一个2的次方数into |= into >> 1;into |= into >> 2;into |= into >> 4;into |= into >> 8;into |= into >> 16;return ++into;}//得到最接近 into 且小于into的二次方数public static int Get2PowLow(int into){return Get2PowHigh(into) >> 1;}}

其次就是Shader了

Shader "Unlit/SequenceFrame"
{Properties{_MainTex("MainTex",2D)="white" {}  _Speed("Speed",float)=0}SubShader{Tags{"LightMode" = "ForwardBase"  "IgnoreProjector" = "True" "RenderType" = "Transparent"  }LOD 100Pass{//  关闭深度写入ZWrite Off//  开启混合模式,并设置混合因子为SrcAlpha和OneMinusSrcAlphaBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag #include "UnityCG.cginc"UNITY_DECLARE_TEX2DARRAY(_TexArr);UNITY_DECLARE_TEX2DARRAY(_HightArr);float _Convert;sampler2D _MainTex;float _Speed;int _Index;struct appdata{float4 vertex : POSITION;float3 uv : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float3 uv : TEXCOORD0;};v2f vert (appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = v.uv;return o;}fixed4 frag (v2f i) : SV_Target{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"}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;/// <summary>
/// 序列帧动画管理器
/// </summary>
public class PictureManager : MonoBehaviour
{public static PictureManager Instance;public List<PictureInfo> PictureInfos = new List<PictureInfo>();/// <summary>/// 全部序列帧的缩放图片的倍数,占用显存过大/// </summary>public int Scale = 1;// Start is called before the first frame updatevoid Awake(){if(Instance!=null)throw new UnityException("已经设置了单例");Instance = this;LoadSequenceFrame();}// Update is called once per framevoid Update(){}/// <summary>/// 加载动画序列帧/// </summary>private void LoadSequenceFrame(){string [] directories= Directory.GetDirectories(GlobalSetting.SequenceFramePath);if(directories.Length<=0)throw new UnityException("在 " + GlobalSetting.SequenceFramePath+" 文件夹下没有找到序列帧文件夹");foreach (string directory in directories){string[] files = Directory.GetFiles(directory);List<Texture2D> texs = new List<Texture2D>();PictureInfo pictureInfo = new PictureInfo();DirectoryInfo directoryInfo= new DirectoryInfo(directory);pictureInfo.Name = directoryInfo.Name;//得到存放序列帧文件夹的名字Vector2 size =Vector2.zero;int scaleSize = 0;  //统一的尺寸foreach (string file in files){if (file.Contains(".meta")) continue;byte[] bytes = File.ReadAllBytes(file);Texture2D tex = new Texture2D(4, 4);tex.LoadImage(bytes);tex.Apply();//应用后,得到图片的真正尺寸if (scaleSize == 0){size = new Vector2(tex.width, tex.height);scaleSize= (int)Mathf.Sqrt( Mathf.ClosestPowerOfTwo((int)(size.x * size.y)));scaleSize =GlobalSetting.Get2PowLow(scaleSize);Debug.Log("序列帧缩放的尺寸为:"+scaleSize+"  继续缩放倍数为:" +Scale);if (scaleSize >= 1024) scaleSize = 1024;//图片尽量别大于1024pictureInfo.Size = size;}tex = GlobalSetting.Resize(tex, scaleSize, scaleSize);texs.Add(tex);}Texture2DArray texture2DArray=   GlobalSetting.SetTexToGpu(texs.ToArray());pictureInfo.Texture2DArray = texture2DArray;PictureInfos.Add(pictureInfo);}}public Texture2DArray GeTexture2DArray(string texArrayName){foreach (PictureInfo info in PictureInfos){if (info.Name == texArrayName){return info.Texture2DArray;}}return null;}public PictureInfo GeTPictureInfo(string texArrayName){foreach (PictureInfo info in PictureInfos){if (info.Name == texArrayName){return info;}}return null;}public  Texture2DArray LoadImage(string key){foreach (PictureInfo info in PictureInfos){if (info.Name == key){return info.Texture2DArray;}}return null;}}
/// <summary>
/// 一个序列帧集合的整体信息
/// </summary>
public class PictureInfo
{public PictureInfo(){}public PictureInfo(string name, Vector2 size){Name = name;Size = size;}/// <summary>/// 序列帧的名字,以加载的序列帧文件夹名字为准/// </summary>public string Name;public Texture2DArray Texture2DArray;/// <summary>/// 序列帧原本的尺寸,一版情况下,每张序列帧的尺寸都是一致的/// </summary>public Vector2 Size;public override string ToString(){string str = "\r\n";str += "PictureName is " + Name + "\r\n";str += "Size is " + Size + "\r\n";return str;}
}

布局图

unity 序列帧动画 UGUI GPU版相关推荐

  1. Unity序列帧动画疑难解答

    **Unity序列帧动画疑难解答 熟悉界面: 界面熟悉很重要,千万不要发生与别人沟通时你说菜单他去找工具栏的情况. **菜单栏:**基础创建设置工具等功能入口 **工具栏:**软件内的基本操作工具,基 ...

  2. Unity序列帧动画——Sprite图片集制作UI动画

    分享一个十分简单的在Unity中制作UI序列帧动画的方法.只需要将动画中要显示的图片导入Unity,将所有图片的TextureType设置成Sprite格式,然后全选所有图片,拖到场景中,提示要创建动 ...

  3. Unity Shader 序列帧动画

    shader中的序列帧动画属于纹理动画中的一种,主要原理是将给定的纹理进行等分,再根据时间的变化循环播放等分中的一部分. Unity Shader 内置时间变量 名称 类型 描述 _Time floa ...

  4. Unity——用代码实现序列帧动画

    序列帧动画经常用到,最直接的方式就是用Animation录制.但某些情况下这种方式并不是太友好,需要靠代码的方式进行序列帧动画的实现. 代码实现序列帧动画,基本的思路是定义一个序列帧的数组/列表,根据 ...

  5. Unity图片序列帧动画

    unity制作序列帧两种简单方法 1.美术将整个序列帧动画图片分割为多个png图片,如 然后选中所有,鼠标拖入Hierarchy面板,unity就会自动生成序列帧动画游戏对象,点击unity播放按钮就 ...

  6. unity spine动画渐变效果(UGUI及fairyGUI )

    unity spine动画渐变效果(UGUI及fairyGUI ) 查阅了一些资料,大部分都是直接去修改整个动画的alpha值.比如说spine动画默认shader为 更改成,此时修改shader的a ...

  7. Unity shader入门精要学习笔记-代码篇6(序列帧动画/滚动背景/流动河流/广告牌/顶点动画的阴影)

    一.序列帧动画 建立一个四边形对着摄像机. 我们需要一张序列帧图像,这里用到8x8的爆炸图. 给四边形上材质和shader,代码如下: Shader "Custom/NewSurfaceSh ...

  8. unity ParticleSystem 实现序列帧动画效果(一)

    用粒子系统实现序列帧动画优势: 先附上一个 介绍序列帧实现方式比较的链接 点击打开链接 在该链接里说较为倾向于使用该方式俩实现序列帧动画,那么具体的好处又有哪些呢? 此处再贴一个链接,这里介绍了下粒子 ...

  9. ❤️❤️❤️Unity废柴看过来,手把手教你做植物大战僵尸(二)—— 序列帧动画

    开始制作游戏,首先,我们要把游戏素材导入到项目中,我这里整理出来了一些项目中用到的图片音乐等素材,大家可以下载下来使用,或者自己从网上找其他好看的素材也可以. 植物大战僵尸素材 链接:https:// ...

  10. unity3d android 路径动画制作,Lesson11.Unity路径动画、路径变形动画实现方式

    鲸鱼的絮絮叨叨 Lesson01.unity简介和菜单栏介绍 Lesson02.unity粒子系统_1 Lesson02.unity粒子系统_2 Lesson03.3dmax粒子系统_1 Lesson ...

最新文章

  1. 剑指Offer_Python实现
  2. vscode 远程linux 服务器开发
  3. Win 8 app 获取窗口的宽度和高度, 本地化, 及文本读取
  4. hdu3951-(Coin Game)
  5. hdu-1104-Remainder(BFS打印路径+数论)(%与mod的区别)
  6. ftrace、kpatch、systemtap的基本原理、联系和区别
  7. mui 获取地图当前位置和经纬度
  8. C++ Templates 中的一个例子
  9. 上学是穷人的出路吗?
  10. CDOJ 29 飞镖(dart) 解题报告
  11. 基于51单片机毕业设计 开题选题
  12. HTML在手机上能编写吗,手机版使用开发
  13. 无线通信设备安装工程概预算编制_祁东设备安装工程施工承包-设计安装_天霖工程...
  14. 【vue学习笔记】vue-cli安装问题
  15. Unity 之 2D水插件推荐和模拟水效果制作分享
  16. W3C 发布 EME 标准,EFF 退出 W3C
  17. 什么是微前端及微前端优缺点
  18. 为了防止火灾发生,安科瑞余压监控系统在某高层住宅的应用方案
  19. 010序列检测电路设计
  20. 34. 在排序数组中查找元素的第一个和最后一个位置

热门文章

  1. 模2运算的加减乘除运算
  2. 【十次方】十次方项目前期准备
  3. 安装包时后面的参数以及简写
  4. 快手AI技术副总裁郑文:为什么说AI是短视频平台的核心能力
  5. 数控系统市场下行压力逐渐增大
  6. VS2010下安装Opencv
  7. 知物由学 | 人工智能、机器学习和深度学习如何在网络安全领域中应用?
  8. 卸载联想硬盘保护系统
  9. 百度文库文字下载(python原码)
  10. G - Power Strings