Notifications
Article
设计适应不同屏幕分辨率的UI
Updated 22 days ago
1.3 K
5
现代游戏和应用程序通常需要支持各种不同的屏幕分辨率,特别是 UI 布局需要能够适应这种情况。Unity 中的 UI 系统包含用于此目的的各种工具,并可通过各种方式将这些工具组合起来使用。
在本操作指南中,我们将研究一个简单案例,并在此背景下了解和比较不同的工具。在我们的案例研究中,我们在屏幕角点处有三个按钮(如下所示),目标是使这种布局适应各种分辨率。
在本操作指南中,我们将考虑四种屏幕分辨率:手机高清 (HD) 纵向 (640 x 960) 和横向 (960 x 640) 以及手机标清 (SD) 纵向 (320 x 480) 和横向 (480 x 320)。最初的布局设置为手机高清纵向分辨率。

使用锚点来适应不同的宽高比

默认情况下,UI 元素锚定到父矩形的中心。这意味着它们与该中心保持恒定的偏移。
如果使用此设置将分辨率更改为横向宽高比,则按钮可能甚至不再位于屏幕的矩形内。
一种将按钮保持在屏幕内的方法是,通过改变布局,使按钮的位置绑定到屏幕的各个角。可使用 Inspector 中的 Anchors Preset 下拉选单或通过拖动 Scene 视图中的三角形锚点控制柄,将左上角按钮的锚点设置为左上角。如果 Game 视图中设置的当前屏幕分辨率是最初设计布局时的目标分辨率(此时的按钮位置看起来正确),那么最适合这样做。同样,左下角按钮和右下角按钮的锚点可以分别设置为左下角和右下角。
一旦这些按钮锚定到各自的角点,当分辨率更改为不同的宽高比时,这些按钮会保持在相应位置。
当屏幕大小更改为更大或更小的分辨率时,按钮也仍然锚定到各自的角点。但是,由于按钮保持原始大小(以像素为单位),因此按钮占据屏幕的比例可能会变大或变小。这种状态可能符合需求,也可能不符合需求,具体情况取决于您希望布局在不同分辨率的屏幕上的表现方式。
在本操作指南中,我们知道手机标清纵向和横向布局的较小分辨率并不意味着屏幕实体较小,只是屏幕的像素密度较低而已。在这些像素密度较低的屏幕上,按钮看起来不应该大于高密度屏幕上显示的按钮,而是应该以相同的大小显示。
这意味着按钮变小的比例应该与屏幕变小的比例相同。换句话说,按钮的比例应遵循屏幕大小。这种情况下,画布缩放器 (Canvas Scaler) 组件很有用。

随屏幕大小缩放

画布缩放器组件可添加到根画布;画布是一种带有画布组件的游戏对象,所有 UI 元素都是其子项。通过 GameObject 菜单创建新的画布时,默认情况下也会添加画布缩放器。
在画布缩放器组件中,可将其 UI Scale Mode 设置为 Scale With Screen Size。使用此缩放模式,可以指定要用作参考的分辨率。如果当前屏幕分辨率小于或大于此参考分辨率,则会相应设置画布的缩放因子,使所有 UI 元素都与屏幕分辨率一起放大或缩小。
在我们的示例中,我们将画布缩放器设置为手机高清纵向分辨率 640 x 960。现在,屏幕分辨率设置为手机标清纵向分辨率 320 x 480 时,整个布局将按比例缩小,从而保持与全分辨率相同的比例。一切都按比例缩小:按钮大小、按钮到屏幕边缘的距离、按钮图形以及文本元素。这意味着手机标清纵向分辨率中的布局与手机高清纵向分辨率中的布局相同;只是像素密度降低而已。
有一点需要注意:添加画布缩放器组件后,还要检查其他宽高比条件下的布局显示情况。通过将分辨率恢复到手机标清横向分辨率,我们可以看到按钮现在看起来比其应有大小(先前的大小)更大。
按钮在横向宽高比条件下变大的原因归结为画布缩放器设置的工作原理。默认情况下会将宽度或当前分辨率与画布缩放器的宽度进行比较,结果将用作缩放所有内容的缩放因子。由于目前横向分辨率 960 x 640 的宽度是纵向画布缩放器 640 x 960 的 1.5 倍,因此布局的比例增大到了 1.5。
该组件有一个名为 Match 的属性,此属性值可以是 0(宽度)、1(高度)或介于两者之间的值。默认情况下设置为 0,表示将当前屏幕宽度与画布缩放器宽度进行比较,如上所述。
如果 Match 属性设置为 0.5,则会将当前宽度与参考宽度做比较并将当前高度与参考高度做比较,并选择两者之间的缩放因子。由于在本示例中的横向分辨率变宽为原来的 1.5 倍,但高度也变短为原来的 1/1.5,因此综合这两个因子后得到最终缩放因子为 1,这意味着按钮将保持其原始大小。
据前文,通过在画布上使用适当的锚定技术和画布缩放器组件相结合,布局将支持所有四种屏幕分辨率。

宣雨松先生建议

自适应屏幕设置

自适应屏幕需要缩放画布,Canvas需要绑定Canvas Scaler组件(默认已添加)。接着需要确认开发的分辨率,也就是说,UI美术人员必须按这个屏幕尺寸来出对应的图片和布局。以移动平台为例,现在主流的手机大多数是16:9的分辨率比例,不能把分辨率设置得太高,得考虑兼容低配的手机。这里我设置的分辨率是1334*750,目标分辨率等比例缩放即可,最后将屏幕相对模式设置成Expand。
这里Expand表示Canvas下的UI始终保持在屏幕内,当屏幕宽度变窄后,它会整体缩放高度来保持自适应。在Screen Match Mode下拉框中,还可以选择Match Width Or Height和Shrink,其中前者表示始终保持宽度或高度来自适应高度或宽度,后者表示当分辨率变化时,始终保持原始比例,超出屏幕部分会被裁切掉(将显示不全)。所以,在游戏开发中,界面自适应首先应该选择Expand模式。
游戏中至少需要两个摄像机,一个是3D摄像机(主摄像机),另一个就是UI摄像机了。我们需先渲染3D摄像机,然后再渲染UI摄像机,这样UI就会盖在3D场景的前面。我们可以设置UI Camera里的Depth值。Depth在3D摄像机上设置的是-1,在UI摄像机设置的就是0(或者大于-1)了。另外,UICamera需要设置正交摄像机,此时只需要在Projection中选择Orthographic即可。Clipping Planes的最小值(Near)建议设置成0,不然有时候某些UI会被剔除掉。

锚点对齐方式

目前,UI的自适应是整体缩放。有时候,我们可能需要将某些UI挂靠在屏幕的4个边角上。锚点的对齐方式一共有9个。由于UI对象是可以多层嵌套的,请记住这里面设置的是相对它的父对象的对齐方式。设置好锚点挂靠后,无论如何修改屏幕分辨率,边角挂靠的按钮永远都会自适应地挂靠在4个角上。

背景图全屏

背景图拉伸分两种,一种是允许图片变形,另一种就是不允许图片拉伸变形,可以自动裁切。设置图片的Rect Transform锚点的横向、纵向都支持自动拉伸,这样图片永远都会保持和屏幕大小一致,但是图片会被拉伸变形。
如果允许图片适当地裁切,保证它不会变形,那么可以给Image添加Aspect Ratio Fitter组件,并且在Aspect Mode中选择Envelope Parent。
将分辨率修改到极端情况,Rect Transform锚点设置的图片虽然显示全了,但是整体变形都被拉伸了。而Aspect Ratio Fitter设置的图片被裁切掉了部分内容,图片整体并没有发生变形。

任意分辨率下相机视口宽高比保持一致

对于很多手机游戏,窗口不能随意拉伸,而且在任意分辨率下,游戏视口的宽高比应该保持一致(避免分辨率不同导致的视野大小不一致)。这种情况下就不能简单使用Scale With Screen Size来解决了。
最简单的方法是:
  • 在Canvas下新建一个空物体Panel,所有的UI元素都放到这个Panel下。
  • 在这个物体上加一个组件AspectRatioFitter,Aspect Mode设置为Fit In Parent,Aspect Ratio 设置为UI设计的宽高比。
  • Canvas上设置Canvas Scaler组件如下图所示:
经过上面3步设置后,UI就会保持宽高比并且保持在屏幕内,但是相机的视口可能会超出该宽高比,需要给相机一个脚本,让相机的视口也能保持宽高比。
private float aspectRatio = 1.7786f; private Camera _camera; private float lastAspectRatio; private void Start() { _camera = GetComponent<Camera>(); _camera.aspect = aspectRatio; } private void Update() { float aspect = Screen.width / (float)Screen.height; if (aspect == lastAspectRatio) { return; } lastAspectRatio = aspect; //当前屏幕比较宽,需要两边留黑 if (aspect > lastAspectRatio) { float actualWidth = Screen.height * aspectRatio; float viewportWidth = actualWidth / Screen.width; float viewportX = (1 - viewportWidth) / 2; _camera.rect = new Rect(viewportX, 0, viewportWidth, 1); } //当前屏幕比较窄,需要上下留黑 else { float actualHeight = Screen.width / aspectRatio; float viewportHeight = actualHeight / Screen.height; float viewportY = (1 - viewportHeight) / 2; _camera.rect = new Rect(0, viewportY, 1, viewportHeight); } }
设置完后,运行效果如下:
Tags:
尹煜
北京宏宇航天技术有限公司Unity工程师 - Programmer
31
Comments
尹煜
22 days ago
北京宏宇航天技术有限公司Unity工程师
lynhi,作者你好。我想问主相机如何控制显示宽度?我的意思是如何我设计的游戏场景整体宽高比是1.5:1但是此时游戏运行在宽高比2:1的手机上,我要如何做才能让游戏只显示原来1.5:1的内容,边缘的宽显示为黑边?
我又更新了一下,在文章底部,看看是否符合你的需求?谢谢
1
l
lyn
25 days ago
PROS正交相机修改orthgraphic size,透视相机修改view port中的w h x y值。具体改为多少需要看具体的目标宽高与实际宽高的关系。
了解了,需要预先检测宽高比再修改orthgraphicsize
0
PROS
a month ago
探险家麦哲伦
lynhi,作者你好。我想问主相机如何控制显示宽度?我的意思是如何我设计的游戏场景整体宽高比是1.5:1但是此时游戏运行在宽高比2:1的手机上,我要如何做才能让游戏只显示原来1.5:1的内容,边缘的宽显示为黑边?
正交相机修改orthgraphic size,透视相机修改view port中的w h x y值。具体改为多少需要看具体的目标宽高与实际宽高的关系。
0
zw
zheng wenhao
a month ago
说是分辨率不能设置太高,1920x1080算高吗?低端手机不支持这个分辨率吗?
0
l
lyn
a month ago
hi,作者你好。我想问主相机如何控制显示宽度?我的意思是如何我设计的游戏场景整体宽高比是1.5:1但是此时游戏运行在宽高比2:1的手机上,我要如何做才能让游戏只显示原来1.5:1的内容,边缘的宽显示为黑边?
0