Unity屏幕适配终极方案
一、分辨率统计
根据最新数据,目前android和ios的分辨率分布如下图1和图2所示:
图1 android分辨率分布
图2 ios分辨率分布
根据上图,我们可以得到一个重要结论,即主流的移动设备的屏幕宽高比(高宽比)的范围在1.33 ~ 1.77之间,这为我们美术上的规划和技术上的适配提供了重要依据。
二、2D游戏的适配
对于例如用cocos2d制作的2d游戏,方案比较成熟,主要有以下三种:
1)全屏,不等比拉升画面,该方法简单粗暴,但是导致失真,效果很差;
2)非全屏,等比拉升画面,空余处用背景色填充,效果优于第一种,但是屏幕利用率较低;
3)全屏,等比拉升画面,结合锚点,实现关键UI的完整呈现。
毫无疑问,第三种的效果是最好的,但是实现也是要更加复杂。
三、Unity游戏的适配
对于用Unity制作的3d游戏,其适配复杂度比2d游戏会更高一些,需要适配的情景主要包括以下几个方面:
1)UI场景的适配
2)3D场景的适配
3)3D场景下的UI适配
4)UI场景下的3D模型适配
本文主要讨论UI场景的适配。
四、UI场景适配
1、关键词解释
我们采用前文所述的第三种方案,即全屏不失真显示,接来下会涉及到五个关键词语:UI元素抽象、屏幕宽高比(高宽比)、基准分辨率、等比拉升、锚点,先分别解释一下。
1)UI元素抽象:如果将UI中所有元素从重要程度抽象成两部分,一部分为有损单元,一部分为无损单元。区分有损和无损对象是适配的关键,因为全屏不失真模式下,不可能做到任何元素都无损。
2)屏幕宽高比(高宽比):在统一的高度(宽度)下,决定了有损单元的损失程度。
3)基准分辨率: 基准分辨率确定了无损单元的基准数据(包括位置、大小等),其他分辨率下则需要基本保持一致。下图解释了UI元素抽象、基准分辨率和宽高比。
4)等比拉升:即拉升时不修改UI元素的宽高比例,是实现适配的第一把武器。
5)锚点:使无损单元完整显示的一种手段,是实现适配的第二把武器。一般的实现手法是设定一个与摄像机或参照物的距离相对(绝对)数值,当摄像机或参照物发生变化时,该单元也随之变化,基于此可以使它始终保持在屏幕中相对固定的位置。
2、Unity适配原理
对于使用ngui(版本:3.5.6,其他版本在实现手法上稍有出入,但是基本原理一致)的项目,ngui已提供了适配的两把武器:UIRoot(实现等比拉升),UIAnchor &&UIWidget(实现锚点)。
UIRoot:一个场景内唯一的ngui根节点,作用是通过设定一个基准高manualHeight,实现整个UI场景的等比缩放。核心函数如下(重点部分红框标示):
其中activeHeight的计算方式如下:
由上面我们可以得到缩放因子为 2 / manualHeight,因此所有UIRoot的子对象也都是缩放的,包括position和scale。
缩放有三条非常重要的原则:
(1)基准高(manualHeight)需要等于基准分辨率的高。
(2)相同的屏幕宽高比,高度和宽度上完全匹配。
(3)不同的屏幕宽高比,高度上完全匹配。
根据第(1)条,我们又得出基准分辨率下,所有UI对象的position、所有UIWidget对象的Dimension,都和屏幕坐标系是一一对应的。也就是说,一个UI对象的局部坐标和屏幕坐标是1:1的映射关系,对象中的材质面片大小和贴图像素大小也是1:1的映射关系,这是非常重要的一条结论,它保证了实际搭建场景时对UI设计稿的精确还原。根据第(3)条,我们知道不同宽高比下,宽度上无法适配,因此该方向上必然出现有损UI单元,因此在设计基准分辨率时,原则上应保证基准宽度不小于有损的可接受范围。
UIAnchor &&UIWidget:根据缩放原则第(3)条,我们知道缩放不能解决宽度上适配的问题,而锚点解决了无损单元的适配问题。Ngui提供两种方式,UIAnchor和UIWidget。
UIAnchor提供了相对于camera或其他参照物的相对偏移和绝对偏移。UIAnchor在Insoector中的呈现如下:
其中Container是可选参照物,Side是指具体参照位置,有TopLeft,Left,BottomLeft,Top,Center,Bottom,TopRight,Right,BottomRight九个位置可选。Relative Offset和Pixel Offset分别是对Side的相对和绝对位置偏移量,
具体算法步骤和关键代码:
(1)求参考物的Rect,参考物的优先选取顺序为:widget、panel、camera。
(2)根据rect求中心坐标,跟Side关联后坐标重新定位,最后 根据绝对偏移量、相对偏移量和rect的尺寸,计算得到一个初步 的局部锚点坐标。
(3)将之前计算得到的局部坐标舍去小数点(防止精度问题),并转换成世界坐标。
UIWidget是所有UI的基类,内置了一个anchor,提供了相对与任一参考物的相对和绝对偏移。Anchor提供了普通和高级两种方式 在Insoector中的具体呈现如下:
普通方式:
高级方式:
很明显可以看到,普通模式只能选择一个参考物,可以设置被锚点物体的每条边与参考物的哪个side锚定,以及确定对应的相对和绝对偏移量。而高级模式可以选择四个参考物。
普通模式下的算法步骤和关键代码:
(1)获取被锚物体的边界坐标,通过相对和绝对偏移量计算得到新坐标
(2)根据被锚物体自身的枢轴(pivot)数据,插值获得新的锚点坐标,同时物体的高宽也受其影响。
(1)合理选取基准分辨率,使得UI有损单元的损失最小。
(2)可以根据不同的宽高比动态调整UIRoot的基准高,确保UI无损单元的完整显示和减少有损单元损失。
(3)UIAnchor可以使用在非UIWidget的组件上,比较独立且比较方便,但是只能选择固定的Side进行偏移,灵活性不太好。UIWidget上的anchor不能独立使用,但是可以选择任意的Side进行偏移,灵活性较好。
(4)有损UI单元(例如背景图)上,尽量不要放置位置信息,因为位置信息通常属于无损信息,拉伸后不利于展示和其他UI适配。