UGUIDrawCall优化

UGUIDrawCall优化,第1张

1、UI DrawCall分析工具

最近只针对项目中主界面的UI进性DrawCall的优化,主要用到的工具就是Unity自带的Frame Debugger,我们项目使用的是Sprite Packer打图集,如果是从本地加载资源,如图1-1,首先要确保Project Settings中Sprite Packer Mode为Always Enabled,保证游戏运行时所有的图标打到对应的图集中。如果使用AssetBundle方式加载,打包的时候应该已经打成图集了,该项设置不设置就无所谓了。

Frame Debugger打开方式Window->Analysis->Frame Debugger(我用的Unity版本是201848),打开后界面如下:

点击后就会展示游戏中所有的DrawCall信息,如图1-3,Unity UI绘制调用的位置在CameraRender下Render的子组CanvasRenderSubBatch下,后边的68即为UI占用的DrawCall数量。Unity UI绘制调用的位置取决于Canvas的Render Mode,我们使用的Render Mode为Screen Space-Camera。如果Render Mode设置为Screen Space - Overlay,则Ui绘制的位置在CanvasRenderOverlay组中,如图1-4所示;如果Render Mode设置为World Space时也是在CameraRender下Render的子组中,虽然都是子Render,但是和Screen Space-Camera模式会在不同的子组中。

选中某个Draw Mesh右侧就会显示该详细信息,如图1-5,选中的是一个Image,其中1表示该Image渲染的渲染层级为203,2表示该组件使用的shader为UI/Default,3表示Image所在的图集为Common(Group 1)。

再看一个Text的详细信息,如图1-6,基本与Image相同,只是使用的纹理不同。

2、影响Draw Call的因素

(1) 针对Image,需要保证使用的在同一图集且同一个Group中,且使用的材质相同。

a、 如下图所示,一般的Image都不会设置Material即Material为None,此时使用的就是UGUI默认的材质,如图1-4中的2标记的位置,都是默认UI/Default,所以材质的问题一般不需要考虑;

b、 重点考虑的就是图集问题,这是比较麻烦的问题,需要根据使用的范围来规划图集。开始我们把所有主界面用到的都放到一个图集中,但是由于个数太多,而图集又有大小上限(我们项目中设置的为10241024),虽然会打到同一个图集中,但是会被分到不同的Group中。通过Frame Debugger查看即使在同一个图集中,不在同一个Group也不能合批。所以直接把所有放在一个图集中并不能解决问题,还要根据UI进行细分,同一个UI中使用的打到一个图集里,或几个UI中用到的图集打到一个图集中。好多个UI公用的单独放到一个公用的图集中。为了便于规划图集,我还写了个小工具统计每个在各个UI中的使用情况。统计结果如图2-2,每一行显示某个的引用次数,次数后紧跟引用该的UI。

[MenuItem("策划/UI/收集UI中引用情况")]

public static void GetSpriteReferCount()

{

            Dictionary<string, Dictionary<string, bool>> spriteToPrefab = new Dictionary<string, Dictionary<string, bool>>();

            List<string> allFullPath = MUEditorUtilityGetAllFullPathIn(UI_ASSET_DIR, "prefab");

            for (int i = 0; i < allFullPathCount; i++)

            {

                EditorUtilityDisplayProgressBar("检测", "正在收集UI中的引用情况……", i / (float)allFullPathCount);

                string assetPath = MUEditorUtilityFullPathToAssetPath(allFullPath[i]);

                GameObject uiGameObj = AssetDatabaseLoadAssetAtPath<GameObject>(assetPath);

                if (uiGameObj == null)

                {

                    continue;

                }

                Component[] allImageComponents = uiGameObjtransformGetComponentsInChildren(typeof(Image), true);

                for (int j = 0; j < allImageComponentsLength; j++)

                {

                    Image t = allImageComponents[j]GetComponent<Image>();

                    if (tsprite != null)

                    {

                        string spriteName = tspritename;

                        if (spriteToPrefabContainsKey(spriteName) == false)

                        {

                            spriteToPrefab[spriteName] = new Dictionary<string, bool>();

                        }

                        if (spriteToPrefab[spriteName]ContainsKey(uiGameObjname) == false)

                        {

                            spriteToPrefab[spriteName][uiGameObjname] = true;

                        }

                    }

                }

            }

            Dictionary<string, Dictionary<string, bool>> spriteToPrefab1 = spriteToPrefabOrderBy(o => oValueCount)ToDictionary(p => pKey, o => oValue);

            DirectoryInfo dirInfo = DirectoryCreateDirectory(ApplicationdataPath + "/Res/Gui");

            FileInfo[] files = dirInfoGetFiles("UI_Sprite_Refertxt");

            foreach (var file in files)

            {

                FileDelete(fileFullName);

            }

            using (FileStream fileStream = FileCreate(dirInfoFullName + "/UI_Sprite_Refertxt"))

            {

                string txt = "";

                string temp = "";

                foreach (KeyValuePair<string, Dictionary<string, bool>> kv1 in spriteToPrefab1)

                {

                    temp = kv1Key + ":";

                    //temp += " " + GetParentDir(kv1Key);

                    temp += "  " + kv1ValueCount;

                    foreach (KeyValuePair<string, bool> kv2 in kv1Value)

                    {

                        temp += "  " + kv2Key;

                    }

                    txt += temp;

                    txt += "\n";

                }

                StreamWriter writer = new StreamWriter(fileStream);

                writerWrite(txt);

                writerFlush();

                writerClose();

                fileStreamClose();

            }

            EditorUtilityClearProgressBar();

        }

c、 在打图集的过程中还遇到一个问题,同一图集中的一个,在总大小没有超过图集上限的情况下,却被打到了该图集的另一个Group中。使用Frame Debugger也看不出问题。然后我就用Sprite Packer在本地打一下图集看一下,这两个Group区别在哪儿。通过对比发现两个Group中的格式不一样,一个为RGBA Compressed DXT5,一个为RGB Compressed DXT1,如图2-3和2-4。

原来Unity打图集时只能将相同格式的打到同一Group中。查看被打到另一个Group中的,发现这张图没有Alpha通道,工程中所有平台的纹理Format都设置为Automatic,Alpha Source设置为Input Texture Alpha,即Unity会根据原图是否有Alpha通道自动设置纹理格式,如图2-5。

我们可以通过重写不同平台上的格式设置,保证该格式与其他格式一致,如图2-6。

(2) 针对Text,保证相邻的Text使用相同字体,相同材质,这个一般情况都可以做到。

(3) 保证可以合批的元素在同一个Canvas下。

为了防止某些动态元素(如倒计时、技能CD等)的改变导致整个UI的网格重建,一般采用动静分离的方式,将动态元素放在单独的Canvas下。这时可能误将一些本可以合批的静态元素放到了不同的Canvas下。我在优化时就遇到了这种情况。如图2-7所示,Image_Kuang和Image_Parent位于同一渲染层级,且他们挂的在同一图集里,但由于Image_Parent节点下有动态变化元素,所以单独挂了Canvas,导致这两个Image不能合批。解决方式就是将Image_Parent的Image移除挂到一个新建的与Image_Kuang位于同一Canvas的节点下,如图2-8所示,在Image_Kuang下单独建了一个Image_PosBack节点挂载Image。

(4) Mask和Rect Mask 2D会将内外元素分离导致不能合批。另外能用Rect Mask 2D解决的尽量不要用Mask,Mask除了使内外元素不能合批,本身也会占用2个DrawCall。Mask和Rect Mask 2D比较如下:

a、 Mask占用两个DrawCall,一个在底下设置Stencil Buffer,一个在顶上还原Stencil Buffer,Mask下的子元素夹在中间本身不占DrawCall;Rect Mask 2D本身不占DrawCall

b、 如果多个Mask绑定的Image组件,属于同一个Atlas,那么Mask之间的元素可以进行合并(包括Mask自己产生的2个DrawCall);否则不能合并如果RectMask2D上绑定了Image,那么多个RectMask2D的Image如果属于同一个Atlas可以合并

c、内外元素 无法合批无法合批

d、完全裁剪元素:Mask 依然占据DrawCall;Rect Mask 2D不占用DrawCall,也不参与Depth计算;

e、Hierarchy 中被分割元素:Mask 可以正常合批;Rect Mask 2D:如果Depth、Atlas与RectMask2D下的某元素相同,则无法合批。

f、裁剪掉的部分:Mask 还会影响其他元素的Depth计算,而它自己的也会受到其他元素的影响。Rect Mask 2D 依然参与Depth计算

g、 多个Mask内的UI节点间如果符合合批条件,可以合批。RectMask2D之间无法合并DrawCall。

(5) UI元素Position或者Rotation改变

a、 Position的Z值不为0,元素会被视为3DUI,不参与合批,如果父节点Z!=0,其下的子元素都无法合批。

b、 Rotation的X或Y修改,导致元素不在UI平面,则无法合批,原因和Position的Z值改变一样。

(6) UI元素重叠和层级深度

这个最终要的结果就是使多个元素的渲染层级相同,就达到合批目的了,如图1-4中1标记的位置。当然这一项的调整也是最耗时最需要耐心的,这就需要我们去分析每个UI中各个元素在Hierarchy中的层级。

下面拿一个例子说明一下,如图2-9所示,图中左侧红框内4个元素,其中Image_Back、Drop_Image、Btn_Image所用在一个图集中,Drop down使用的在其它图集中。这时所有的元素占用3个DrawCall,查看右侧Draw Mesh发现Image_Back和Drop_Image共占一个DrawCall,而Btn_Image和Dropdown分别单占一个DrawCall。

下面我们调整一下Btn_Image的位置,如图2-10所示,整体的Draw Call变成2,Image_Back、Drop_Image、Btn_Image三个元素共占一个DrawCall。Dropdown单独占一个DrawCall。这是因为开始时Dropdown夹在Drop_Image和Btn_Image中间,由于它属于另外的图集,破坏了Drop_Image和Btn_Image的合批。

Unity自带的Profiler工具中可以查看合批中断的原因。如图2-11所示,打开Profiler,滑动到最底部可以看到两个和UI相关的选项,一个UI,一个UI Details,选中任意一个,下边有全部Canvas的一个列表,点击展开任意一个Canvas,就可以看到该Canvas下所有的批处理信息,其中有一列Batch Breaking Reason显示了该批次不能与上一个批次合批处理的原因,合批被破坏的主要原因有两个:Different Texture:即使用的纹理不同,可能是不在同一个图集,也可能是Image和Text相邻导致;Different Material Instance:材质不同,一般由于自己给Image使用了自定义材质或者游戏中使用了Text Mesh Pro UGUI。另外GameObject Count为该批次处理对象的个数;GameObjects列列出了所有对象,可能由于对象太多只显示有几个Objects,这是可以双击这一行,会直接选中该批次处理的所有对象,如图2-12所示。

另外看到一个Cumulative Batch Count:累计批次数量。如图2-11,我们可以看到该列的总值为82,我们再看一下Frame Debugger中 Draw Mesh的个数,如图2-13为69。这两个数不一样,Frame Debugger中显示的是当前帧Batch个数,而Cumulative Batch Count是从开始游戏运行总的批次个数,在游戏过程中有些元素的改变导致批处理的变化,所以这两个数会不一样。

(7) 对于Alpha为0的Image,需要勾选其CanvasRender组件上的Cull Transparent Mesh选项,否则依然会产生DrawCall且容易打断合批。

(8)空Image

有时会使用空的Image来接受点击事件,空Image也会产生DrawCall。我们可以使用Empty4Raycast组件替代空Image。

效果图,素材是网上随便拉的

这是运行后的Hierarchy的界面,其中最下面的Item是放在摄像机不能拍到的位置,当做预设体,每个Item都有Toggle组件,在Grid上有Toggle Group 组件,并且将Itme上的Toggle组件中Group设置为Grid,这实现了点了一个之后,其他不会高亮。Grid上有组件 Grid Layout Group,该组件实现了当创建Item,并且将父物体设置为Grid后能够自动排版

这里我们需要写三个类,一个是自己捏造的数据类,放在Item上获取各种UI的类,还有能够创建Item并且能够对Item进行管理的类

public class ItemData//这个类存放的是数据

{

public int _starNum;//星星的数量(本来是想找星星的,现在就随便将就一下)

public string _icon;//这是名字

public string _itemName;//关卡的名字

public string _itemNum;//第几关

}

item上的类,用来获取item上的UI控件

public class Item : MonoBehaviour

{    

public Image _star1;    

public Image _star2;    

public Image _star3;    

public Image _icon;    

public Text _levelNum;    

public Text _levelName;    

void initializeItem()    

{        

_star1 = transformFind("StarGroup/star1")GetComponent<Image>();       

 _star2 = transformFind("StarGroup/star2")GetComponent<Image>();        

_star3 = transformFind("StarGroup/star3")GetComponent<Image>();

 _icon = transformFind("Icon")GetComponent<Image>();        

_levelName = transformFind("Levelname/name")GetComponent<Text>();        

_levelNum = transformFind("Levelname/Lv")GetComponent<Text>();

}

void Awake()

{

initializeItem();

}

}

这是个管理Item创建的类

public class MainMgr : MonoBehaviour 

{    

GameObject item;    

Transform Parent;    //这里需要得到Gird的Transform

List<ItemData> dataGroup = new List<ItemData>();   

void Awake()    

{        

CreateData();        

item = transformFind("Item")gameObject;        //获得一开始放在摄像机外的游戏对象,当做预设体

Parent = transformFind("Grid");    //保存Gird的Transform

}    

void CreateData()    //创建自己捏造的数据

{       

 dataGroupClear();//确保这个List没有其他数据        

ItemData itemdata = new ItemData();        

itemdata_icon = "1 (5)";       

itemdata_itemName = "小树林";        

itemdata_itemNum = "第一关";        

itemdata_starNum = 3;        

dataGroupAdd(itemdata);        

ItemData itemdata1 = new ItemData();        

itemdata1_icon = "1 (7)";        

itemdata1_itemName = "沼泽";       

 itemdata1_itemNum = "第二关";        

itemdata1_starNum = 1;        

dataGroupAdd(itemdata1);        

ItemData itemdata2 = new ItemData();        

itemdata2_icon = "1 (15)";        

itemdata2_itemName = "山海关";        

itemdata2_itemNum = "第三关";       

itemdata2_starNum = 1;        

dataGroupAdd(itemdata2);        

ItemData itemdata3 = new ItemData();        

itemdata3_icon = "1 (12)";        

itemdata3_itemName = "墓地";        

itemdata3_itemNum = "第四关";        

itemdata3_starNum = 2;        

dataGroupAdd(itemdata3);       

 ItemData itemdata4 = new ItemData();        

itemdata4_icon = "1 (32)";        

itemdata4_itemName = "神殿";       

 itemdata4_itemNum = "第五关";       

 itemdata4_starNum = 3;        

dataGroupAdd(itemdata4);        

ItemData itemdata5 = new ItemData();        

itemdata5_icon = "1 (25)";        

itemdata5_itemName = "天庭";        

itemdata5_itemNum = "第六关";        

itemdata5_starNum = 2;        

dataGroupAdd(itemdata5);        

ItemData itemdata6 = new ItemData();        

itemdata6_icon = "1 (30)";        

itemdata6_itemName = "心魔";        

itemdata6_itemNum = "第七关";        

itemdata6_starNum = 3;        

dataGroupAdd(itemdata6);   

 }    

GameObject tempItem; //创建临时的游戏对象   

void CreateTempItem()    //创建Item

{        

if (dataGroup != null)       //当这个List不为空时

{            

for (int i = 0; i < dataGroupCount; i++)            //循环创建Item

{                

tempItem = Instantiate(item) as GameObject;          //创建Item并且获取到这个游戏对象     

 tempItemtransformlocalPosition = Vector3zero;                //将其位置,缩放大小,旋转角度初始化

tempItemtransformlocalRotation = new Quaternion();                

tempItemtransformlocalScale = Vector3one;                

tempItemtransformSetParent(Parent);                //设置其父物体为Grid

Item itemSprite = tempItemAddComponent();  //为每一个创建的Item添加脚本

itemSprite_levelNametext = dataGroup[i]_itemName;//获取每一个关卡的名字获取,一下类似

itemSprite_levelNumtext = dataGroup[i]_itemNum;

string path = stringFormat("Icon/{0}", dataGroup[i]_icon);//字符串拼接

itemSprite_iconsprite = ResourcesLoad(path, typeof(Sprite)) as Sprite;

ShowStar(dataGroup[i]_starNum, itemSprite);//调用这个方法获得星星的显示

}

}

}

void ShowStar(int num,Item tempitem)//这个方法是用来显示星星

{

if (num == 1)

{

tempitem_star1gameObjectSetActive(true);

tempitem_star2gameObjectSetActive(false);

tempitem_star3gameObjectSetActive(false);

}

if (num == 2)

{

tempitem_star1gameObjectSetActive(true);

tempitem_star2gameObjectSetActive(true);

tempitem_star3gameObjectSetActive(false);

}

if (num == 3)

{

tempitem_star1gameObjectSetActive(true);

tempitem_star2gameObjectSetActive(true);

tempitem_star3gameObjectSetActive(true);

}

}

void Start () {

CreateTempItem();

}

}

以上就是简单的游戏关卡的选择界面的实现,如果有错误,或者更好的方法,望指正,万分感谢!

文章内容:

一、认识DoTween插件

二、通过UGUI制作血条的三种方法

认识DoTween:

Using DGTweening;//引用DoTween插件的命名空间

脚本:

1DoTween Animation

2DoTween Path

类:

1DOTweenAnimation(补间动画)

属性:设置是否自动销毁:补间动画对象autoKill

更改动画持续时间:补间动画对象duration

更改动画的延时播放时间:补间动画对象delay

更改动画的循环次数:补间动画对象loops

更改动画的循环类型:补间动画对象loopType

方法:向前播放动画:补间动画对象DOPlayForward();

向后播放动画:补间动画对象DOPlayBackwards();

通过ID名称播放动画:补间动画对象DOPlayById(ID名称);

2Tweener

DoTween创建的动画,默认都是Play状态,而且播放完毕之后会自动销毁,

但是有很多动画需要不断的循环次数,

所以说按默认的话,就需要不断的创建和销毁动画,

这样就造成了内存的消耗和资源的浪费,我们要避免这种情况。

DoTween为我们提供了一个类,方便我们对动画的状态和属性进行修改,这个类就是Tweener

方法:

移动:Tweener对象=Transform对象DOMoveX(X方向移动的距离,持续播放的时间);

延迟:Tweener对象SetDelay(延迟播放的时间);

设置播放(运动)方式:Tweener对象SetEase(Ease枚举成员名);

暂停:Tweener对象Pause();

//可以分开写,也可以一次写完

Tweener对象=Transform对象DOMoveX(距离,持续时间)SetAutoKill(true或false)SetEase(Ease枚举成员名)SetLoops(循环次数,循环类型枚举成员名);

实例:通过点击打开按钮、关闭按钮播放动画(OnEnable方法、OnDisable方法、Tweener类、Button类,使用标志位)

3Sequence(队列)

实例化一个Sequence:Sequence 对象名= DOTweenSequence();

方法:

往队列中添加动画:队列对象Append(添加的动画);

队列延时播放动画:队列对象PrependInterval(延迟时间);

往队列中插入动画:队列对象Insert(插入的位置,插入的动画);

实例:制作血条(通过克隆、动态加载、父子物体、坐标转换、显示隐藏)

方法一:通过静态加载:拖游戏对象给脚本进行实例化;

方法二:通过动态加载:使用ResourcesLoad<后面的预设体类型对应>("预设体的路径")   //此方法默认状态下是在Resources目录下查找预设体!!!!

预设体必须在Resources的目录下才能被找到,否则会报错!!!

以上两种方法,克隆的血条需要单独放在另一个画布(Canvas)中存放;

在调整血条大小的时候,不要直接调整血条的宽高,很容易出错,最好是调整它的缩放;

画布的渲染模式选择摄像机渲染屏幕空间,方便观察,可以显示血条(2D),也可以显示方块(3D物体)

方法三:画布的渲染模式选择世界空间渲染模式,把画布也当作一个游戏对象,分别把每个画布作为每个方块的子物体,并把每个方块的血条当作每个画布的子物体,通过画布显示每个方块的血条信息,使用LookAt方法写一个让带有血条的父的物体画布朝向摄像机的位置

Image控件是用来显示一张的,sprite类型的

CanvasRenderer:负责UI元素的渲染,所有UI控件都需要一个CanvasRenderer,它继承自Componet

SpriteRenderer(渲染sprite用的)、MeshRenderer(渲染三维物体用的)、SkinnedMeshRenderer(渲染蒙皮的物体),它们都继承自Renderer负责渲染的

SourceImage: 是你要显示的,下面的有一个对应的 Set Native Size按钮 ,有可能会被拉伸,按这个按钮可以让Image控件的宽高设置为和原始的宽高一样,

Material: 材质,当我们需要一些比较高级的效果,比如你要为这张编写自己的shader的时候,才会用到材质

ImageType: 表示展示的类型,默认是Simple,就是正常显示,对应一个Preserve Aspect选项,意思是,是否保持比例(宽高比), 在屏幕适配的时候,有可能会拉伸,这时候为了不让失真,一般会勾选这个选项

1引入命名空间

using UnityEngineUI

2获取组件

Image image = GetComponent<Image>();

3改变属性

imagesprite = xxx;

RawImage控件也是用来显示一张的,但是显示的是Texture类型的

用于显示文本,这个和一些应用里面的字体差不多,没什么好说的

Line Spacing:行距

Rich Text:是否使用富文本,就是会检测到尖括号

Best Fit:根据Text控件的大小来显示合适的字体大小,字体大小会受控件大小的限制,即文本会完整显示

1 判定cell大小

LoopScrollRect要解决的核心问题是:如何计算每个元素的大小。这里我使用了Content Size Fitter配合Layout Element来控制每个cell的长宽,因此对于GridLayout直接取高度,否则取Preferred Height。需要注意的是,除了元素本身的大小之外,我们还要将padding考虑进去。

这个其实也是最核心的一个地方:在能够准确计算格子大小的基础上,后续工作就好实现了。

2 如何优雅的增删元素

对于每个ScrollRect,其实只需要考虑在头部和尾部是否需要增加或者删除元素。在这里以头部的各种情况为例进行解释,因为在正向滑动情况下,必须保证在修改元素之后整个ScrollRect内容显示一致不跳变;这些情况比尾部处理会麻烦一些。

NewItemAtStart函数实现了在头部增加一个(或一行,针对GridLayout)元素,并返回这些元素的高度;DeleteItemAtStart代表删除头部的一个元素。需要注意的是,在修改头部元素之后要及时修改content的anchoredPosition,这样才能保证整个内容区域不会因为多了或者少了一行而产生跳变。

3 何时需要增删元素

这里需要有两个概念viewBounds和contentBounds:前者是指ScrollRect本身的大小,一般也对应Mask;后者是指ScrollRect里所有cell组成的内容部分的大小。在这个基础上就简单了:如果contentBounds的最上面比viewBounds的最上面要低,那么尝试在顶部增加元素;如果contentBounds的最上面比viewBounds的最上面高很多,那么尝试删除元素。

4 对象池交互

在新建cell和销毁cell的时候,使用对象池来避免内存碎片;同时这里使用了SendMessage来向每个cell发送必须的信息,保证数据的正确性。

5 滚动条相关

这块我其实是估算的,根据当前的长度和当前元素个数/总个数按照比例缩放,这个在所有cell大小一致的情况下是没有问题的;但是如果大小不一致我就无法得到精确结果,所以会产生一定抖动。我暂时没有更好办法,因为得到的信息就是不够用。

自学备用,如有问题欢迎及时指正,我会尽快改正。

(版本unity 201829f1)

本篇记录 Unity UGUI canvas 中的各项参数设置和效果,还是那句话自学备用,如有问题欢迎指正,我会尽快改正。

新建的Canvas拥有Rect Transform,Canvas,Canvas Scaler 以及 Graphic Raycaster 这几个组件。Rect Transform此时为灰色,在Scene视图中不能对Canvas进行 *** 作。Rect Transform 下一行英文“Some values driven by Canvas” ——Canvas驱动一些值。

此设置时,Canvas会填满整个屏幕,并将Canvas下面所有的UI元素置于屏幕的最上层,Canvas将一直覆盖场景中普通的3D GameObject。

启用pixelPerfect可以使元素看起来更清晰并防止模糊。勾选不启用抗锯齿(还在实验中,暂无截图)。

Canvas深度,数值越大,显示的优先级就越高,也就是数值大的Canvas会遮挡住数值小的Canvas。

此设置使Canvas渲染到指定的显示中。支持的辅助显示器(例如监视器)的最大数量为8。

最新几个版本中拥有的特性,提供给Shader使用的参数。此处不太了解。

启用pixelPerfect可以使元素看起来更清晰并防止模糊。勾选不启用抗锯齿(还在实验中,暂无截图)。

指定Canvas渲染在哪一个摄像机上

当Camera的Projection为Orthograhic时,此值得改变仅仅会改变Canvas的Pos Z;

当Camera的Projection为Perspective时,此值得不仅会改变Canvas的Pos Z,还会影响Scale。

当Plane Distance等于Camera的Clipping Planes的Near时 ,相当于Render Mode 是 Screen Space-Overlay 的效果,当Plane Distance等于Camera的Clipping Planes的Far时,Canvas在所有物体的后面。

Sorting Layer,可为UGUI设置画布深度,在下拉菜单中点击“Add Sorting Layer”按钮进入标签和层的设置界面,或者点击导航菜单->edit->Project Settings->Tags and Layers进入该页面。可以点击“+”添加Layer,或者点击“-”删除Layer。画布所使用的Sorting Layer越排在下面,显示的优先级也就越高。

在相同的Layer中区别显示层级关系的设定,相同的Layer中Order in Layer 越高,显示的优先级也就越高。

World Space即世界控件模式。在此模式下,Canvas被视为与场景中其他普通游戏对象性质相同的类似于一张面片(Plane)的游戏物体。在此模式下我们可以手动设置RectTransform数值,来改变Canvas在世界中的位置选择大小等。当所用UI为场景中的一部分时,我们可以使用这种模式。

此处在我使用时World Space模式下,Event Camera为空也可以执行按钮点击事件,摸索中~~~~~

不断学习中。整理出来忘记时看看,有错误的地方感谢指出。

以上就是关于UGUIDrawCall优化全部的内容,包括:UGUIDrawCall优化、Unity UGUI实现游戏关卡选择界面、认识DoTween插件以及通过UGUI制作血条等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/9449630.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-28
下一篇 2023-04-28

发表评论

登录后才能评论

评论列表(0条)

保存