0 楔子
最近想换工作,在准备面试,突然想起了三年前一个没有答上来的问题,就是Unity的UI有几种适配模式,都是什么意思。当时支支吾吾半天没讲出来,今天必须搞懂它。
1 Canvas组件
我们知道,Unity里的UI元素都要绘制在Canvas上,那么就先来看Canvas是个什么东西:
如上图所示,Canvas 渲染模式有三种:Screen Space-Overlay、Screen Space-Camera、World Space。下面就分别进行进行解析:
1.1 World Space —— 世界模式
属性****功能Event Camera 响应事件的相机Sorting Layer画布的深度,指定了相机的渲染顺序Order in Layer值越大,该UI越显示在前面Addtional Shader Channels附加着色通道,决定Shader可以读取哪些相关数据,比如 法线、 切线 等数据。
在世界空间渲染通常用于显示人物头顶的血条,NPC头上的名字等等。这种情况下,UI位于世界坐标系中,我习惯称它为3DUI,3DUI是游戏中负责渲染3d模型的相机来渲染的,如果要做适配,也是针对3d相机适配,通常都是调节下fov,来保证不同长宽比的屏幕看到的画面大致一样。并不需要针对3dui做适配。
1.2 Screen Space-Overlay —— 屏幕空间覆盖模式
属性****功能Pixel Perfect使UI元素像素对应,效果就是边缘清晰不模糊Sort Order多个Canvas时,数值越大越后渲染。值大的画布,会挡住值小的Target Display目标显示器,如果有多个屏幕的话可以选择Addtional Shader Channels附加着色通道,决定Shader可以读取哪些相关数据,比如 法线、 切线 等数据。
这个屏幕空间覆盖模式渲染模式,就是表示不管有没有相机去渲染场景,Canvas下的所有UI永远位于屏幕的前面,覆盖掉渲染场景显示的元素。
1.3 Screen Space-Camera —— 相机模式
属性****功能Pixel Perfect使UI元素像素对应,效果就是边缘清晰不模糊Render Camera渲染的相机Sort Order多个Canvas时,数值越大越后渲染。值大的画布,会挡住值小的Order In LayerCanvas属于的排序层,在 Edit->Project Setting->Tags and Layers->Sorting Layers 进行新增,越下方的层显示越前面Plane Distance Canvas与相机之间的距离
这种渲染模式 适用于场景模型太多太大,在调整UI的时候挡住UI,让UI和渲染的相机移动到比较远的位置,就可以避免遮挡。并且Canvas 和 摄像机之间有一定的距离 , 可以在摄像机和 Canvas之间放置一些模型或粒子特效。
Screen Space-Camera和Overlay很相似,不同点就在于,Overlay只会根据屏幕的尺寸、分辨率和CanvasScaler的设置,来自动调节画布长宽和缩放。而使用Camera的画布除了受上述因素影响,还会额外受到Camera参数设置影响。
Overlay的性能也会稍微好点,使用Overlay时,UI元素会简单粗暴的绘制在最上层,Unity不需要考虑剔除和排序。但这点性能损耗也无伤大雅,大家知道就好。
2 Canvas Scaler
我们可以观察到,Canvas物体上面,除了Canvas组件,还有一个组件叫做Canvas Scaler,这个组件就是专门来调节Canvas大小的,以达到UI适配的目的。如下图所示:
Canvas 缩放模式有三种:Constant Pixer Size、Scale With Screen Size、Constant Physical Size
下面就分别进行进行解析:
2.1 Constant Pixer Size —— 恒定像素
属性****功能Scale Factor 缩放因子Reference Pixels Per Uit
单位面积像素数量,默认100
Scale Factor参数
首先,来看官方代码对于这个参数的设置:
protected void SetScaleFactor(float scaleFactor)
{
if (scaleFactor == m_PrevScaleFactor)
return;
m_Canvas.scaleFactor = scaleFactor;
m_PrevScaleFactor = scaleFactor;
}
用代码可以看出来,Canvas Scaler 透过设定Canvas下的Scale Factor参数来缩放所有在此Canvas下的UI元素的大小,下面就举个例子说明一下。
例子:
当前分辨率为1920 X 1080,将Scale Factor设为1:
Canvas的长宽等于整成的屏幕的长宽(1920 X 1080),缩放是1倍。
将ScaleFactor设置为2:
Canvas的长宽变成了缩减了一半,缩放变成了2倍。
而图片大小也变成了原来的2倍大。
假如我们维持 ScaleFactor 不变,使用不同的分辨率进行游戏,又会怎样呢?
下面是ScaleFactor为1时,分辨率为1280x720的图片大小:
我们将分辨率调成100x100:
可以观察到,图片本身大小没有变化。但是已经充满了整个屏幕。也就是说,在这种适配模式下,如果我们想将图片等比缩小,就需要自己手动调节ScaleFactor。
Reference Pixels Per Unit参数
这里,我们详细的介绍一下Constant Pixer Size模式下的Reference Pixels Per Unit属性跟图片的Pixels Per Unit的关系。
举个例子
导入项目一张图片,将图片的Pixels Per Unit设置为100:
场景中有一个标准大小的Cube和一个标准大小的Sprite,两者的缩放比例都为1,大小一致:
当图片的Pixels Per Unit为100,每单位由100Pixels组成,那么这个Sprite在世界坐标就是
图片大小 = (100/100) =1 Unit
然后,将图片的Pixels Per Unit设置为10:
图片大小 = (100/10) =10 Unit
结论:
- Unity中一单位等于 100 Pixels
- 公式:Sprite的大小 = 原图大小(Pixels)/ Pixels Per Unit
让我们回到 Reference Pixels Per Unit,官方解释是,如果图片档有设定Pixels Per Unit,则会将Sprite 的 1 pixel 转换成 UI 中的 1 pixel,让我们来看一下官方代码:
public float pixelsPerUnit
{
get
{
float spritePixelsPerUnit = 100;
if (sprite)
spritePixelsPerUnit = sprite.pixelsPerUnit;
float referencePixelsPerUnit = 100;
if (canvas)
referencePixelsPerUnit = canvas.referencePixelsPerUnit;
return spritePixelsPerUnit / referencePixelsPerUnit;
}
}
public override void SetNativeSize()
{
if (overrideSprite != null)
{
float w = overrideSprite.rect.width / pixelsPerUnit;
float h = overrideSprite.rect.height / pixelsPerUnit;
rectTransform.anchorMax = rectTransform.anchorMin;
rectTransform.sizeDelta = new Vector2(w, h);
SetAllDirty();
}
}
上面的代码,可以看出 Image 是通过 spritePixelsPerUnit / referencePixelsPerUnit 方式算出新的 pixelsPerUnit大小。
在设定 Image 图片大小时,是把 宽高 / Pixels Per Unit。
实测一下,建立一个Canvas参数如下:
Canvas下面新建一个Image,参数如下:
通过修改Canvas Scaler的Reference Pixels Per Unit参数 与 图片的Pixels Per Unit参数,来做4组测试,然后来看图片的不同变化:
■ 上表可以看出当数值改变时,图片预设大小也会改变
■ 由此可以推导出公式
UI大小 = 原图大小(Pixels) / Pixels Per Unit * Reference Pixels Per Unit
版权归原作者 萌新求带 所有, 如有侵权,请联系我们删除。