43.FGUI基础-Unity中的使用必备-DrawCall优化
43.1 知识点
DrawCall是什么
在 NGUI 中已经详细讲解了 DrawCall 的概念。
简单回顾 DrawCall:
- DC 就是 CPU 通知 GPU 进行一次渲染的命令。
- 如果 DC 次数较多会导致游戏卡顿。
- 我们可以通过打图集,将小图合并成大图,将本应 n 次的 DC 变成 1 次 DC 来提高性能。
FairyGUI 和 UGUI 以及 NGUI 的 DrawCall 优化异同
相同点:
- 三种 UI 都是通过打图集来优化 DC。
不同点:
- UGUI 和 NGUI 的 UI 元素层级会影响 DC 的数量,我们拼 UI 时,要注意不同图集的 UI 元素的层级关系。
- FairyGUI 中不用太过在意 UI 元素的层级,它会帮助我们进行深度调整。
- FairyGUI 采用了 Unity 的动态批处理技术,提供了深度调整技术进行 DC 优化。
动态批处理:CPU 每帧把可以进行动态批处理的网格进行合并,再把合并后的数据传给 GPU,使用同一个材质对其渲染,达到降低 DC 的目的。
FairyGUI 中优化 DrawCall 的关键步骤
TeachPanel panel = UIManager.Instance.ShowPanel<TeachPanel>("Teach");
// 1. 打开 Unity 中的动态批处理 File——>Build Setting——>PlayerSetting——>Other Setting——>Dynamic Batching
// 2. 将组件的 fairyBatching 属性设置为 true
panel.fairyBatching = true;
// 注意:
// 某个组件设置了 fairyBatching,那么无需在子组件和孙子组件再启用 fairyBatching
// 一般只在顶层组件打开这个功能(面板组件)
// 永远不要在 GRoot 上开启 fairyBatching
手动触发深度调整
对于打开了 fairyBatching 的组件,当自己调用 SetPosition 等 API 改变子元件或者孙子元件的位置、大小、旋转或缩放,并不会自动触发深度调整,可能导致 DrawCall 的增加。
例如一个图片原来显示在一个窗口里的顶层,你将它从原来的位置移到另外一个位置,这个图片就有可能被窗口里的其他元素遮挡。
开发者需要手动触发深度调整,使用 GObject 中的 InvalidateBatchingState 方法重新调整 DrawCall。
panel.InvalidateBatchingState();
注意:这个 API 并不需要由开启了 fairyBatching 的组件调用,可以是任何一个内含的元件。
43.2 知识点代码
using System.Collections;
using System.Collections.Generic;
using Teach;
using UnityEngine;
public class Lesson43_FGUI基础_Unity中的使用必备_DrawCall优化 : MonoBehaviour
{
void Start()
{
#region 知识点一 DrawCall是什么?
//具体DrawCall是什么在NGUI课程中已经详细讲解
//该节课是免费课 即使没有购买 也可以前往观看
//简单回顾DrawCall
//DC就是CPU通知GPU进行一次渲染的命令
//如果DC次数较多会导致游戏卡顿
//我们可以通过打图集,将小图合并成大图,将本应n次的DC变成1次DC来提高性能
#endregion
#region 知识点二 FairyGUI 和 UGUI以及NGUI的 DrawCall优化异同
//相同点:
//3种UI都是通过打图集来优化DC
//不同点:
//UGUI和NGUI的UI元素层级会影响DC的数量,我们拼UI时,要注意不同图集的UI元素的层级关系
//FairyGUI中不用太过在意UI元素的层级,它会帮助我们进行深度调整
//FairyGUI采用了Unity的动态批处理技术,提供了深度调整技术进行DC优化
//动态批处理:cpu每帧把可以进行动态批处理的网格进行合并,再把合并后的数据传给gpu,使用同一个材质对其渲染,达到降低DC的目的
#endregion
#region 知识点三 FairyGUI中优化DrawCall的关键步骤
TeachPanel panel = UIManager.Instance.ShowPanel<TeachPanel>("Teach");
//1.打开Unity中的动态批处理 File——>Build Setting——>PlayerSetting——>Other Setting——>Dynamic Batching
//2.将组件的fairyBatching属性设置为true
panel.fairyBatching = true;
//注意:某个组件设置了fairyBatching,那么无需在子组件和孙子组件再启用fairyBatching
// 一般只在顶层组件打开这个功能(面板组件)
// 永远不要在GRoot上开启fairyBatching
#endregion
#region 知识点四 手动触发深度调整
//对于打开了fairyBatching的组件
//当自己调用SetPosition等API改变子元件或者孙子元件的位置、大小,旋转或缩放
//并不会自动触发深度调整
//可能导致DrawCall的增加
//例如一个图片原来显示在一个窗口里的顶层
//你将它从原来的位置移到另外一个位置
//这个图片就有可能被窗口里的其他元素遮挡
//这时开发者需要手动触发深度调整 GObject中的InvalidateBatchingState方法 重新调整DrawCall
panel.InvalidateBatchingState();
//注意:这个API并不需要由开启了fairyBatching的组件调用
//可以是任何一个内含的元件
#endregion
}
}
43.3 练习题
在上节课的练习题基础上,优化练习题面板的DC
项目设置中开启动态批处理
在UI管理器,显示面板时设置开启动态批处理
//进行批处理 DC优化 开关开启
panel.fairyBatching = true;
43.4 练习题代码
using FairyGUI;
using System;
using System.Collections;
using System.Collections.Generic;
using Teach;
using UnityEngine;
public class UIManager
{
//在上节课的练习题基础上
//优化练习题面板的DC
private static UIManager instance = new UIManager();
public static UIManager Instance => instance;
//用于存储已经显示的 UI面板
private Dictionary<string, GComponent> panelDic = new Dictionary<string, GComponent>();
private UIManager()
{
//默认字体
UIConfig.defaultFont = "Other/STHUPO";
//默认音效
UIPackage.AddPackage("UI/Public");
UIConfig.buttonSound = (NAudioClip)UIPackage.GetItemAssetByURL("ui://Public/btnMusic");
//适配相关的设置
GRoot.inst.SetContentScaleFactor(1365, 768, UIContentScaler.ScreenMatchMode.MatchHeight);
//注册相关的代码
UIObjectFactory.SetPackageItemExtension("ui://Teach/ExercisesPanel", typeof(ExercisesPanel));
UIObjectFactory.SetPackageItemExtension("ui://Teach/ExercisesBagPanel", typeof(ExercisesBagPanel));
UIObjectFactory.SetPackageItemExtension("ui://Teach/TeachPanel", typeof(TeachPanel));
UIObjectFactory.SetPackageItemExtension("ui://Teach/ControllerTest", typeof(ControllerTest));
}
//组件名和面板类名 是一致的
public T ShowPanel<T>(string packageName) where T:GComponent
{
Type panelType = typeof(T);
string panelName = panelType.Name;
//如果字典中有该面板的名字 证明已经创建过了 直接返回即可
if (panelDic.ContainsKey(panelName))
{
panelDic[panelName].visible = true;
return panelDic[panelName] as T;
}
//加载包和依赖包
//由于从Resources文件夹中加载包 会帮助我们判断重复没有 所以 这里既是重复执行也没什么问题
UIPackage package = UIPackage.AddPackage("UI/" + packageName);
foreach (var item in package.dependencies)
{
UIPackage.AddPackage("UI/" + item["name"]);
}
//创建组件面板
GComponent panel = UIPackage.CreateObject(packageName, panelName).asCom;
//把组件的尺寸设置的和逻辑分辨率一致
panel.MakeFullScreen();
GRoot.inst.AddChild(panel);
//和父对象建立 宽高关联 这样 分辨率变化时 面板也不会出问题
panel.AddRelation(GRoot.inst, RelationType.Size);
//进行批处理 DC优化 开关开启
panel.fairyBatching = true;
//把当前显示的面板存起来 用于之后的隐藏
panelDic.Add(panelName, panel);
//把父类转换成对应的 子类
return panel as T;
}
public void HidePanel<T>(bool isDispose = false) where T:GComponent
{
Type panelType = typeof(T);
string panelName = panelType.Name;
//如果没有面板显示着 就直接返回
if (!panelDic.ContainsKey(panelName))
return;
//希望移除面板
if( isDispose )
{
//移除面板 并且从字典中移除
panelDic[panelName].Dispose();
panelDic.Remove(panelName);
}
//希望只是失活
else
{
panelDic[panelName].visible = false;
}
}
public T GetPanel<T>() where T:GComponent
{
Type panelType = typeof(T);
string panelName = panelType.Name;
//如果有这个面板 直接返回
if (panelDic.ContainsKey(panelName))
return panelDic[panelName] as T;
return null;
}
//主要用于销毁所有面板 和 资源垃圾回收的方法
public void ClearPanel(bool isGC = false)
{
//销毁所有面板 并且清空字典
foreach (var item in panelDic.Values)
{
item.Dispose();
}
panelDic.Clear();
if(isGC)
{
//释放所有包资源
UIPackage.RemoveAllPackages();
//垃圾回收
GC.Collect();
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com