找回密码
 立即注册
查看: 378|回复: 0

Unity UGUI 手机屏幕适配方案

[复制链接]
发表于 2022-11-11 20:33 | 显示全部楼层 |阅读模式
#### 一、手机中 SafeArea 的定义

在刘海屏幕没有出现的时候,UGUI的适配方案是非常简单的,只需要直接将UGUI的内容全部显示到屏幕上,然后根据节点布局来调整对应的锚点(*Anchors*),这样就完成了UGUI在手机屏幕的适配。随着全面屏和刘海设计的出现,一些手机厂商跟随果厂的步伐,也开始设计异形屏幕了,结果现在的手机屏幕不再是一个完整的矩形了,适配的时候就需要注意了。在这种情况下,就出现了屏幕安全区域(*SafeArea*)的定义。

>安全区域指的是一个可视窗口范围,处于安全区域的内容不受圆角(corners)、齐刘海(sensor housing)、小黑条(Home Indicator)的影响。

看看苹果官方给的这2张图就明白了,中间蓝色区域即为安全区域



#### 二、UGUI适配思路

为了让UGUI的元素完美的适配到不同的手机型号屏幕中,我们需要做到以下几点:

1. 保证让所有的UI元素完整的显示到屏幕上,这些UI元素不能被裁切;
2. 全屏背景图片需要特殊处理,为了不让图片被拉伸,需要对图片进行缩放后裁切;
3. 在异型屏的机型中,保证所有的可交互UI元素不被那些孔或者刘海挡住;
4. 支持在编辑器中,直接预览查看不同机型屏幕下的UI界面效果;

下面我们来对各个点进行分析解决:

##### 1、完全显示UI元素

  *Canvas* 组件一般情况下,将 *RenderMode* 设置为 *ScreenSpace-Camera*  模式,同时需要添加一个 UI 专用的摄像机。这样可以直接将一些模型通过这个 UI 相机直接渲染到 UI 层,当然也可以使用 *ScreenSpace-Overlay* 模式,这里没什么强制性的要求,主要看项目需求。

在所有*Canvas* 的根画布上挂在 *CanvasScaler* ,全局只需要一个 *CanvasScaler* 组件,它会自动影响 CanvasRoot 下的所有 Canvas。这个 *CanvasScaler*  组件才是正真起到适配作用的组件,它会进行自动缩放*Canvas*。为了跟随屏幕的尺寸来进行适配,我们需要将 *UIScaleMode* 设置为 *ScaleWithScreenSize* 。然后设置项目在开发模式下的分辨率大小,*ScreenMathMode* 直接设置为 *Expand*,这个模式下可以将所有的UI元素显示在屏幕中,并且保证UI元素不超出屏幕被裁剪。关于 *CanvasScaler* 的具体原理,这里不再展开叙述了,可以直接看 [UGUI源码](JezzyChao/uGUI)。

###### 基本适配规则
* ScreenRatio:手机设备屏幕处于横向状态下的宽度和高度之比。
* 高度适配:以屏幕高度为基准,将开发分辨率乘以一定的缩放值,使开发分辨缩放后的高度和实际屏幕高度相同。高度一致的情况下,宽度就有2种情况了。
    1. 开发分辨率的 ScreenRatio > 实际分辨率的 ScreenRatio,如下图所示,蓝色区域 Left 和 Right 中的UI元素是无法显示在实际屏幕中的,被裁剪了。


    2. 开发分辨率的 ScreenRatio < 实际分辨率的 ScreenRatio,如下图所示,黄色区域的开发分辨率完全处于红色区域的实际屏幕分辨率内部,UI元素能够完全现在在屏幕内。


* 宽度适配:以屏幕宽度为基准,将开发分辨率乘以一定的缩放值,使开发分辨缩放后的宽度和实际屏幕宽度相同。
    1. 开发分辨率的 ScreenRatio > 实际分辨率的 ScreenRatio,如下图所示,黄色区域的开发分辨率完全处于红色区域的实际屏幕分辨率内部,UI元素能够完全现在在屏幕内。


    2. 开发分辨率的 ScreenRatio < 实际分辨率的 ScreenRatio,如下图所示,蓝色区域中的UI元素是无法显示在实际屏幕中的,被裁剪了。


###### ScreenMathMode.Expand 的适配策略

1. 开发分辨率的 ScreenRatio > 实际分辨率的 ScreenRatio:先使用**宽度适配**进行缩放,使开发分辨率内的全部UI元素都能显示在实际屏幕内。然后再对**高度进行补偿**,简单说就是**拉伸适配后开发分辨率的高度**,使其填充满上下两侧的待补偿区域,如果不拉伸上下两侧就会是黑边了。如果有UI元素节点的锚点钉在了上下两侧,则其对应的位置也会被拉伸。
2. 开发分辨率的 ScreenRatio < 实际分辨率的 ScreenRatio:先使用**高度适配**进行缩放,使开发分辨率内的全部UI元素都能显示在实际屏幕内。然后再对**宽度进行补偿**,简单说就是**拉伸适配后开发分辨率的宽度**,使其填充满左右两侧的待补偿区域,如果不拉伸左右两侧就会是黑边了。如果有UI元素节点的锚点钉在了左右两侧,则其对应的位置也会被拉伸。
3. 开发分辨率的 ScreenRatio == 实际分辨率的 ScreenRatio:不用多解释了。

##### 2、全屏背景图片的处理

全屏背景图片的适配,我们不能直接将图片节点的锚点布局设置为平铺模式(`RectTransform.anchorMin = Vector3.zero; RectTransform.anchorMax = Vector3.one`),这样会使背景图片在不同屏幕分辨率下被拉伸,导致图片失真。

正确的做法应该是,比较图片纹理的分辨率和当前运行平台的分辨率,动态缩放图片纹理,使图片纹理能够覆盖整个屏幕,并且保证图片不被拉伸。下面我们来分析一下具体的原理。

为了方便后续分析,我们先给出一些定义:

* **ScreenRatio**:手机设备屏幕处于横向状态下的宽度和高度之比;
* **ReferenceRatio**:开发模式下定义的设计分辨率,所有元素的设计都是按照这个分辨率来进行的;
* **TextureRatio**:图片纹理的宽度和高度之比,一般情况下全屏纹理的分辨率和 *ReferenceRatio* 一致,下面我们默认这一规则;

显然,两个参数进行比较可以分为三种情况:

1. *ScreenRatio*==*ReferenceRatio*:运行平台的屏幕分辨率和设计分辨率一致的情况下,最简单不过了,图片完全就不需要操作,保持原始分辨率就可以了。
2. *ScreenRatio*>*ReferenceRatio*:假定两者的高度相同,则运行平台的屏幕宽度大于设计分辨率的宽度。如果图片保持不变,则会在屏幕左右两侧留有黑边,无法保证纹理填充满屏幕。所以我们需要以宽度为基准对纹理进行缩放,消除黑边。在纹理放大之后,它的高度会超过运行平台屏幕的高度,会被裁切一部分,完全可以接受这种情况。适配后的效果如下图:



3. *ScreenRatio*<*ReferenceRatio*:假定两者的宽度相同,则运行平台的屏幕高度大于设计分辨率的高度。如果图片保持不变,则会在屏幕上下两侧留有黑边,无法保证纹理填充满屏幕。所以我们需要以高度为基准对纹理进行缩放,消除黑边。在纹理放大之后,它的宽度会超过运行平台屏幕的宽度,会被裁切一部分,完全可以接受这种情况。适配后的效果如下图:



知道原理之后,我们就可以很轻松的来动态的计算纹理的缩放值,可以自己写脚本,也可以给通过 Unity 提供的内置组件 *AspectRatioFitter*  来完成背景纹理的适配。将 *AspectRatioFitter*  的 *AspectMode* 设置为 *EnvelopeParent* 模式,然后设置 *AspectRatio* 的值为纹理的分辨率宽高比。如下图这般设置即可:



##### 3、异型屏幕的处理

敲黑板,敲黑板,这一块是重点啊,考试必考。

根据手机屏幕中 SafeArea 的定义,我们将手机屏幕切分为9宫格的样式,如下图:



SafeArea 这块区域是一块矩形区域,不存在任何遮挡。其他剩余的区域均为非安全区域,可能存在遮挡的情况。这9块区域,是根据手机屏幕 SafeArea 尺寸大小动态变化的。例如:在直角刘海屏中,RT、Right和RB的区域宽度可能为0;在圆角刘海屏中,它们的宽度一定不为0。

既然已经划定了区域,那我们怎么去使用呢?

###### 单区域节点适配

对于所有可交互元素,像按钮之类的一些组件,一般情况下,我们需要将它们放置在 SafeArea 区域,这样无论在什么屏幕下,用户的交互都不会被遮挡。在特殊情况下,如果这个UI元素需要贴紧屏幕边缘,那我们直接放置在 SafeArea 区域中的话,这个UI元素是不能够知道屏幕的边缘,无法通过锚点布局来实现的。针对这种特殊情况,我们有两种实现方式。

1. 拉伸图片:我们任然将紧贴屏幕的UI元素放置在 SafeArea 区域中布局,但是我们将图片拉伸超出屏幕范围。在实际运行时,如果 SafeArea 区域的尺寸比屏幕小,UI元素相对 SafeArea 区域的布局是相对的,这个UI元素也会向中间靠拢,因为UI元素的图片实际上是超出屏幕范围很多的,这样显示起来也不会觉得奇怪。说得比较抽象,还是上图比较方便。第一张图是布局在 SafeArea 区域中,没有拉伸图片的效果,可以看到按钮并没有紧靠屏幕边缘。第二张是拉伸后的效果,完全达到了我们的预期。





2. 调整适配区域和组件锚点:我们可以将元素不放置在 SafeArea 中,而是布局在非安全区域的节点中,同时当 SafeArea 缩小的时候UI元素需要跟随 SafeArea 移动并且也要紧贴屏幕边缘。我们将按钮放置在 Left 区域中,然后设置组件锚点,为了让按钮要动态适应 Left 区域的变化,将 `RectTransform.anchorMin = Vector2.zero; RectTransform.anchorMax = Vector2.one`,在运行的时候就会产生如下的效果,无论 Left 区域怎么变化,按钮都紧靠屏幕边缘同时又在安全区域内。





###### 复合矩形区域适配

这种情况就是针对那些特殊中的特殊需求,比如UI中有一块滑动区域,滑动区域需要紧贴屏幕右边缘和下边缘,整体在 SafeArea 区域中,我们可以通过勾选多个适配区域,组合成一块适配区域。勾选的多块区域必须要能组合成矩形,就是由一个 Rect 就能表示这块复合区域。如下图所示:



###### 异型屏幕总结

以上就是异型屏幕适配的原理,为了方便开发使用,我们总结一下:

1. 没有紧靠屏幕边缘需求的UI元素都布局在 SafeArea 区域中;
2. 紧靠屏幕左侧的UI元素,选择 Left 区域适配,同时更改 UI组件的锚点为 `RectTransform.anchorMin = Vector2.zero; RectTransform.anchorMax = Vector2.one`;
3. 紧靠屏幕右侧的UI元素,选择 Right 区域适配,同时更改锚点;
4. 紧靠屏幕上方的UI元素,选择 Top 区域适配,同时更改锚点;
5. 紧靠屏幕下方的UI元素,选择 Bottom 区域适配,同时更改锚点;
6. 紧靠屏幕左上角的UI元素,选择 LT 区域适配,同时更改锚点;
7. 紧靠屏幕左下角的UI元素,选择 LB 区域适配,同时更改锚点;
8. 紧靠屏幕右上角的UI元素,选择 RT 区域适配,同时更改锚点;
9. 紧靠屏幕右下角的UI元素,选择 RB 区域适配,同时更改锚点;
10. 多区域适配的,选择的区域必须能够组成一个矩形才可以,同时更改锚点;

#### 三、编辑器预览处理

为了方便开发预览,需要在编辑器中添加一个插件**Device Simulator**,在 PacnageManager 中搜索添加即可。可以查看官方文档,直接扩展自定义机型的预览。

#### 四、使用限制和扩展

##### 1.粗暴模式

`Screen.safeArea` 获取刘海屏信息适用于安卓9.0或以上系统(抛弃8.0奇葩刘海),ios是可以使用。对于那些老旧安卓机型,需要做特殊处理。安卓8.0可以使用配置表,因为机型不会非常多(直接不适配8.0最方便)。配置表格式可以和 **Device Simulator** 支持的扩展格式一致,这样可以直接在编辑器中预览,而不需要额外添加配置。如果不想适配,刘海直接黑边处理,把Render outside safe area取消就可以。

##### 2.科学模式

NotchFit是一款Android端的刘海屏适配库,适配了O版本和P版本,它屏蔽了不同厂商不同设备不同系统版本对刘海屏适配带来的一系列的繁杂的问题。NotchFit可以智能的判断刘海的逻辑参数,所谓的刘海逻辑参数是该库对设备刘海参数的一个抽象获取,刘海逻辑参数不单是获取设备的硬件参数,还会根据系统的设置(如小米、华为等手机可以在系统中控制刘海区域的使用与否)等条件判断当前屏幕的统一的UI布局状态,检查是否需要进行刘海适配。[Github地址](https://github.com/wcl9900/NotchFit)

科学一点的处理流程可以这样:

1. iOS 和 Android9.0 以上机型直接使用 `Screen.safeArea`;
1. Android9.0 以下机型,通过 Android 系统提供的操作API,来调用获取手机的 SafeArea,这样需要在 Unity 中导入 jar 包;
2. 对用通过 Android 的API获取到的 SafeArea 不正确的机型,需要将该机型的 SafeArea 信息添加到配置表中,运行时直接读取使用即可;

[适配O版本和P版本Android](Unity快速适配IOS/安卓刘海屏(又简单又快 适配了O版本和P版本))

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-15 14:41 , Processed in 0.091012 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表