如何用UGUI制作背包系统

如何用UGUI制作背包系统,第1张

可以直接使用EventTriggerListener 组件 不过会覆盖其它事件也可以单独使用接口,不会对其它事件造成影响IPointerEnterHandler 当鼠标进入对象时IPointerExitHandler 当鼠标退出对象时IPointerDownHandler 当鼠标点下对象时IPointerUpHandler 当鼠标抬起时IPointerClickHandler 当鼠标点击时IBeginDragHandler 鼠标开始拖动时IDragHandler鼠标拖动时IEndDragHandler 拖动结束时IScrollHandler 鼠标滚轮时。。。

这个组件能提供组件的最大最小和最合适大小信息,比如Text,Image都是实现了ILayoutElement接口的组件,所以它会自动获取

如果一个RectTransform的物体的其中一个组件实现了ILayoutElement接口,同时这个RectTransform的物体又添加了Layout Element组件, 那么起作用的是Layout Element

Image继承了ILayoutElement,重写了虚方法,如果没有图片就返回0,如果图片类型是九宫或者重复平铺类型,那么就返回图片的原始大小

LayoutGroups是用来控制子对象位置的,实现布局,但它不控制自己;当父对象添加了LayoutGroup,那么子对象的位置不再由子对象自己控制

一共有3种组件如下图, 网格布局(Grid)、水平布局(Horizontal)、垂直布局(Vertical) ,一个游戏物体最多只能添加一个LayoutGroup

水平布局和垂直布局都差不多,区别只是一个排列是水平方向,一个是垂直方向,这里选水平方向来介绍.

  Padding: 内边距,可以设置距离上下左右四个边框的偏移量

  Spacing: 表示cell之间的距离,也就是整个布局中每个单元之间的距离

  Child Alignment: 表示对齐方式

  Control Child Size: 是否控制子物体的大小(新版UGUI水平和垂直布局都添加了这个选项,网格布局依旧自动控制,老版本三个布局组件都默认控制子对象大小)

  Child Force Expand: 表示自适应 宽 高,勾选上后所有子物体会被拉伸以至填充满整个布局容器

Cell Size: 子物体大小

  Start Corner: 子物体排列的角度

  Start Axis: 子物体排列的方向

  Constraint: Flexible灵活的( 按照父物体长宽自动横竖行 )Fixed Column Count 指定列数 Fixed Row Count 指定行数

一般用空物体来作为布局容器(父物体)然后布局容器下就是很多个格子(cell)了,而一个cell下面可能会放很多图片,例如说要有一个格子的背景图片,然后一个物品图片,所以一个cell我们也会用空的gameobject来当父节点。

LayoutControl主要用于控制组件附加物体的RectTransform,ContentSizeFitter就是一个LayoutControl组件,LayoutGroup下的子物体大小已被控制了,会冲突所以添加不了ContentSizeFitter

1.创建一个空物体作为布局容器(父物体),为父物体添加一种布局方式的组件如Grid Layout Group

2.把做好的格子(cell)拖放成Prefab,创建脚本实例化Prefab,把它设置为布局容器的子物体即可(setParent),Layout Group会根据大小自动计算布局,注意这个脚本最好挂在布局容器上

父控件: 指的是添加了LayoutGroups组件的GameObject

控件大小信息: 指的是控件最小大小,最合适(期望)大小,控件最大大小

1.1 LayoutElement负责提供该组件所附加的物体的控件大小的信息,不提供位置信息。只提供大小信息,并且不直接控制自己的大小,由其它组件控制.例如,LayoutControl、LayoutGroups

1.2Text,Image本身就实现了ILayoutElement接口,提供了控件大小的信息,但它的控件大小信息是系统源码提供的;如果想修改Text,Image等控件大小的信息,可以给物体添加LayoutElement组件,系统会读取LayoutElement组件提供的大小

父对象LayoutGroups是如何利用控件提供的大小信息来控制子对象大小和位置,并分配空间的呢?

  首先, 父对象获取控件期望的最小的大小,然后分配最小的大小,即使父控件的空间比子控件的期望大小要小,子控件也一样可以获得它期望的最小值,也就是最小的大小一定能够得到满足.

  其次, 如果父对象还有足够的空间,那就分配控件的最合适(期望)的大小给控件.

  最后, 如果父控件分配完最合适(期望)大小之后,还有额外空间.则按照控件提供的最大(扩展比率)大小,按照比率分配额外的空间给控件.

最大值,它是一个相对数值 ,是0和大于0的数组成。该值的意思是表示父控件的多个子控件分配额外空间的一个比率,最大值设置是在Layout Element的Flexible Width和Flexible Height.

举个栗子: 比如一个父控件下有A、B两个子控件,A的最大值是1,B的最大值是2,则表示父控件剩余的空间平分三分,A占用1份,B占用2份,0表示不占用父控件的多余空间.

总之, 父控件分配子控件的大小是按照先保证子控件满足最小大小的要求;然后,如果父控件还有足够的空间,就分配给子控件按照合适的大小;如果还有额外的空间,则分配给子控件比率空间.

再举个栗子: 有A、B两个子控件,A设置宽高最小值为0,0,期望值为0,0,最大值2,2,B设置宽高最小值为0,0,期望值为100,100,最大值1,1,父控件的大小为160×160.

===》这是一遍尴尬的日志。。因为一开始就是错误的。。所以结局也是错误的。。

GetComponent这个接口在PC上消耗惊人,但是在手机上消耗非常少,大概24Byte。。  

1、首先要说一下GetComponent这个接口的GC消耗

下图中的Other里只做了一件事情:m_Mask=GetComponent()

可以看出,一次调用产生0.6KB的GC,这是我们的背包界面,有200多张Image,光一个Awake产生116KB的GC。

2、UGUI的Image、Text都继承自MaskableGraphic

下图可以看到一堆OnEnable的消耗点开看详细列表,和图一是多么的相似~

然后看源码:

每个Text和Image在显示的时候,都要走一遍这个OnEnable,一次0.5~0.6KB,几乎每个项目里用到这两个控件的地方是成百上千,前面图中的GC如果扣掉这部分的消耗,就是很少很少很少~

OnDisable也是一样的道理,直接看图:

所以为什么UI对象SetActive DeActive会产生那么多的GC,大家想尽歪门邪道去解决这个问题,比如关闭界面把UI挂到其他非canvas节点,移出视野(这种办法有很大概率触发子线程的sorting或者batch异常导致crash)。

我们项目现在使用的办法是给所有UI界面挂一个canvas,需要关闭窗口的时候disable这个canvas,canvas下的粒子系统加一个UIBehavior脚本监听这个canvas是否隐藏去做特殊处理(关闭界面的时候stop这个particlesystem,打开界面的时候play);对于canvas下挂了resorting的子canvas,只能用原来的方式继续用SetActive啦

3、让我们看看哪些UI继承了MaskableGraphic

4、还有另外一个坑,这个接口也是高频率调用,OnEnable和OnDisable的时候会调用,看起来UI发生网格重建的时候也会调用,这个问题我们一开始处理的方式比较粗暴,就是直接将它移到Awake,这样对象生命周期内只发生一次调用,就是图一中看到的现象。但是这样也是不好的,这样就意味着游戏里打开的所有Image、Text都要经历一遍这个GetComponent的洗礼,还是一句话,偶尔几个地方可以忍了,但是所有节目加起来大概有几千个这个控件吧。。

所以最终,我们的解决方案是,(我们游戏里的所有Image、Text是用我们自己改写过的版本,继承了原生的Image和Text),在SDImage和SDText里加一个Mask成员变量,在编辑器模式下设置好这个值……

5、其实有更好的办法

重写MaskableGraphic,重写那几个继承自MaskableGraphic的对象,自己定制一组UI,其实Image和Text就基本够用了。这样前面我说的旁门左道都可以不需要,还可以愉快的Active和DeActive。下个版本我们打算这么干!!


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

原文地址: http://www.outofmemory.cn/bake/11800464.html

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

发表评论

登录后才能评论

评论列表(0条)

保存