Unity屏幕适配终极方案

发表于2015-04-29
评论1 2.14w浏览

一、分辨率统计

根据最新数据,目前android和ios的分辨率分布如下图1和图2所示:

      http://km.oa.com/files/post_photo/690/228690/d9a9657fe68316af2d282cfc22f52a981421287563.gif

图1 android分辨率分布

 

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image002.gif

 图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元素抽象、基准分辨率和宽高比。

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image003.gif

4)等比拉升:即拉升时不修改UI元素的宽高比例,是实现适配的第一把武器。

5)锚点:使无损单元完整显示的一种手段,是实现适配的第二把武器。一般的实现手法是设定一个与摄像机或参照物的距离相对(绝对)数值,当摄像机或参照物发生变化时,该单元也随之变化,基于此可以使它始终保持在屏幕中相对固定的位置。

2、Unity适配原理

对于使用ngui(版本:3.5.6,其他版本在实现手法上稍有出入,但是基本原理一致)的项目,ngui已提供了适配的两把武器:UIRoot(实现等比拉升),UIAnchor &&UIWidget(实现锚点)。

 

UIRoot:一个场景内唯一的ngui根节点,作用是通过设定一个基准高manualHeight,实现整个UI场景的等比缩放。核心函数如下(重点部分红框标示):

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image004.gif

其中activeHeight的计算方式如下:

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image005.gif

由上面我们可以得到缩放因子为 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中的呈现如下:

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image006.jpg

其中Container是可选参照物,Side是指具体参照位置,有TopLeft,Left,BottomLeft,Top,Center,Bottom,TopRight,Right,BottomRight九个位置可选。Relative Offset和Pixel Offset分别是对Side的相对和绝对位置偏移量,

    具体算法步骤和关键代码:

(1)求参考物的Rect,参考物的优先选取顺序为:widget、panel、camera。

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image007.jpg

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image008.jpg

(2)根据rect求中心坐标,跟Side关联后坐标重新定位,最后  根据绝对偏移量、相对偏移量和rect的尺寸,计算得到一个初步     局部锚点坐标

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image009.jpg

 

(3)将之前计算得到的局部坐标舍去小数点(防止精度问题),并转换成世界坐标。

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image010.jpg

 

UIWidget是所有UI的基类,内置了一个anchor,提供了相对与任一参考物的相对和绝对偏移。Anchor提供了普通和高级两种方式 在Insoector中的具体呈现如下:

普通方式:

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image011.jpg

高级方式:

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image012.jpg

很明显可以看到,普通模式只能选择一个参考物,可以设置被锚点物体的每条边与参考物的哪个side锚定,以及确定对应的相对和绝对偏移量。而高级模式可以选择四个参考物。

普通模式下的算法步骤和关键代码:

  (1)获取被锚物体的边界坐标,通过相对和绝对偏移量计算得到新坐标

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image013.jpg

  (2)根据被锚物体自身的枢轴(pivot)数据,插值获得新的锚点坐标,同时物体的高宽也受其影响。

http://avocado.oa.com/fconv/files/201501/bd6b81d9e6bcb8caa9e367827aed5d59.files/image014.jpg

 

3、Unity适配总结和技巧

(1)合理选取基准分辨率,使得UI有损单元的损失最小。

(2)可以根据不同的宽高比动态调整UIRoot的基准高,确保UI无损单元的完整显示和减少有损单元损失。

(3)UIAnchor可以使用在非UIWidget的组件上,比较独立且比较方便,但是只能选择固定的Side进行偏移,灵活性不太好。UIWidget上的anchor不能独立使用,但是可以选择任意的Side进行偏移,灵活性较好。

(4)有损UI单元(例如背景图)上,尽量不要放置位置信息,因为位置信息通常属于无损信息,拉伸后不利于展示和其他UI适配。

 

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引