Unity 性能优化汇总
渲染优化静态批处理将静态(不移动)的物体合并为一个大网格,减少DrawCall(需标记为Static)。 原理标明为 Static 的静态物件,如果在使用相同材质球的条件下,在Build(项目打包)的时候Unity会自动地提取这些共享材质的静态模型的Vertex buffer和Index buffer。根据其摆放在场景中的位置等最终状态信息,将这些模型的顶点数据变换到世界空间下,存储在新构建的大Vertex buffer和Index buffer中。并且记录每一个子模型的Index buffer数据在构建的大Index buffer中的起始及结束位置。 前提条件标记为 Static:物体必须在 Inspector 窗口勾选 Static 标志(至少勾选 “Static” 或 “Batching Static”)。 不可移动:静态批处理的物体在运行时不能移动、旋转或缩放,否则会破坏批处理效果。 相同材质:合并的物体必须使用相同的材质(Shader 和材质属性需一致)。如果材质实例不同(即使参数相同),批处理可能失败。 顶点限制:单个批次的合并网格顶点数不能超过目标平台的限制(例如 ...
基本数据结构
非泛型集合(System.Collections)特点:可存储任意类型(object),但存在装箱拆箱性能损耗,类型不安全。 ArrayList动态数组,支持自动扩容。 默认容量上限是4,初始容量是0。当容量不够时会判断触发双倍增容。 123ArrayList list = new ArrayList();list.Add("Hello"); // 任意类型list.Add(123); 注意由于 ArrayList 是非泛型集合所以很多操作都会触发拆装箱。不过如果插入一个继承自 ICollection 的集合,其内部会批量化拆装箱。 Hashtable键(Object)值(Object)对集合,基于哈希表实现。 12Hashtable table = new Hashtable();table["key1"] = "value1"; QueueStackSortedListBitArray上面的集合都类似于 ArrayList 和 ArrayList<T> 的对比,所以剩下的内容直接写入泛型集合中。 泛型集...
Path Finder
Dijkstra算法与A*算法的详细对比 算法目标 Dijkstra算法:寻找单源最短路径,即从起点到图中所有其他节点的最短路径。 A*算法:在启发式函数的引导下,高效寻找起点到特定目标节点的最短路径。 核心原理 算法 核心思想 关键公式 Dijkstra 优先扩展距离起点最近的未访问节点(贪心策略),逐步”淹没”整个图。 g(n):起点到节点n的实际代价 A* 综合实际代价g(n)和启发式估计h(n),优先扩展最接近目标的节点。 f(n) = g(n) + h(n) 算法步骤Dijkstra算法流程: 初始化:起点g=0,其他节点g=∞,所有节点标记为未访问。 将起点加入优先队列(按g排序)。 取出队列中g最小的节点u: 若u是目标节点,结束。 遍历u的邻居v: 计算新代价:tentative_g = g(u) + edge(u, v)。 若tentative_g < g(v),更新g(v)并将v加入队列。 重复步骤3,直到队列为空或找到目标。 A*算法流程: 初始化:起点g=0,f=h(start),其他节点f=∞,维护开放列表和关...
华佗热更
前言我知道的时候还叫华佗,后面改名了,不过我感觉我算是赶上好时候了,目前没有更好的热更方式了,无论是性能还是框架复杂度,上手情况。 而且这就不得不提一下我的上一个主程了,在华佗还没有广泛应用的时候就很有先见之明的选择了华佗,然后配合 Jenkins 自动化热更出包,这一步真是省了太多人力。 热更的逻辑很简单,我登陆之前先访问一个地址,获得版本号,如果不一样那么就下载更新到最近版本就行了(如果比版本号高那就想不到啥情况了),然后按照正常逻辑加载,进入游戏。如果是资源的话,可能还需要多做一步根据比对,删除废弃无关的资源。 热更技术原理:app+脚本解释器+脚本代码,动态执行最新代码,实现热更 Lua 热更的细节和过程:Lua虚拟机的初始化与配置 单例模式:通常,一个Unity应用会创建一个Lua虚拟机实例(如LuaEnv),作为全局唯一的Lua运行环境。 初始化过程: 123LuaEnv luaEnv = new LuaEnv(); // 创建虚拟机实例luaEnv.AddLoader(CustomLoader); // 注册自定义加载器(用于从文件或AB包加载脚本)luaEn...
UI 性能优化(持续更新中)
性能优化性能优化是个老生常谈的问题了,而且性能优化不是开发完再开始优化,是做之前就想好优化方向,在做的时候就要开始注意。 TD123456A[Canvas] --> B(标记脏区域)B --> C[Canvas.BuildBatch]C --> D[生成网格数据]D --> E[合批处理]E --> F[提交渲染指令]F --> G[GPU渲染] 减少Draw Calls 使用Atlas图集:将多个小的UI图像合并成一个大图集,减少纹理切换的次数,由于纹理是预先合并的,GPU在渲染时不需要频繁切换纹理,减少了状态切换的开销,从而提高了渲染效率。注意必须要合并到同一个图集的同一张中,如果因为图片太大导致在同一个图集的不同张中是无法减少 Draw Call 的。 所以背景图不建议放到图集中,图标和小的 UI 切图建议打成图集。 合理使用Canvas:将需要频繁更新的UI元素放在独立的Canvas中,以减少整个UI的重绘次数。 纹理格式格式设置规则根据平台对图片进行合适的设置。 优先使用压缩格式除非特殊需求(如后期处理源纹理、遮罩图),始...
屏幕破碎
屏幕破碎屏幕破碎效果其实很简单,就是一个 UV 偏移就行,如果厉害一点的大佬可能直接用函数就直接做了,对美术效果要求高一点或者更简单一点的方法用法线贴图做就行。 核心代码123456789101112131415161718v2f vert(a2v v){ v2f o; o.pos=UnityObjectToClipPos(v.vertex); o.uv.xy=TRANSFORM_TEX(v.texcoord,_MainTex); o.uv.zw=TRANSFORM_TEX(v.texcoord, _BrokenNormalMap); return o;} fixed4 frag(v2f i) : SV_Target{ fixed4 packedNormal = tex2D(_BrokenNormalMap,i.uv.zw); fixed3 tangentNormal = UnpackNormal(packedNormal); tangentNormal.xy*=_BrokenScale; fl...
深度扫描
深度扫描这是死亡搁浅里面的一个经典效果,以人物为中心,扫描周围的环境。我想复刻一下这个根据人物为中心,扫描周围的效果。 思路分析第一眼看上去就跟自身位置有关,并且随着距离的增加发现每个物体的边缘都显现了出来。但是我总不能在所有模型里面加一个材质专门做这个事情吧,所以目前想法就是后处理做这件事,那么目前思路如下。 通过屏幕坐标还原世界坐标。 根据世界坐标生成一个圈,然后根据距离变动。 修改这个圈附近的效果,将边缘识别放进去。 美化一下,增加一下颜色等效果。 还原世界坐标通过屏幕坐标还原世界坐标方法还蛮多的。 暴力还原,如果先暂时不考虑性能,通过 NDC 暴力还原回去。 一开始就考虑性能,通过相似三角形和向量加减在 Vert 里面做,然后在 Frag 里得到差值结果。 暴力还原从 World Position 到 View Position 到 Clip Position 到 Screen Position,一步一步往回推。 第一步先求 NDC 坐标,在后处理 Shader 中,Frag 函数内拿到的 UV 坐标就是屏幕范围了,注意这里并不是屏幕坐标,这里的 UV 大小取值...
Addressable
Bundle Bundle 的简单原理就是按照一定的规则和压缩格式,将 Unity 中的所需要的文件都集合成统一的形式。 每一个单独形成的 Bundle 文件,都有一个描述的 manifest 文件来表述这个 Bundle 文件中包含的文件。 而 Bundle 与 Bundle 之间又会有一个总的 AssetBundles.manifest 文件来描述他们之间的关系。 Bundle 的实现细节 Build Asset Bundle 总会通过下面这样的方法来进行。 123456public static AssetBundleManifest BuildAssetBundles(string outputPath, AssetBundleBuild[] builds, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform){ BuildTargetGroup buildTargetGroup = GetBuildTargetGroup(targetPlatform); ...
Command Buffer
CommandBuffer在 Unity 中,Command Buffer 是一个强大的工具,用于在 GPU 上控制渲染操作的顺序。它允许开发者在渲染管线的特定阶段插入自定义的绘制命令,以实现更复杂的效果和优化。以下是 Command Buffer 的用法、优缺点以及一些示例。 Command Buffer 的用法 创建 Command Buffer: 使用 CommandBuffer 类创建一个命令缓冲区。 12CommandBuffer commandBuffer = new CommandBuffer();commandBuffer.name = "My Command Buffer"; 插入绘制命令: 向 Command Buffer 中添加绘制命令,例如绘制物体、设置渲染状态等。 12commandBuffer.Clear(); // 清空命令缓冲区commandBuffer.DrawMesh(mesh, matrix, material); // 绘制网格 将 Command Buffer 附加到渲染管线: 将 Command Buff...
Compute Shader
Compute Shader使用 Compute Shader 可以充分发挥 GPU 的并行计算能力,显著提升复杂计算任务的性能,特别是在需要大规模并行计算的场景中。它提供了自由灵活的计算流程,适用于各种非图形的通用计算任务,同时还能无缝结合渲染任务,为开发者带来更大的优化空间和跨平台支持。 常量定义CBuffer(常量缓冲区)CBuffer 是最常用的方式之一,用于在 GPU 程序中定义常量,可以从 Unity C# 脚本中传递数据。常量缓冲区中的数据是从外部提供的(通常通过 CPU 传递给 GPU),并且在计算着色器的所有线程中共享。 1234567// ComputeShader 中定义一个常量缓冲区CBuffer MyConstants{ float4 someColor; // 颜色常量 int someIntValue; // 整型常量 float someFloatValue; // 浮点数常量}; 在 C# 中传数据过去 1234// 从 C# 脚本设置常量缓冲区中的值computeShader.Set...