UGUI系列-原理分析(Unity3D)(一)

简介: 了解各种不同UI Render Mode

Unity UGUI 原理篇(一):Canvas 渲染模式

目标

了解各种不同UI Render Mode


使用环境与版本

Window 7 Unity 5.2.5


Render Mode

UI渲染的方式,有以下三种

  • Screen Space – Overlay:萤幕空间 – 覆盖
  • Screen Space – Camera:萤幕空间 – 摄影机
  • World Space:世界座标空间

Screen Space - Overlay

网络异常,图片无法展示
|

在此模式下不会参照到Camera,UI直接显示在任何图形之上

  • 1.Pixel Perfect:可以使图像更清晰,但是有额外的性能开销,如果在有大量UI动画时,动画可能会不平顺
  • 2.Sort Order:深度值,该值越高显示越前面

Screen Space - Camera

网络异常,图片无法展示
|

使用一个Camera作为参照,将UI平面放置在Camera前的一定距离,因为是参照Camera,如果萤幕大小、分辨率、Camera视锥改变时UI平面会自动调整大小。如果Scene中的物件(GameObject)比UI平面更靠近摄影机,就会遮挡到UI平面。

  • 1.Render Camera:用于渲染的摄影机
  • 2.Plane Distance:与Camera的距离
  • 3.Sorting Layer:Canvas属于的排序层,在 Edit->Project Setting->Tags and Layers->Sorting Layers 进行新增,越下方的层显示越前面
  • 4.Order in Layer:Canvas属于的排序层下的顺序,该值越高显示越前面

World Space

网络异常,图片无法展示
|

把物体当作世界座标中的平面(GameObject),也就是当作3D物件,显示3D UI

  • 1.Event Camera:处理UI事件(Click、Drag)的Camera,所设定的Camera才能触发事件

参考资料 Unity – Manual: Canvas

docs.unity3d.com/Manual/clas…



Unity UGUI 原理篇(二):Canvas Scaler 缩放核心


目标

  • 1.了解各种不同 UI Scale Mode
  • 2.Pixels Per Unit 每单位像素
  • 3.Canvas Scale Factor 缩放因子
  • 4.Reference Resolution(预设屏幕大小)
  • 5.Screen Size丶Canvas Size 之间的关系与算法


使用环境 与 版本

Window 7

Unity 5.2.4


Canvas Scaler

Canvas Scaler是Unity UI系统中,控制UI元素的总体大小和像素密度的Compoent,Canvas Scaler的缩放比例影响著Canvas下的元素,包含字体大小和图像边界。

Size

  • Reference Resolution:预设萤幕大小
  • Screen Size:目前萤幕大小

网络异常,图片无法展示
|

Canvas Size:Canvas Rect Transform 宽高

网络异常,图片无法展示
|

Scale Factor

docs.unity3d.com/ScriptRefer…

用于缩放整个Canvas,而且调整Canvas Size与Screen Size一样

先来看一段官方代码

CanvasScaler.cs

protected void SetScaleFactor(float scaleFactor)
{
    if (scaleFactor == m_PrevScaleFactor)
        return;
    m_Canvas.scaleFactor = scaleFactor;
    m_PrevScaleFactor = scaleFactor;
}
复制代码

程式码可以看出,Canvas Scaler 透过设定Canvas下的Scale Factor,缩放所有在此Canvas下的元素

当Scale Factor为1时,Screen Size (800600)、Canvas Size(800600),图片大小1倍

网络异常,图片无法展示
|
网络异常,图片无法展示
|

当Scale Factor为2时,Screen Size (800600)、Canvas Size(400300),图片大小2倍

网络异常,图片无法展示
|
网络异常,图片无法展示
|

在当Scale Factor为2时,Scale Factor 会调整整个Canvas 的大小,并让他的大小跟Screen Size一样,运算后Canvas Size放大2倍,刚好等于Screen Size,而底下的图片会放大2倍


UI Scale Mode

Constant Pixel Size Canvas Size 始终等于 Screen Size,透过Scale Factor直接缩放所有UI元素

网络异常,图片无法展示
|

  1. Scale Factor:透过此Factor缩放所有在此Canvas下的元素
  2. Reference Pixels Per Unit:

先介绍图片档设定中的Pixels Per Unit,意思是在这张Sprite中,世界座标中的一单位由几个Pixel组成

网络异常,图片无法展示
|

这边使用的测试图片为原始大小100*100 的图档,这边统称测试图

网络异常,图片无法展示
|

举例来说,场景中有一个11 Cube ,与一个Sprite图片指定为测试图,两者的Transform Scale 都为 1 当 Pixels Per Unit=100,每单位由 100 Pixel组成,Sprite 是100100 Pixels,那 Sprite 在世界座标中大小就会变成 100/100 * 100/100 = 1*1 Unit

网络异常,图片无法展示
|

(左:Cube ,右:Sprite)

当 Pixels Per Unit=10,每单位由 10 Pixel组成,Sprite 是100100 Pixels,那 Sprite 在世界座标中大小就会变成 100/10 * 100/10 = 1010 Unit

网络异常,图片无法展示
|

(左:Cube,右:Sprite)

结论:

  • Unity中一单位等于 100 Pixels
  • 由此可以推导出公式:

Sprite 在世界座标中大小 = 原图大小(Pixels) / Pixels Per Unit

让我们回到 Reference Pixels Per Unit,官方解释是,如果图片档有设定Pixels Per Unit,则会将Sprite 的 1 pixel 转换成 UI 中的 1 pixel


Image.cs

public float pixelsPerUnit
{
    get
    {
        float spritePixelsPerUnit = 100;
        if (sprite)
            spritePixelsPerUnit = sprite.pixelsPerUnit;
        float referencePixelsPerUnit = 100;
        if (canvas)
            referencePixelsPerUnit = canvas.referencePixelsPerUnit;
        return spritePixelsPerUnit / referencePixelsPerUnit;
    }
}
复制代码

上面官方程式码,可以看出 Image 透过 spritePixelsPerUnit / referencePixelsPerUnit 方式算出新的 pixelsPerUnit


Image.cs

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 图片大小时,是把 宽高 / pixelsPerUnit

实作一下,建立一个Canvas参数如下

网络异常,图片无法展示
|

Canvas底下建立一个Image,Sprite设定为测试图,参数如下

网络异常,图片无法展示
|
这边做4种不同的测试:测试方式是修改 Reference Pixels Per Unit 与 Pixels Per Unit 后,点下 Image Compoent 的 Set Native Size来设定图片原始大小,藉此看到图片变化.
网络异常,图片无法展示
|

■ 上表可以看出当数值改变时,图片预设大小也会改变

■ 由此可以推导出公式

UI大小 = 原图大小(Pixels)  /  (Pixels Per Unit / Reference Pixels Per Unit)

Scale With Screen Size: 透过设定的Reference Resolution(预设萤幕大小)来缩放

  1. Reference Resolution:预设萤幕大小
  2. Screen Match Mode:缩放模式

先来看官方的算法


CanvasScaler.cs

Vector2 screenSize = new Vector2(Screen.width, Screen.height);
float scaleFactor = 0;
switch (m_ScreenMatchMode)
{
    case ScreenMatchMode.MatchWidthOrHeight:
    {
        // We take the log of the relative width and height before taking the average.
        // Then we transform it back in the original space.
        // the reason to transform in and out of logarithmic space is to have better behavior.
        // If one axis has twice resolution and the other has half, it should even out if widthOrHeight value is at 0.5.
        // In normal space the average would be (0.5 + 2) / 2 = 1.25
        // In logarithmic space the average is (-1 + 1) / 2 = 0
        float logWidth = Mathf.Log(screenSize.x / m_ReferenceResolution.x, kLogBase);
        float logHeight = Mathf.Log(screenSize.y / m_ReferenceResolution.y, kLogBase);
        float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight);
        scaleFactor = Mathf.Pow(kLogBase, logWeightedAverage);
        break;
    }
    case ScreenMatchMode.Expand:
    {
        scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
        break;
    }
    case ScreenMatchMode.Shrink:
    {
        scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
        break;
    }
}
复制代码

a. Expand(扩大):将Canvas Size进行宽或高扩大,让他高于Reference Resolution,计算如下

scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);

意思是分别算出长宽 ,”Screen Size” 佔了 “Reference Resolution” 的比例,在求小的

举例来说,Reference Resolution为1280720,Screen Size为800600

ScaleFactor Width: 800/1280=0.625

ScaleFactor Height:600/720=0.83333

套用ScaleFactor公式:Canvas Size = Screen Size / Scale Factor

Canvas Width:800 / 0.625 = 1280

Canvas Height:600 / 0.625 = 960

Canvas Size 为 1280*960,高度从720变成了960,最大程度的放大(显示所有元素)

网络异常,图片无法展示
|

b. Shrink(收缩):将Canvas Size进行宽或高收缩,让他低于Reference Resolution,计算如下

scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);

意思是分别算出长宽 ,”Screen Size” 佔了 “Reference Resolution” 的比例,在求大的

举例来说,Reference Resolution为1280720,Screen Size为800600

ScaleFactor Width: 800/1280=0.625

ScaleFactor Height:600/720=0.83333

套用ScaleFactor公式:Canvas Size = Screen Size / Scale Factor

Canvas Width:800 / 0.83333 = 960

Canvas Height:600 / 0.83333 = 720

Canvas Size 为 960*720,宽度从1280变成了960,最大程度的缩小

网络异常,图片无法展示
|

c. Match Width or Height:根据Width或Height进行混合缩放,计算如下

float logWidth = Mathf.Log(screenSize.x / m_ReferenceResolution.x, kLogBase);
float logHeight = Mathf.Log(screenSize.y / m_ReferenceResolution.y, kLogBase);
float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight);
scaleFactor = Mathf.Pow(kLogBase, logWeightedAverage);
复制代码

分别对ScaleFactor Width、Height取对数后,再进行平均混合,那为什麽不直接使用March对Width、Height进行混合呢??,让我们来比较一下

假设Reference Resolution为400300,Screen Size为200600 大小关系是

Reference Resolution Width 是 Screen Size Width的2倍

Reference Resolution Height 是 Screen Size 的0.5倍

看起来会像下图

网络异常,图片无法展示
|

当March为0.5时,ScaleFactor应该要是 1 (拉平)

ScaleFactor Width: 200/400=0.5

ScaleFactor Height:600/300=2

一般混合:

ScaleFactor = March * ScaleFactor Width + March * ScaleFactorHeight

ScaleFactor = 0.5 * 0.5 + 0.5 * 2 = 1.25

对数混合:

logWidth:log2(0.5) = -1

logHeight:log2(2) = 1

logWeightedAverage:0

ScaleFactor:20 = 1

scaleFactor一般混合为1.25,对数混合为1,结果很明显,使用对数混合能更完美的修正大小

Constant Physical Size

透过硬体设备的Dpi(Dots Per Inch 每英吋点数),进行缩放

网络异常,图片无法展示
|


  1. Physical Unit:使用的单位种类

网络异常,图片无法展示
|


2.Fallback Screen DPI:备用Dpi,当找不到设备Dpi时,使用此值


3.Default Sprite DPI:预设的图片Dpi

float currentDpi = Screen.dpi;
float dpi = (currentDpi == 0 ? m_FallbackScreenDPI : currentDpi);
float targetDPI = 1;
switch (m_PhysicalUnit)
{
    case Unit.Centimeters: targetDPI = 2.54f; break;
    case Unit.Millimeters: targetDPI = 25.4f; break;
    case Unit.Inches:      targetDPI =     1; break;
    case Unit.Points:      targetDPI =    72; break;
    case Unit.Picas:       targetDPI =     6; break;
}
SetScaleFactor(dpi / targetDPI);
SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit * targetDPI / m_DefaultSpriteDPI);
复制代码

结论

■ ScaleFactor 为 “目前硬体dpi” 佔了 “目标单位” 的比例

■ ReferencePixelsPerUnit 要与目前的Dpi在运算求出新的值,再传入Canvas中求出大小,公式如下:

新的 Reference Pixels Per Unit = Reference Pixels Per Unit * Physical Unit / Default Sprite DPI

UI大小 = 原图大小(Pixels)  /  (Pixels Per Unit / 新的 Reference Pixels Per Unit) 参考资料 ■ Unity – Manual: Canvas

docs.unity3d.com/Manual/clas…



相关文章
|
C# 图形学
【unity之c#】所以迭代器的原理知识你还清楚吗?
【unity之c#】所以迭代器的原理知识你还清楚吗?
137 0
|
前端开发 图形学 数据安全/隐私保护
|
前端开发 图形学
|
.NET C# 图形学
Unity跨平台的机制原理
首先需要了解的是,Unity3D的C#基础脚本模块是通过Mono来实现的。 什么是Mono? 参考下百度百科:Mono是一个由Novell公司(由Xamarin发起)主持的项目,并由Miguel de lcaza领导的,一个致力于开创.NET在Linux上使用的开源工程。
3535 0
|
测试技术 图形学
【Unity Shader】(五) ------ 透明效果之半透明效果的实现及原理
笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题 【Unity Shader学习笔记】(三) ---------------- 光照模型原理及漫反射和高光反射的实现 【Unity Shader】(四) ------ 纹理之法线纹理、单张纹理及遮罩纹理的实现   前言 相信读者对透明效果都不陌生,因为透明效果是游戏中经常使用的一种效果。
4250 0
|
图形学
【Unity Shader】(三) ------ 光照模型原理及漫反射和高光反射的实现
【Unity Shader】(三) ---------------- 光照模型原理及漫反射和高光反射的实现 【Unity Shader】(四) ------ 纹理之法线纹理、单张纹理及遮罩纹理的实现 【Unity Shader】(五) ------ 透明效果之半透明效果的实现及原理   本文主要参考了冯乐乐老师的《Unity Shader入门精要 》一书,再加上网上一些参考资料而写。
1985 0
|
测试技术 Go 图形学
unity中实现简单对象池,附教程原理
Unity对象池的创建与使用 本文提供全流程,中文翻译。 Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例) Chinar —— ...
2589 0
|
3月前
|
图形学 C#
超实用!深度解析Unity引擎,手把手教你从零开始构建精美的2D平面冒险游戏,涵盖资源导入、角色控制与动画、碰撞检测等核心技巧,打造沉浸式游戏体验完全指南
【8月更文挑战第31天】本文是 Unity 2D 游戏开发的全面指南,手把手教你从零开始构建精美的平面冒险游戏。首先,通过 Unity Hub 创建 2D 项目并导入游戏资源。接着,编写 `PlayerController` 脚本来实现角色移动,并添加动画以增强视觉效果。最后,通过 Collider 2D 组件实现碰撞检测等游戏机制。每一步均展示 Unity 在 2D 游戏开发中的强大功能。
183 6