我们知道,如今的移动端设备分辨率五花八门,而开发过程中往往只取一种分辨率作为设计参考,例如采用1920*1080分辨率作为参考分辨率。

选定了一种参考分辨率后,美术设计人员就会固定以这样的分辨率来设计整个游戏的UI概念图;而这时就需要程序尽可能精准的匹配各种不同屏幕的分辨率。

好在Unity ugui中自带Canvas适配:

例如,我们要在手机上采用竖屏设计,可能就会用到如上这样的参考分辨率,这时Canvas画布会自动检测当前的屏幕分辨率并进行缩放。

为了更直观的了解ugui的缩放原则,我们可以直接通过实验测试数据来观察:

如上所示,此时我设置的测试分辨率为1440*2960,因为设置的是按照参考分辨率的宽度进行匹配,所以整个画布的高度就会变为2960*1080/1440=2220;同样的,画布的宽度是这样计算的1440*1080/1440=1080。

同时,画布也按照相应的比例进行了缩放1440/1080=1.333333...

通过上面的观察我们可以发现,当以宽度进行适配时,只与参考分辨率的宽度和屏幕分辨率的宽度有关,是以这两个数值的比例进行的画布缩放;

同样的道理,如果我们设置为以高度进行匹配,就与屏幕的宽度和参考分辨率的宽度无关了,而只与对应高度的比值有关。

上面这一点非常重要,一定要非常清楚的,不然很可能会在适配和坐标转换时踩坑。(例如很多人是宽度按宽度适配和缩放,高度按高度适配和缩放,最后计算的结果可想而知!)

现在的问题就在于,什么时候应该适配参考分辨率的宽度,什么时候应该适配高度呢。

最好的方法是以最小的缩放幅度来达到适配UI的目的,也就是说,我们需要比较当前屏幕的宽高比与参考分辨率的宽高比之间的大小,最理想的情况当然是双方宽高比相同,那就无论匹配宽还是高都一样,也无需进行任何比例的缩放就能完美适配。

但事实上这种可能性几乎为零,当参考分辨率的宽高比大于屏幕分辨率的宽高比时,此时屏幕分辨率看上去会比参考分辨率显得更高,所以此时应该以参考分辨率的宽度进行匹配,将高度进行对应比例的压缩,宽度则保持不变。

如果此时还以高度进行匹配,则缩放幅度明显会比之前大,此时宽度的改变值会比高度的改变值更大,这样就无法达到最低限度的画布缩放。

 using UnityEngine;
using UnityEngine.UI; [RequireComponent(typeof(CanvasScaler))]
public class FixCanvasTool : MonoBehaviour
{
void Awake()
{
FixResolution();
} public void FixResolution()
{
CanvasScaler scaler = GetComponent<CanvasScaler>(); float sWToH = scaler.referenceResolution.x * 1.0f / scaler.referenceResolution.y;
float vWToH = Screen.width * 1.0f / Screen.height;
if (sWToH > vWToH)
{
//匹配宽
scaler.matchWidthOrHeight = ;
}
else
{
//匹配高
scaler.matchWidthOrHeight = ;
}
}
}

上面的脚本实现了前面所说的原理,将它挂载到Canvas的根节点上就可以自动按照屏幕分辨率以最优化的缩放方式适配不同分辨率的屏幕。

下面来讨论进行过缩放后的ugui中如何显示指定三维世界坐标位置的点。

这种功能是十分常见的,例如我们在场景中打一个怪物,怪物在三维空间的世界坐标系中,但击中它后我希望在Canvas画布上对应的位置(例如就在怪物头上)显示当前怪物受到的伤害数值。

当然了,如果你坚持再创建一个基于场景中三维空间的画布,那我无话可说,但更好的做法显然是统一在一个二维画布的对应屏幕位置正确显示,这样你每个场景只需要统一管理一个Canvas即可。

如果你很熟悉GPU渲染管线的知识,那这里的坐标系转化对你来说应该就再简单不过了。

我们知道,一个点要在屏幕当中显示,需要经历以下坐标系的转换,首先转化为场景空间的世界坐标,然后转化为观察空间的坐标(摄像机坐标),此时Z轴的值代表摄像机的深度值。

得到观察空间的坐标后,就可以很方便的按照屏幕分辨率的值进行转化了,从而得到屏幕空间的坐标。如果是在写Shader的话中间还包括裁剪空间。

得到屏幕坐标后,此时的坐标并不能直接就按照该值点在画布上,因为屏幕坐标值和画布所给的参考分辨率的值一般是不相同的,所以这个值还要按照一定的缩放比例点在画布正确的位置。

需要注意的是,网上很多的转化方式都是有问题的,很多都是屏幕宽度按照参考参考分辨率的宽度缩放,屏幕高度按照参考分辨率的高度缩放,看上去好像没有任何问题。

但如果你的UI已经进行过适配,并且只按照其中一种模式进行匹配,那计算出来的结果就会完全不同。

下面给出参考:

     public Vector2 WorldPosToUIPos(Vector3 worldPos,Canvas canvas)
{
//摄像机空间值域[0,1],z轴值代表深度
var viewPos = Camera.main.WorldToViewportPoint(worldPos);
//按照值域进行裁剪
if (viewPos.x >= && viewPos.x <= && viewPos.y >= && viewPos.y <= )
{
//屏幕空间高度值
float sheight = viewPos.y * Screen.height;
//屏幕空间宽度值
float swidth = viewPos.x * Screen.width;
//适配转化
return new Vector2(swidth.GetFixed(canvas), sheight.GetFixed(canvas));
}
//返回一个固定值-1代表不在屏幕当中
return -Vector2.one;
}

GetFixed为float类型的扩展方法:

     //Screen坐标值适配Canvas画布
public static float GetFixed(this float value, Canvas canvas)
{
var cs = canvas.GetComponent<CanvasScaler>();
if (cs.matchWidthOrHeight == )
//匹配宽度时仅按照宽度计算
return value * cs.referenceResolution.x / Screen.width;
else
//匹配高度时仅按照高度计算
return value * cs.referenceResolution.y / Screen.height;
}

需要注意的是,这里只进行高度或宽度的单一匹配,不进行混合计算。返回的值是以屏幕左下角为坐标原点得到的UIPos,因为默认情况下二维屏幕计算坐标轴就是以左下为原点的。(当然这是因为Unity内部对不同平台例如OpenGL和Direct3D进行了统一)

如果锚点(Anchor,注意和Pivot轴心区分)正好在左下:

则可以直接设置上面函数的返回值:

ShowUI.GetComponent<RectTransform>().anchoredPosition=WorldPosToUIPos(moster.transform.position+offse,canvas);

即使锚点不在左下,也只需要按照锚点的位置再进行简单的坐标转换即可。

注意在Canvas下不要用transfrom.localPosition设置元素的位置,最好采用anchoredPosition来设置以保证无论怎么改分辨率该值都不发生变化。

anchoredPosition显示的就是在Inspector面板中根据锚点计算后显示的Pos X,Pos Y的值。

2019年12月26日更新:

更新一个刘海屏的适配方案:

在游戏的全局系统设置中增加可以压缩canvas左右边缘的设置滑条,类似于这样:

通过该滑条的设置向左或向右来滑动场景中的canvas画布边缘向左或向右偏移。

下面是具体功能实现的脚本:

 using System.Collections.Generic;
using UnityEngine;
using AGrail; //这个脚本用于执行调整UI的边缘
public class UIEdgeFix : MonoBehaviour
{
[SerializeField]
private List<RectTransform> roots = new List<RectTransform>(); private void Start()
{
FixEdge(GameManager.UIInstance.UIEdge);
} public void FixEdge(float value)
{
foreach (var root in roots)
{
//判断为四周扩展类型的锚点预设
if (root.anchorMin == Vector2.zero && root.anchorMax == Vector2.one)
{
if (value > .5f)
{
//设置左下
root.offsetMin = new Vector2((value - .5f) * , );
root.offsetMax = new Vector2(, );
}
else {
//设置右上
root.offsetMax = new Vector2(-(.5f - value) * , );
root.offsetMin = new Vector2(, );
}
}
}
}
}

原理非常简单,根据滑条传入的值来判断是那一边的画布需要被压缩移动。

需要注意的是,在canvas下的根节点处必须将锚点预设为四周扩展的类型,然后canvas下的其他元素全部位于根节点下作为子物体。(背景图元素除外)

一般来说,规范的canvas布局也理应是如此。

这样做的好处是随时可以很方便的调整整个canvas窗口距离屏幕边缘的距离。

当滑条的值改变时更新调用所有canvas上的UIEdgeFix 脚本:

         public void OnUIEdgeChange(float vol)
{
GameManager.UIInstance.UIEdge = vol;
var fixList = FindObjectsOfType<UIEdgeFix>();
foreach (var item in fixList)
{
item.FixEdge(vol);
}
}

将改变的值随时记录到本地:

         public float UIEdge
{
set
{
PlayerPrefs.SetFloat("UIEdge", value);
}
get
{
if (!PlayerPrefs.HasKey("UIEdge"))
PlayerPrefs.SetFloat("UIEdge", 0.5f);
return PlayerPrefs.GetFloat("UIEdge");
}
}

Unity ugui屏幕适配与世界坐标到ugui屏幕坐标的转换的更多相关文章

  1. Unity 手机屏幕适配

    ////如有侵权 请联系我进行删除 email:YZFHKM@163.com 1.游戏屏幕适配 屏幕适配是为了让我们的项目能够跑在各种电子设备上(手机,平板,电脑) 那么了解是适配之前首先要了解两个知 ...

  2. UGUI 屏幕适配 导致 BoxCollider无效 解决记录

    从来没有做过一个完整的游戏,所以用UGUI来做个手游界的 " Hello World " - 微信打飞机.看起来easy做起来也碰到各种奇异的问题. 昨天导出安卓包之后,在我的MX ...

  3. 屏幕坐标点转UGUI坐标【包含屏幕适配】

    using UnityEngine; public class ScreenToUI : MonoBehaviour { public const float UI_Width = 1366f; pu ...

  4. 关于Unity中的屏幕适配

    一.Game视图的屏幕分辨率可以先自定义添加,供以后选择,以下是手游经常用到的分辨率: 1.1136X640,iPhone5 2.1920X1080,横屏,主流游戏都是这个分辨率 3.1080X192 ...

  5. 【Unity】基于MVC模式的背包系统 UGUI实现

    前言 本文基于MVC模式,用UGUI初步实现了背包系统. Control层包括了点击和拖拽两种逻辑. 博文首发:http://blog.csdn.net/duzixi 下载地址:https://git ...

  6. Unity 屏幕适配小脚本

    屏幕适配是可以通过代码实现的,相信给你时间就一定能写出来. 我们公司貌似没有分辨率适配框架通常对应小屏幕的苹果4要额外设置下等等就完了! 屏幕适配框架实现思路:  通过代码获取当前的分辨率 –> ...

  7. Unity2D多分辨率屏幕适配方案(转载)

    一下内容转自:http://imgtec.eetrend.com/forum/3992 此文将阐述一种简单有效的Unity2D多分辨率屏幕适配方案,该方案适用于基于原生开发的Unity2D游戏,即没有 ...

  8. [原创]一种Unity2D多分辨率屏幕适配方案

    此文将阐述一种简单有效的Unity2D多分辨率屏幕适配方案,该方案适用于基于原生开发的Unity2D游戏,即没有使用第三方2D插件,如Uni2D,2D toolkit等开发的游戏,NGUI插件不受这个 ...

  9. 一种Unity2D多分辨率屏幕适配方案

    http://www.cnblogs.com/flyFreeZn/p/4073655.html 此文将阐述一种简单有效的Unity2D多分辨率屏幕适配方案,该方案适用于基于原生开发的Unity2D游戏 ...

随机推荐

  1. 学习ES6笔记──工作中常用到的ES6语法

    学习博客:https://segmentfault.com/a/1190000016068235

  2. 【Html JS】使用问题记录

    [Html JS]使用问题记录 ================================================================ 1.td 文字换行 2.正则表达式 = ...

  3. hexo + next 搭建博客时Cannot GET /tags/问题处理

    原来是要修改新建的index.md文件,不仔细. 此外,愈发觉得百度和谷歌搜索同一问题的差距,谷歌更适合程序员! https://www.zhihu.com/question/29017171 这个可 ...

  4. THLM,CSS

    目录 HTTP协议的四大特性 数据格式 状态码 HTML概念 标签 标签分类 按是否封闭分类 按级别分类 标签属性 head内常用标签 body内常用标签 body内重要标签 a 标签 img 标签 ...

  5. 《Java练习题》习题集四

    编程合集: https://www.cnblogs.com/jssj/p/12002760.html Java总结:https://www.cnblogs.com/jssj/p/11146205.ht ...

  6. 开源日志框架Exceptionless使用教程

    Exceptionless是一款日志记录框架,它开源.免费.提供管理界面.易于安装和使用.ExceptionLess底层采用ElasticSearch作为日志存储,提供了快速.丰富的查询API,方便我 ...

  7. bsoj5988 [Achen模拟赛]期望 题解

    bsoj5988 Description [题目背景] NOI2018 已经过去了许久,2019 届的 BSOIer 们退役的退役,颓废的颓废,计数能力大不如前.曾经的数数之王 xxyj 坦言:&qu ...

  8. JS 操作符、控制流程、循环、字符串/数组方法

    操作符 算术运算符:+ .- . * . / . %.++.-- 赋值运算符:= .+=.-=. *=./=.%= 比较运算符:>.>=.<.<=.!=.==.===(全等,数 ...

  9. Aery的UE4 C++游戏开发之旅(2)编码规范

    目录 C++基础类型规范 命名规范 头文件规范 字符串规范 字符集规范 参考 C++基础类型规范 由于PC.XBOX.PS4等各平台的C++基础类型大小可能不同(实际上绝大部分都是整型类型的大小不同) ...

  10. React: 研究Flux设计模式

    一.简介 一般来说,State管理在React中是一种最常用的实现机制,使用这种state管理系统基本可以开发各种需求的应用程序.然而,随着应用程序规模的不断扩张,原有的这种State管理系统就会暴露 ...