• 浅谈Unity UI适配(一)


    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参数
    首先,来看官方代码对于这个参数的设置:

    1. protected void SetScaleFactor(float scaleFactor)
    2. {
    3.     if (scaleFactor == m_PrevScaleFactor)
    4.         return;
    5.  
    6.     m_Canvas.scaleFactor = scaleFactor;
    7.     m_PrevScaleFactor = scaleFactor;
    8. }

    用代码可以看出来,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,让我们来看一下官方代码:

    1. public float pixelsPerUnit
    2. {
    3.     get
    4.     {
    5.         float spritePixelsPerUnit = 100;
    6.         if (sprite)
    7.             spritePixelsPerUnit = sprite.pixelsPerUnit;
    8.  
    9.         float referencePixelsPerUnit = 100;
    10.         if (canvas)
    11.             referencePixelsPerUnit = canvas.referencePixelsPerUnit;
    12.  
    13.         return spritePixelsPerUnit / referencePixelsPerUnit;
    14.     }
    15. }
    16. public override void SetNativeSize()
    17. {
    18.     if (overrideSprite != null)
    19.     {
    20.         float w = overrideSprite.rect.width / pixelsPerUnit;
    21.         float h = overrideSprite.rect.height / pixelsPerUnit;
    22.         rectTransform.anchorMax = rectTransform.anchorMin;
    23.         rectTransform.sizeDelta = new Vector2(w, h);
    24.         SetAllDirty();
    25.     }
    26. }

    上面的代码,可以看出 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
  • 相关阅读:
    C# 连接SQL Sever 数据库与数据查询实例 数据仓库
    public,private,protected,default的区别
    时间复杂度、空间复杂度
    SpringBoot 打包与运行
    java计算机毕业设计校园共享单车系统源程序+mysql+系统+lw文档+远程调试
    【计算机思维】01.计算机思维框架
    NLP(1)--NLP基础与自注意力机制
    [附源码]Python计算机毕业设计SSM开放实验室管理系统(程序+LW)
    详解AUTOSAR:AUTOSAR方法论(理论篇—3)
    Centos安装RabbitMQ,JavaSpring发送RabbitMQ延迟延时消息,JavaSpring消费RabbitMQ消息
  • 原文地址:https://blog.csdn.net/qq_17758883/article/details/125883295