热搜词: 

ugui怎么制作背包系统,ugui layout 性能

发布:小编

本文目录

ugui layout 性能

一、 UGUI布局系统由 布局元素 (Layout Elements)和 布局控制器 (Layout Controller)组成。

1、布局控制器的基础接口为ILayoutController。

(1)ILayoutController有两个方法,布局系统调用这两个方法的顺序是固定的,首先调用SetLayoutHorizontal,再调用SetLayoutVertical:

A、void SetLayoutHorizontal() :处理水平方向的布局;

B、void SetLayoutVertical() :处理垂直方向的布局。

(2)继承ILayoutController的两个接口:

A、ILayoutSelfController :只控制自己RectTransform的改变;继承ILayoutSelfController的两个类如下:

[1] (内容尺寸适配器) Content Size Fitter  <—— ILayoutSelfController:根据孩子中布局元素的尺寸及自身包含内容的多少控制自身的尺寸;

[2] (纵横比适配器) AspectRatioFitter  <—— ILayoutSelfController:根据自身设置的模式(AspectMode)和比率(Aspect Ratio)以及宽或高调整另一边的大小,不受子元素的影响。

B、ILayoutGroup :控制所有孩子节点的布局变化。这个接口就是我们比较熟悉的接口,平时项目中使用的很多布局组件都继承该接口:

[1] ScrollRect   <—— ILayoutGroup;

[2] GridLayoutGroup   <—— LayoutGroup  <—— ILayoutGroup;

[3] HorizontalLayoutGroup\VerticalLayoutGroup->HorizontalOrVerticalLayoutGroup  <—— LayoutGroup <—— ILayoutGroup

2、所有包含RectTransform组件的GameObject都视为布局元素。

(1)布局元素拥有一些定义自身尺寸的属性:

A、Minimum width:最小宽度;

B、Minimum height:最小高度;

C、Preferred width:如果有充足的空间可以分配的最合适宽度;

D、Preferred height:如果有充足的空间可以分配的最合适高度;

E、Flexible width:灵活宽度,一般是相对于父元素的比例;

F、Flexible height:灵活高度,一般是相对于父元素的比例。

默认情况下,Minimum width、Minimum height、Preferred width、Preferred height均为0,Flexible width、Flexible height都是disabled的。如果想调整这些属性就需要添加继承ILayoutElement接口的组件重写这些值。像我们常用的Text、Image组件都继承了ILayoutElement;同时一些布局控制器自身也继承了ILayoutElement,如:LayoutGroup(包括所有继承LayoutGroup的子类)、ScrollRect;UISystem也创建了一个单独组件LayoutElement继承ILayoutElement,任何一个想重写上述属性的GameObject都可以通过添加LayoutElement组件实现。LayoutElement同时继承了ILayoutIgnorer接口,可以通过设置ignoreLayout决定该GameObject是否被Layout System忽略。

(2)ILayoutElement定义了布局元素用到的属性minWidth、preferredWidth、flexibleWidth、minHeight、preferredHeight、flexibleHeight,

同时定义了两个方法:

void CalculateLayoutInputHorizontal():计算布局元素的minWidth,preferredWidth和flexibleWidth值

void CalculateLayoutInputVertical():计算布局元素的minHeight,preferredHeight和flexibleHeight值

二、布局重建过程

1、首先引起布局重建的元素调用LayoutRebuilder.MarkLayoutForRebuild() ,然后逐层向上递归查找直到找到最上层的布局根节点,这个根节点必须包含继承ILayoutGroup接口、Behaviour类并且isActiveAndEnabled属性为true的组件。然后以这个根节点初始化一个布局重建器LayoutRebuilder,然后将该LayoutRebuilder添加到CanvasUpdateRegistry中m_LayoutRebuildQueue队列中等待处理。调用过程如下:

static void LayoutRebuilder.MarkLayoutForRebuild(RectTransform rect) 

->static void LayoutRebuilder.MarkLayoutRootForRebuild(RectTransform controller) 

->static bool CanvasUpdateRegistry.TryRegisterCanvasElementForLayoutRebuild(ICanvasElement element) 

-> CanvasUpdateRegistry.InternalRegisterCanvasElementForLayoutRebuild(ICanvasElement element) 

-> 加入m_LayoutRebuildQueue队列中。

其中有一个比较特殊的就是ScrollRect,在ScrollRect的OnEnable()方法和SetDirtyCaching()方法中会先调用CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this),再调用LayoutRebuilder.MarkLayoutForRebuild(rectTransform)。与LayoutRebuilder比较,ScrollRect本身也继承了ICanvasElement接口。在Rebuild()函数中,ScrollRect针对CanvasUpdate.Prelayout(布局执行前)和CanvasUpdate.PostLayout(布局执行后)两个阶段实现了自己的执行逻辑。而LayoutRebuilder的Rebuild()函数只针对CanvasUpdate.Layout阶段实现对应的执行逻辑

2、CanvasUpdateRegistry构造函数中会将PeformUpdate方法加入Canvas.willRenderCanvases事件中,然后在渲染所有的Canvas之前,抛出willRenderCanvases事件从而调用PeformUpdate(),在PeformUpdate函数中执行步骤如下:

(1)首先会删除m_LayoutRebuildQueue中所有无效的元素;

(2)然后根据节点的深度(父节点个数越大越靠前)对m_LayoutRebuildQueue排序;

(3)然后分别以CanvasUpdate.PreLayout(布局前),CanvasUpdate.Layout(布局),CanvasUpdate.PostLayout(布局后)的参数顺序调用每一个ICanvasElement元素的Rebuild方法;

(4)然后调用所有ICanvasElement元素的LayoutComplete()方法。针对该方法只有LayoutRebuilder类中实现:将该LayoutRebuilder从LayoutRebuilder的静态对象池s_Rebuilders中删除;

(5)然后清空m_LayoutRebuildQueue队列;

(6)布局结束后调用ClipperRegistry.instance.Cull()遍历所有裁剪组件(继承IClipper接口的组件,如RectMask2D)的裁剪方法PerformClipping()。 

3、下面看一下ICanvasElement布局重建过程的实现,重建的实现就在LayoutRebuilder的Rebuild方法中,源码如下:

如上所示,整个布局分四个步骤:

(1)对该节点下所有有效(继承Behaviour且isActiveAndEnabled为true)的布局元素(即包含继承ILayoutElement组件的节点)执行CalculateLayoutInputHorizontal()计算水平方向布局尺寸。对孩子的遍历顺序由下往上,因为父节点尺寸计算依赖于子节点的尺寸;虽然针对所有布局元素,但是真正实现CalculateLayoutInputHorizontal()方法的只有LayoutGroup相关的组件。

LayoutGroup实现了一个虚方法CalculateLayoutInputHorizontal(),主要就是收集其子节点下所有没有被标记 ignoreLayout 的物体存到m_RectChildren列表中。LayoutGroup的子类重写该函数时都会先调用这个虚函数,然后再执行自己特有的逻辑。

A、 水平布局组件 HorizontalLayoutGroup <—— HorizontalOrVerticalLayoutGroup <—— LayoutGroup

重写CalculateLayoutInputHorizontal()。如上,首先调用LayoutGroup中CalculateLayoutInputHorizontal()方法,然后调用HorizontalOrVerticalLayoutGroup中的CalcAlongAxis(int axis, bool isVertical)方法,CalcAlongAxis()是一个公用方法,可以根据传入的参数确定计算哪个方向的尺寸,其中axis表示方向(0:horizontal 1:vertical),isVertical判断是否一个Vertical Group。CalcAlongAxis()执行过程如下:

① 首先遍历所有孩子节点调用GetChildSizes()方法获取对应方向上的尺寸(min、preferred、flexible);

② 然后根据所有子节点的尺寸计算当前节点对应方向上的尺寸totalMin、totalPreferred、totalFlexible;

③ 调用SetLayoutInputForAxis()方法将第二步计算的值设置对应的向量m_TotalMinSize、m_TotalPreferredSize、m_TotalFlexibleSize;

B、垂直布局组件VerticalLayoutGroup 重写方法如下,实现参考HorizontalOrVerticalLayoutGroup

(2)对该节点下所有布局控制器(即包含继承ILayoutController组件的节点)执行SetLayoutHorizontal()设置水平布局。对孩子的遍历顺序自上而下,因为孩子节点实际的布局依赖于父节点的布局;

A、HorizontalLayoutGroup和VerticalLayoutGroup中的SetLayoutHorizontal()会直接调用HorizontalOrVerticalLayoutGroup.SetChildrenAlongAxis()方法根据布局方向和布局组件类型设置其下子布局元素的位置和尺寸。

B、GridLayoutGroup也重写了SetLayoutHorizontal()方法,直接调用SetCellsAlongAxis(0),针对水平布局实现如图2-4所示,只设置每个子元素的尺寸,而不会改变子元素的位置。

(3)对该节点下所有布局元素(即包含继承ILayoutElement组件的节点)执行CalculateLayoutInputHorizontal()计算垂直方向布局尺寸;对孩子的遍历顺序由下往上;

(4)对该节点下所有布局控制器(即包含继承ILayoutController组件的节点)执行SetLayoutVertical()设置垂直布局。对孩子的遍历顺序自上而下;

从上面的执行顺序还可以看出,Layout System先搞定水平方向的布局然后才去设置垂直方向的布局。

三、布局重建触发因素

1、 以下函数,都会直接或间接的调用LayoutBuilder.MarkLayoutForRebuild()触发布局重建:

(1)OnEnable() :所有继承ILayoutController或ILayoutElement组件(AspectRatioFitter除外)的OnEnable()方法都会都会间接调用LayoutBuilder.MarkLayoutForRebuild();

A、Graphic :OnEnable() -> SetAllDirty() -> ( m_SkipLayoutUpdate变量为false时才会调用 )LayoutBuilder.MarkLayoutForRebuild();

B、LayoutGroup\ContentSizeFitter\LayoutElement\ScrollRect :OnEnable() -> SetDirty() -> LayoutBuilder.MarkLayoutForRebuild()。

(2)OnDisable() :所有继承ILayoutController或ILayoutElement组件的OnDisable()方法都会调用LayoutBuilder.MarkLayoutForRebuild()

(3)OnRectTransformDimensionsChange(): RectTransform尺寸变化时调用。继承自UIBehaviour的ContentSizeFitter、LayoutGroup、ScrollRect、Graphic都重写了该方法,具体调用如下:

A、ContentSizeFitter :OnRectTransformDimensionsChange() ->SetDirty() -> (IsActive()为true才会调用)LayoutRebuilder.MarkLayoutForRebuild();

B、LayoutGroup :OnRectTransformDimensionsChange() ->(isRootLayoutGroup为true时才会调用)SetDirty() ->(IsActive()为true时才会调用)LayoutRebuilder.MarkLayoutForRebuild();

C、ScrollRect :OnRectTransformDimensionsChange() -> SetDirty() ->( IsActive()为true时才会调用 )LayoutRebuilder.MarkLayoutForRebuild();

D、Graphic :OnRectTransformDimensionsChange() ->( gameObject.activeInHierarchy为true时才会调用 )SetLayoutDirty() ->(IsActive()为true时才会调用)LayoutRebuilder.MarkLayoutForRebuild();

(4)OnDidApplyAnimationProperties(): 动画导致属性( 重写OnDidApplyAnimationProperties()方法的类自身所特有的属性 )改变时调用。继承UIBehaviour的Graphic、LayoutGroup、LayoutElement都重写了该方法,具体调用如下:

A、Graphic :OnDidApplyAnimationProperties() -> SetAllDirty() -> ( m_SkipLayoutUpdate变量为false时才会调用 )LayoutBuilder.MarkLayoutForRebuild()。Graphic本身是个抽象类,不能当作组件直接添加,而继承自Graphic的Image\Text( ——> MaskableGraphic  ——> Graphic)组件的一些特有属性,如图3-1红框内标记,当动画改变Text的这些属性时会调用OnDidApplyAnimationProperties()函数。如图3-2红框内标记,当动画改变Image的这些属性时会调用OnDidApplyAnimationProperties()函数。

B、LayoutGroup :OnDidApplyAnimationProperties() -> SetDirty() -> ( IsActive()为true时才会调用 )LayoutBuilder.MarkLayoutForRebuild()

如图3-3、3-4、3-5分别是VerticalLayoutGroup、HorizontalLayoutGroup,当动画改变VerticalLayoutGroup的这些属性时会调用OnDidApplyAnimationProperties()函数。

C、LayoutElement :OnDidApplyAnimationProperties() -> SetDirty() -> ( IsActive()为true时才会调用 )LayoutBuilder.MarkLayoutForRebuild()

如图3-6所示,当动画改变LayoutElement的这些属性时会调用OnDidApplyAnimationProperties()函数。

(5)OnBeforeTransformParentChanged() :父节点transform属性变化前调用。继承自UIBehaviour的Graphic和LayoutElement类都重写了该方法:

A、Graphic :OnBeforeTransformParentChanged() -> SetAllDirty() -> LayoutBuilder.MarkLayoutForRebuild()

B、LayoutElement :OnBeforeTransformParentChanged() -> SetDirty() -> LayoutBuilder.MarkLayoutForRebuild()

2、 某些组件特有属性变化或函数调用:

(1)RectTransform 中导致布局变化的属性:Width、Height、Scale、以及锚点变化;

(2)Text 中导致布局变化的属性:text、alignment、fontSize、horizontalOverflow、verticalOverflow、lineSpacing、fontStyle、resizeTextForBestFit、resizeTextMinSize、supportRichText、resizeTextMaxSize、font;方法:FontTextureChanged()。

(3)Image 中导致布局变化的属性:sprite、overrideSprite;方法:SetNativeSize()、OnCanvasHierarchyChanged()。其中OnCanvasHierarchyChanged()只有在canvas.referencePixelsPerUnit变化且Image类型为Sliced或Tiled时才会调用SetLayoutDirty()

(4)LayoutGroup 中导致布局变化的属性:padding、childAlignment;方法:OnTransformChildrenChanged()

(5)GridLayoutGroup 中导致布局变化的属性:startCorner、startAxis、cellSize、spacing、constraint、constraintCount

(6)HorizontalOrVerticalLayoutGroup 中导致布局变化的属性:spacing、childForceExpandWidth、childForceExpandHeight、childControlWidth、childControlHeight、childScaleWidth、childScaleHeight

(7)ContentSizeFitter 中导致布局变化的属性:horizontalFit、verticalFit

(8)LayoutElement 中导致布局变化的属性:ignoreLayout、minWidth、minHeight、preferredWidth、preferredHeight、flexibleWidth、flexibleHeight、layoutPriority

(9)ScrollRect 中导致布局变化的属性:verticalScrollbar、horizontalScrollbar、horizontalScrollbarVisibility、verticalScrollbarVisibility、viewport

四、布局重建分析工具

1、第三节我们总结了一些可能导致布局重建的原因,游戏中尽量避免执行这些操作以减少布局重建,同时尽可能少的使用Layout相关组件以减少CPU时间的占用。通过第二节对重建过程的分析,我们知道每个引起布局重建的元素的元素都会先加入CanvasUpdateRegistry中m_LayoutRebuildQueue队列中,所以我们可以通过反射的方式来获取游戏中引起布局重建的元素(引用

),脚本如下:

public class CheckRebuildElement:MonoBehaviour

{

       IList m_LayoutRebuildQueue;

        privatevoid Awake()

        {

           System.Type type = typeof(CanvasUpdateRegistry);

           FieldInfo field = type.GetField("m_LayoutRebuildQueue",BindingFlags.NonPublic | BindingFlags.Instance);

           m_LayoutRebuildQueue = (IList)field.GetValue(CanvasUpdateRegistry.instance);

        }

        privatevoid Update()

        {

            for(int j = 0; j ugui怎么制作背包系统,ugui layout 性能图1

unity异步加载进度条

最简单的方法就是通过点击菜单GameObject->UI->Slider创建一个可以拖动的Slider,然后根据自己需要,去掉不需要的组件。

另一种办法就是自己定义一个:首先点击GameObject->UI->Image创建一个作为进度条背景的图。然后选中创建的Image,再次点击GameObject->UI->Image创建一个作为进度显示的图。设置好它们的大小尺寸,把第二次创建的Image,作为第一个Image的子物体,并在第一个创建的Image上添加Slider组件。设置Slider组件的FillRect为第二次创建的Image。

ugui怎么制作背包系统,ugui layout 性能图2

unity togglegroup 当前选中的

参考

Unity 3D UGUI Toggle用法教程

Is On :用来表示Toggle当前开关状态,勾选为开;

Graphic :控制Toggle组件开关图片的显示隐藏,默认选取Checkmark,就是那个勾勾图片;

挂上这个脚本,会发现事件中有一个带参数的,还有一个不带参数的:

虽然ListenInFunction是带参数的,但是却要选上面那个绿色不带参数的,刚开始我是懵逼的…………

参照 UGUI中Button和Toggle 添加动态事件 得到了解释:

选下方红色的静态方法,其实UI中会多出一个checkbox,可以指定传入的参数:

当然也可以传入其它类型,比如

参考

Unity UGUI入门组件整理(三)【Toggle组件与Toggle Group组件】

假如初始状态是Toggle3被勾选,当我点击Toggle1勾选时,打印如下:

这样就可以根据名字和选中状态来做逻辑处理了。

是否允许不打开任何开关?如果启用此设置,则按下当前打开的开关会将其关闭,因此没有任何开关处于打开状态。如果禁用此设置,则按下当前打开的开关将不改变该开关的状态。

这个说的有点绕,并且有些博客上还有种错误的说法,就是这个开关能允许Group多选。其实测试一下就知道了,在勾选Allow Switch off时,点击当前选中的Toggle,可以将当前Toggle改成未选择状态,此时整个Group中所有的Toggle都会处于未选择状态。而如果没勾选Allow Switch off,就达不到这个效果,会强迫你必须选一个。

在官方文档中,描述ToggleGroup叫开关组:

UGUI学习笔记(五) ToggleGroup制作选择题单选框

ugui怎么制作背包系统,ugui layout 性能图3

ug1926平面自适应模式

UGUI自适应设置:

1:新建个Canvas,下面不要有子物体,然后选中Canvas进行设置

ugui怎么制作背包系统,ugui layout 性能图4

设置完成就能够实现屏幕自适应,可以测试下,比较完美。

具体总结:ugui自适应,依靠的是Canvas下的组件:Canvas Scaler

Canvas Scaler指的是UI Canvas整体的缩放比例,这个值在Canvas 的Render Mode为 Screen Space – Overlay 的情况下默认为1,在 Canvas 的 Render Mode 为 Screen Space – Camera 的情况下则不一定为1,会受到渲染UI的摄像机 参数影响(如果为Perspective 相机,会受到 Field Of View影响,如果是Orthographic相机,会受到 Size影响)。

Screen Match Mode—Match Width Or Height

Match

Match是一个滑条,拉在最左时是Width ,最右时是Height,中间则是按比例混合。

当处于最左边时,屏幕高度对于UI大小完全没有任何影响,只有宽度会对UI大小产生影响。假设宽度为Reference Resolution宽度的x倍,则UI整体缩放为Reference Resolution状态下的x倍。也就是说只有宽度等于Reference Resolution宽度时,才能做到pixel perfect,否则像素就会有拉伸

当处于最右边时,与上述情况正好相反,决定整体缩放值的是高度,而宽度则没有任何影响

处于中间某处时,对上述两者的影响进行权重加成

Screen Match Mode—Expand

当屏幕分辨率大于参考分辨率时,选择变化较小的一个方向(横向还是纵向),作为放大Canvas Scale的标准,另一方向上的变化则是在整体缩放以后再进行补偿性的变化。此举旨在减少扩大分辨率时由于非等比扩大而对UI整体布局造成影响。适合制作较小标准尺寸,扩充到较大屏幕。

例如:Reference Resolution为800600,(假设此时Canvas Scale为(1,1,1))。如果实际分辨率为800800,那么Canvas Scale还保持为(1,1,1)因为宽度没有发生变化,只是单纯的高度增加了200。所以势必对布局造成一定得影响。1000600的情况也是一样,Canvas Scale没有变化,只是单纯宽度增加了200。但如果实际分辨率变为1000800,那么Canvas Scale就变成(1.25,1.25,1.25)。因为宽度是参考分辨率宽度的1.25倍,高度是1.33倍,那么取较小的1.25。 这个1.25倍的意义是:整体Canvas渲染放大1.25倍,横向或纵向的变化超过了1.25倍,则靠拉伸Canvas来变化,此时因为这部分变化,可能会对布局产生一些相对较小的影响,例如相对位置、某些元素的长宽比。

Screen Match Mode—Shrink

和Expand类似,但是更适合于缩小的情形。它会在屏幕尺寸缩小时,通过缩小CanvasScale尽量减少由于非等比缩小对布局产生的影响。按照影响较小的一个方向缩小的比例去缩小CanvasScale,然后再通过变形调整另外一个方向

总结:

ugui怎么制作背包系统,ugui layout 性能图5

以上就是关于ugui怎么制作背包系统,ugui layout 性能的全部内容,以及ugui怎么制作背包系统的相关内容,希望能够帮到您。

大家都在看

查看更多综合百科