在 Unity 中实现 GIF 动态功能
引言
工具链接在文章末尾。
Unity 本身是不支持 GIF 图的直接播放的,不过有多种方法可以间接实现:
通过 .net 的 Drawing库 来实现 GIF 图片解析,不过只能在 windows 平台使用。
使用 AE 等动画软件先将 GIF 转成序列帧,然后再在 Unity 中作为动画调用。
在脚本中手动解码 GIF 文件,然后将 GIF 的帧信息缓存在数组中,进行轮播。
本文主要介绍第三种方法。目的是将 GIF 图变为和普通图片一样, 可以导入到Unity中, 并挂载在脚本中运行。具体分为三个步骤, 导入、解码和播放。
导入 GIF 图
Unity默认会将 .gif 文件导入成 TextureAsset ,这显然不是我们需要的,解码 GIF 图需要文件的所有二进制信息,因此此处需要自定义一种 Asset ,代码如下。
123456789using UnityEngine;public class GifData : ScriptableObject{ /// <summary> /// GIF的解码器,可获取解码内容 /// &l ...
自定义 UnityEngine.Object.FindObjectsOfType<T>()
MyFindObjectsOfType该函数的遍历顺序为(以Hierarchy窗口为基准)从上到下,由外向内依次遍历。
手动遍历Hierarchy窗口, 对于某些需求(例如获取所有的顶层组件T)在一定程度上能够降低时间复杂度(剪枝),不过需要自己添加限制条件(例如:使用Dictionary记录已经获取过的组件)
同样的,MyFindObjectsOfType的返回值根据项目实际情况自行更改
1234567891011121314151617181920212223242526272829303132333435public static T[] MyFindObjectsOfType<T>() where T : Component{ Scene scene = SceneManager.GetActiveScene(); GameObject[] roots = scene.GetRootGameObjects(); List<T> objects = new List<T>(); for (int i = 0 ...
自动捕获UI变化
自动捕获UI变化
在UI组件发生变化的时候(布局、内容的变化,例如:某个Image组件被开启、Image组件里的Source Image发生改变、Image组件位置发生改变、Image组件大小发生改变等)会自动捕获该组件
UI变化的本质是网格重建
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283public class CaptureUIChanges : MonoBehaviour{ // 保存待重建布局元素(如:RectTransform变化) IList<ICanvasElement> m_LayoutRebuildQueue; // 保存待重建渲染元素(如:Image变化) IList<ICanvasElement> m_GraphicRebuildQu ...
A* 算法基础
A* 算法基础Dijkstra算法
Dijkstra算法用来寻找图形中节点之间的最短路径。
在Dijkstra算法中,需要计算每一个节点距离起点的总移动代价。同时,还需要一个优先队列结构。对于所有待遍历的节点,放入优先队列中会按照代价进行排序。
在算法运行的过程中,每次都从优先队列中选出代价最小的作为下一个遍历的节点。直到到达终点为止。
下面对比了不考虑节点移动代价差异的广度优先搜索与考虑移动代价的Dijkstra算法的运算结果:
最佳优先搜索
在一些情况下,如果我们可以预先计算出每个节点到终点的距离,则我们可以利用这个信息更快的到达终点。
其原理也很简单。与Dijkstra算法类似,我们也使用一个优先队列,但此时以每个节点到达终点的距离作为优先级,每次始终选取到终点移动代价最小(离终点最近)的节点作为下一个遍历的节点。这种算法称之为最佳优先(Best First)算法。
这样做可以大大加快路径的搜索速度,如下图所示:
但这种算法会不会有什么缺点呢?答案是肯定的。
因为,如果起点和终点之间存在障碍物,则最佳优先算法找到的很可能不是最短路径,下图描述了这种情况。
A* 算法 ...
AssetBundle内存、资源管理机制
管理已加载的Asset
在调用[AssetBundle.Unload]方法时,传入的 unloadAllLoadedObjects 参数(true或false)会导致不同的行为,这对于管理资源和AssetBundle来说非常重要。
例如,假设材质M加载自AssetBundle AB,并且M目前正在活动Scene中。
如果调用了 AssetBundle.Unload(true),那么M会被从Scene中移除、销毁并卸载。然而,如果调用了 AssetBundle.Unload(false),AB的数据头信息会被卸载,但是M仍然会留在Scene中而且有效。调用 AssetBundle.Unload(false) 会破坏M和AB之间的链接关系。如果之后又加载了AB,会将AB中的Object的新的副本加载到内存中。
如果之后又加载了AB,那么会重新加载一份新的AssetBundle数据头信息副本,但是不会在新的AB副本中加载M。Unity不会再新的AB和M之间建立任何链接关系。
如果调用 AssetBundle.LoadAsset() 重新加载M,Unity不会将已有的M副本 ...
Unity渲染优化的4种批处理
Static Batching(静态批处理)
静态批处理 是一种 draw call batching 方法, 它组合不移动的网格以减少 Draw Calls。它将组合的网格转换为世界空间,并为它们构建一个共享顶点和索引缓冲区。然后,Unity 执行单个 Draw Call,该调用使用此组合网格一次绘制批处理中的所有对象。静态批处理可以显著减少绘制调用的数量。
静态批处理 比 动态批处理 更高效,因为 静态批处理 不会转换 CPU 上的顶点。有关静态批处理的性能影响的更多信息,请参阅 性能影响。
使用静态批处理
Unity 可以在构建时和运行时执行静态批处理。作为一般规则,如果在构建应用程序之前场景中存在游戏对象,请使用 Editor 在构建时对游戏对象进行批处理。如果在运行时创建游戏对象及其网格,请使用 运行时 API。
使用 运行时 API 时,您可以更改静态批处理根的转换属性。这意味着您可以移动、旋转或缩放构成静态批处理的整个网格组合。您无法更改单个网格的 transform 属性。
注意:要使用运行时静态批处理,您还必须将网格设置为启用 读/写。
构建时的静态批处理
您可以 ...