文章目录
UGUI 源代码
之前以为Unity 收了NGUI后,重新开发UI,提供的UGUI是引擎层的(C++实现),其实不是。
ContentSizeFitter:
该组件可以帮我们在空间内容发生变化导致空间尺寸改变时,重新计算RectTransform。
但在复杂的UI应用中,可能我们给文本赋值后,没有正确地计算RectTransform,那么我们需要手动触发计算:
RectTransform rt;
UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(rt);
RectTransform
改变RectTransform的top
GetComponent<RectTransform>().offsetMax = new Vector2(GetComponent<RectTransform>().offsetMax.x, top);
改变RectTransform的bottom
GetComponent().offsetMin = new Vector2(GetComponent().offsetMin.x, bottom);
改变RectTransform的width,height
GetComponent<RectTransform>().sizeDelta = new Vector2(width, height);
改变RectTransform的pos
GetComponent<RectTransform>().anchoredPosition3D = new Vector3(posx,posy,posz);
GetComponent<RectTransform>().anchoredPosition = new Vector2(posx,posy);
更新子元素前后关系
RectTransform.SetSiblingIndex,越小,会在Hierarchy的上面,会先渲染。
Text
获取文本显示的宽高
Text txtObject;
String str;
TextGenerationSetting settings = txtObject.GetGenerationSettings(Vector2.zero);
Vector2 sz = Vector2.zero;
sz.x = txtObject.cachedTextGeneratorForLayout.GetPreferedWidth(str, settings);
sz.y = txtObject.cachedTextGeneratorForLayout.GetPreferedHeight(str, settings);
获取文本mesh构造信息
Text txtObject;
String str;
TextGenerationSetting settings = txtObject.GetGenerationSettings(Vector2.zero);
UICharInfo[] arrCh = txtObject.cachedGeneratorForLayout.Populate(str, settings);
UICharInfo存储了构造Mesh需要的数据
获取文本在RectTransform内显示的行数(宽度固定)
遍历UICharInfo,累计UICharInfo.charWidth,如果宽度超过RectTransform.sizeDelta.x,则换行。
字库文件过大
可以用FontCreator来精简,合并字库。合并后还能能带来额外的提升合批效率的好处
描边和阴影
描边会增加4次绘制(上下左右),阴影一次
静态字体
对于在UI上显示的文本,要显示的不同文字的个数是确定的(不像聊天,用户可以输入),所以可以用SDF来降低字库大小,可以使用TextMeshPro插件。
字体花屏
对于动态字体,UGUI会创建字体渲染缓冲贴图,将要显示的字体绘制在上面,再通过该贴图绘制到UI上。该贴图最大是4096x4096(根据平台支持最大贴图尺寸)。当贴图到达最大尺寸,且有新文字不再缓冲贴图上,则会刷新该贴图,将最近不用的文字删除,这时会造成字体花屏,尤其是帧速率慢时(比如加载时)。
解决办法:
监听字体刷新事件:Font.textureRebuilt
发现刷新时,将UI中所有Text的刷新一遍:Text.FontTextureChanged()
public class CheckFontTextureRebuilt : MonoBehaviour
{
private Font _changedFont;
void Start()
{
Font.textureRebuilt += (Font font)=>{_changedFont = font};
}
void Update()
{
if(_changedFont != null)
{
Text[] allText = GameObject.FindObjectsOfType<Text>();
if(allText != null)
{
foreach(Text text in allText)
{
if(text.font == _changedFont)
text.FontTextureChanged();
}
}
_changedFont = null;
}
}
}
基础控件事件
Button
- 事件监听:Button.onClick.AddListener
Toggle
- 事件监听:Toggle.onValueChanged.AddListener
- 设置选中状态:Toggle.isOn
Slider
- 事件监听:Slider.onValueChanged.AddListener
- 设置位置:Slider.value
InputField
- 验证输入字符:onValidateInput,发生在onValueChanged之前,可以进行验证修改。
- 监听输入字符:onValueChanged,输入框内容发生改变。
Sprite 及图集
打包设置
- 在Project窗口内,右键鼠标->Create->Sprite Atlast,创建Atlas
- 选中创建的Atlas,在Objects For Packing列表,点+号,添加,并将图标目录拖上来
- 在Project Settings->Editor的Sprite Packer的Mode,选择always enabled
加载图集
SpriteAtlas spriteAtlas = Resources.Load<SpriteAtlas>("SpriteAtlas");
transform.Find("icon").GetComponent<Image>().sprite = spriteAtlas.GetSprite("ToolTip_Bg_0");
//sprite
Sprite[] spriteArray = new Sprite[spriteAtlas.spriteCount];
//spriteArray得到数组
spriteAtlas.GetSprites(spriteArray);
ScrollRect
如果控件没有Image组件,则Mask不起作用
重新打开时,滚动到顶端:scrollRect.content.localPosition = Vector2.zero;
Vertical/Horizental Layout Group
Control Child Size 自动计算尺寸
官方文档解释是控制子元素尺寸,实际应用中,当子元素尺寸发生变化,需要选中这个,该Group尺寸才会自动更新,否则只能SetActive(false) ->SetActive(true)才正确。
要加入Content Size Fitter组件来计算尺寸
UI粒子
UI与粒子的遮挡关系
可以把不同的UI及粒子,放到不同的Canvas里,并设定Canvas和粒子的参数,来实现UI与UI之间,UI与粒子之间的渲染排序:
canvas.overrideSorting = true;
canvas.sortingOrder = 1;
particle.sortingOrder = 1;
粒子裁剪
如果我们希望粒子旨在我们的RectTransform内显示,超出部分裁掉,可以修改粒子的shader,将RectTransform的4个角传递给shader,对超出的像素,不进行渲染。
粒子缩放
如果屏幕分辨率发生变化,UGUI会自动对UI元素进行缩放,但是粒子需要我们自己处理。
根据设计分辨率宽高比,和当前分辨率宽高比,计算缩放
// 设计宽高比
float designAspectRatio = designWidth / designHeight;
// 屏幕宽高比
float screenAspectRatio = screen.width/screen.height;
// 如果屏幕宽高比,小于设计宽高比(屏幕在变高了)
if(screenAspectRatio < designAspectRatio)
{
float scale = screenAspectRatio / designAspectRatio;
particle.transform.localScale = scale * particle.firstLocalScale;
}
else
{
particle.transform.localScale = particle.firstLocalScale;
}
UI挂粒子
粒子的资源引用关系比较复杂,所有不要直接将特效挂在UI对象上,而是保存预制名字,运行时加载。
小地图优化
小地图一般时美术把整张地图画在一张大图上,之渲染一部分。但是如果用裁切,虽然被裁切的地方没有渲染出来,但它还是执行渲染了,这其实拉高了填充率。其实我们可以创建一个小的Image,移动时修改UV 坐标,能达到相同的效果。