UGUI源码学习之一(UI坐标系统)
2016-05-13 18:46
603 查看
本文章装载字:http://mp.weixin.qq.com/s?__biz=MzAxMDcxOTkxNQ==&idx=1&mid=402542189&sn=682dee9056d5059d5c17edd4e6865295
我们先从一个实际的需求出发。
需求描述:3D游戏中的怪物死亡会随机掉落一个宝箱,当有宝箱掉落时需要实现一个 UI 宝箱效果,从怪物当时所在的屏幕坐标处飞到游戏的宝箱 UI 上,然后宝箱计数加1。
解决思路:
宝箱的动画属于 UI层 的东西,需要在 UI 坐标系上进行;
需要将怪物的 3D世界坐标 转换成 2D的UI坐标。
为了解决以上问题,我们先来了解一下UGUI中涉及到的坐标系,我把这些坐标系总结为三个类型:屏幕坐标系,UI逻辑坐标系和UI世界坐标系。下面来逐一介绍。
屏幕坐标系是和屏幕分辨率一致的坐标系。
屏幕坐标系以屏幕的左下角为远点,屏幕右上角的坐标是(Screen.width, Screen.height)。示意图如下图所示:
![](http://mmbiz.qpic.cn/mmbiz/Kr2Sgibjn0HNsmcBQYN3468mL0HmO39vYBlQgfMx7jaP3n96BSXV1tHia4poicXSXAjPfXX7iaXQIZwnMibU8jwmIGQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
屏幕坐标系的范围和分辨率有关,例如 iPhone4S 就是 960*640, 5S 则是 1136*640等。不同型号设备的分辨率可能不同,因此屏幕坐标系的范围也可能不同。UI 界面显示的时候因为要照顾到不同分辨率的手机,所以不能以屏幕坐标系来进行布局。
UI逻辑坐标系是我们在设计UI界面的时候的参考坐标系。
参考坐标系一般对当前流行的手机的分辨率保持一致,例如当前流行的是 iPhone6s 的 2000*1125,则美术在制作界面示意图的时候就需要参考这个分辨率进行制作,保证在当前主流机型上有最佳的显示效果。
uGUI中坐标系的起点和屏幕坐标系是相同的,界面的左下角是原点,size是通过 Canvas 和 Canvas Scalar 组件来确定的。如下图所示。
![](http://mmbiz.qpic.cn/mmbiz/Kr2Sgibjn0HNsmcBQYN3468mL0HmO39vYJjdAy59SR2eNd6z1Bqdic8MX62ib4N8Iy8icw3ru8sGSQllZmw2ZbjHFw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
没有 Canvas Scalar 组件
此时 UI逻辑坐标系 == 屏幕坐标系。一般不采用此种用法,除非只想针对一种分辨率设备来做UI或者有自己的屏幕适配方案。
有Canvas Scalar
顾名思义,Canvas Scalar 使用 RectTransform 的 localScale 来使得 UI逻辑坐标 * RectTranform.localScale == 屏幕坐标。
Canvas Scalar 有三种 UIScaleMode:
Constant Pixel Size: 不论屏幕分辨率怎么变化,UI元素例如按钮在不同分辨率上占据的像素大小相同。这种情况下需要使用者来手动设置 ScaleFactor 来设置 localScale。
![](http://mmbiz.qpic.cn/mmbiz/Kr2Sgibjn0HNsmcBQYN3468mL0HmO39vYRLmicUyFqRtBiczFRz6hEf02mbG3kebcMia8Sxa7iciadZIcNXz6YfBKfXA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
例如屏幕大小变成了 960*640,则 Canvas 的大小便为 960*640。如果设置 ScaleFactor = 2, 则 localScale = new Vector3(2, 2, 2), Canvas 的大小为 480*320。此种设置的具体使用场景还未知。
Scale With Screen Size:根据实际屏幕的宽高来设置缩放
这种方式是根据屏幕的实际比例来调整 Size 和 Scale,调整Size保证和设计时一致,调整 Scale 是为了和屏幕坐标系一致。
这里需要填入一个 Reference Resolution,就是我们制作 UI 的时候的参考屏幕大小,这里为了示例,我设置成了 800*600,而屏幕分辨率是 960*640。
其中 ScreenMatchMode 又分为三种模式:
Expand:尽量减小缩放
Shrink:尽量放大缩放
MatchWidthOrHeight:根据屏幕的宽或高或者之间的一个值来进行缩放
上面三种适配方式的结果如下:
![](http://mmbiz.qpic.cn/mmbiz/Kr2Sgibjn0HNsmcBQYN3468mL0HmO39vY5kDSeB3KvQzA7XWMZDWWbFItkySSmlqepvzFaicnMTv1JCkTkwSGtcA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
![](http://mmbiz.qpic.cn/mmbiz/Kr2Sgibjn0HNsmcBQYN3468mL0HmO39vYCjKjVPibX8on5KeY70EtO1SuZMF1nQ1AQjK0FgibX93LFe9NBLLiclxoQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
![](http://mmbiz.qpic.cn/mmbiz/Kr2Sgibjn0HNsmcBQYN3468mL0HmO39vYPu05Flg6SjbgZQibOTKttxR0ibb7xy5tX2pl8Dn0ZLsymmZpZVS2WTsA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
一般在进行屏幕适配的时候会选择 MatchWidthOrHeight 来进行适配。
Scale with Physical Size:在像素上和设计时类似
在确定了 scale 之后,UI逻辑坐标系的大小 = 屏幕坐标系大小/scale,这样子可以保证UI逻辑坐标系的宽高比(Aspect Ratio)和屏幕坐标系的宽高比相同。
UI世界坐标系是指通过 RectTransfrom.position 获取的坐标值。这个是由 Canvas 组件的 RenderMode 来决定的:
Screen Space - Overlay:则 UI 坐标系和屏幕坐标系完全一致,RectTransform.position 代表的就是屏幕坐标位置。
Screen Space - Camera:此时 Canvas 需要指定一个 Camera。根据Camera的 Projection,则又有不同。
Perspective: UI 坐标区域由 Camera 到 Canvas 的距离决定,不细述。
Orhographic: Camera.size 决定了 整个屏幕的 UI 坐标区域。
![](http://mmbiz.qpic.cn/mmbiz/Kr2Sgibjn0HNsmcBQYN3468mL0HmO39vY2z84ibwVWbSMRr2nhBU1fgPViaKMApDXRJssTyhe6yGmr2BcGQvZshmg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
此时通过 Canvas 上的 RectTransfrom 的 localScale 来将 屏幕坐标规约到 UI 坐标区域。
![](http://mmbiz.qpic.cn/mmbiz/Kr2Sgibjn0HNsmcBQYN3468mL0HmO39vYxUl7q6qpLaJyNmvoH9hxxtTt22LhpAND1iawv54camhQOasFAS377lA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
此时如果需要获取需要获取一个UI的屏幕坐标,可以通过下述代码来获得:
![](http://mmbiz.qpic.cn/mmbiz/Kr2Sgibjn0HNsmcBQYN3468mL0HmO39vYicf8y5PV8nY4ZWg9P0jdGAYVMHfrplZRv4nPGXaBoueMx709ErMiaXog/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
World Space:此时没有 UI 坐标系这个概念了,不细述。
Overlay的 RenderMode 该如何选择?
如果UI一致显示界面的最高层,那么使用 Screen-Space Overlay 模式即可;如果需要多个 Canvas 可以通过 Sort Order 来进行分成,Sort Order 越大显示越靠前;
如果UI中间需要嵌入一个3D物体,例如背景UI在最底层,人物 avatar 显示在中间层,上层显示其他 UI界面,则使用 Screen-Space Camera,此时需要多个 Camera ,然后通过 Camera 的 depth 来控制显示的次序,depth 越大越后渲染,越靠前。另外各个 Camera 的 ClearFlags 需要正确设置,背景的设置为 Solid Color,负责擦屏幕;之后的设置为 Depth Only 即可;
如果 UI 界面需要显示在世界中,则需要使用 World Space,例如在 VR 场景中。
Canvas Scalar的 UIScaleMode 该怎么选择?怎么可以让UI可以适配所有设备?
这里介绍一下个人使用经验,因为其他用得不多,优点还不知道:
UIScaleMode 设置成 ScaleWithScreenSize;
Reference Resolution 设置成当前最主流机型的分辨率,同时告诉美术这个就是参照的大小;
Match 设置成0或者1,设置成0的时候就是按照宽度适配,则以后宽度不需要考虑适配,高度上进行适配,此时告诉UI在设计UI的时候高度上一定要设置成高度可以调节的;反之亦可。一般建议横屏设置成宽度不变,竖屏设置成高度不变。
什么时候用 UI 逻辑坐标系,什么时候用 UI世界坐标系?
在游戏制作过程中拼界面的时候用 UI 逻辑坐标系,这个时候需要使用 RectTransform 提供的 pivot, anchorPos等进行UI的对齐和大小设置;
在使用程序控制UI进行动画的时候最好使用 UI世界坐标系。
![](http://mmbiz.qpic.cn/mmbiz/Kr2Sgibjn0HNsmcBQYN3468mL0HmO39vYw8blDXCBRXPbmP3IUZv4w7A5LgbpaTt8MzibCVibG3CICNEhKJCEZxEQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)
代码工程放在github[https://github.com/tnqiang/UI_Flow_Effect]上,代码解释:
第一步需要将 source 3D 物体从世界坐标转换到屏幕坐标,这个需要3D世界摄像机才能取得;
第二步需要将这个屏幕坐标转换到 UI 世界的世界坐标中,这个需要看 Camara 的 RenderMode 是否是 Overlay,如果是则直接是世界坐标,否则需要UI的摄像机来帮助。
uGUI源代码之CanvasScaler[https://bitbucket.org/Unity-Technologies/ui/src/0155c39e05ca5d7dcc97d9974256ef83bc122586/UnityEngine.UI/UI/Core/Layout/CanvasScaler.cs?at=5.2&fileviewer=file-view-default]
我们先从一个实际的需求出发。
需求描述:3D游戏中的怪物死亡会随机掉落一个宝箱,当有宝箱掉落时需要实现一个 UI 宝箱效果,从怪物当时所在的屏幕坐标处飞到游戏的宝箱 UI 上,然后宝箱计数加1。
解决思路:
宝箱的动画属于 UI层 的东西,需要在 UI 坐标系上进行;
需要将怪物的 3D世界坐标 转换成 2D的UI坐标。
为了解决以上问题,我们先来了解一下UGUI中涉及到的坐标系,我把这些坐标系总结为三个类型:屏幕坐标系,UI逻辑坐标系和UI世界坐标系。下面来逐一介绍。
屏幕坐标系
屏幕坐标系是和屏幕分辨率一致的坐标系。屏幕坐标系以屏幕的左下角为远点,屏幕右上角的坐标是(Screen.width, Screen.height)。示意图如下图所示:
屏幕坐标系的范围和分辨率有关,例如 iPhone4S 就是 960*640, 5S 则是 1136*640等。不同型号设备的分辨率可能不同,因此屏幕坐标系的范围也可能不同。UI 界面显示的时候因为要照顾到不同分辨率的手机,所以不能以屏幕坐标系来进行布局。
UI逻辑坐标系
UI逻辑坐标系是我们在设计UI界面的时候的参考坐标系。参考坐标系一般对当前流行的手机的分辨率保持一致,例如当前流行的是 iPhone6s 的 2000*1125,则美术在制作界面示意图的时候就需要参考这个分辨率进行制作,保证在当前主流机型上有最佳的显示效果。
uGUI中坐标系的起点和屏幕坐标系是相同的,界面的左下角是原点,size是通过 Canvas 和 Canvas Scalar 组件来确定的。如下图所示。
没有 Canvas Scalar 组件
此时 UI逻辑坐标系 == 屏幕坐标系。一般不采用此种用法,除非只想针对一种分辨率设备来做UI或者有自己的屏幕适配方案。
有Canvas Scalar
顾名思义,Canvas Scalar 使用 RectTransform 的 localScale 来使得 UI逻辑坐标 * RectTranform.localScale == 屏幕坐标。
Canvas Scalar 有三种 UIScaleMode:
Constant Pixel Size: 不论屏幕分辨率怎么变化,UI元素例如按钮在不同分辨率上占据的像素大小相同。这种情况下需要使用者来手动设置 ScaleFactor 来设置 localScale。
例如屏幕大小变成了 960*640,则 Canvas 的大小便为 960*640。如果设置 ScaleFactor = 2, 则 localScale = new Vector3(2, 2, 2), Canvas 的大小为 480*320。此种设置的具体使用场景还未知。
Scale With Screen Size:根据实际屏幕的宽高来设置缩放
这种方式是根据屏幕的实际比例来调整 Size 和 Scale,调整Size保证和设计时一致,调整 Scale 是为了和屏幕坐标系一致。
这里需要填入一个 Reference Resolution,就是我们制作 UI 的时候的参考屏幕大小,这里为了示例,我设置成了 800*600,而屏幕分辨率是 960*640。
其中 ScreenMatchMode 又分为三种模式:
Expand:尽量减小缩放
scale = Min(screenSize.x / ReferenceResolution.x, screenSize.y / ReferenceResolution.y)
Shrink:尽量放大缩放
scale = Max(screenSize.x / ReferenceResolution.x, screenSize.y / ReferenceResolution.y)
MatchWidthOrHeight:根据屏幕的宽或高或者之间的一个值来进行缩放
scaleWidth = screenSize.x / ReferenceResolution.x; scaleHeight = screenSize.y / ReferenceResolution.y; scale = scaleWidth * (1 - scaleWithWidthOrHeight) + scaleHeight * scaleWithWidthOrHeight
上面三种适配方式的结果如下:
一般在进行屏幕适配的时候会选择 MatchWidthOrHeight 来进行适配。
Scale with Physical Size:在像素上和设计时类似
scale = Screen.dpi / TargetDPI
在确定了 scale 之后,UI逻辑坐标系的大小 = 屏幕坐标系大小/scale,这样子可以保证UI逻辑坐标系的宽高比(Aspect Ratio)和屏幕坐标系的宽高比相同。
UI世界坐标系
UI世界坐标系是指通过 RectTransfrom.position 获取的坐标值。这个是由 Canvas 组件的 RenderMode 来决定的:Screen Space - Overlay:则 UI 坐标系和屏幕坐标系完全一致,RectTransform.position 代表的就是屏幕坐标位置。
Screen Space - Camera:此时 Canvas 需要指定一个 Camera。根据Camera的 Projection,则又有不同。
Perspective: UI 坐标区域由 Camera 到 Canvas 的距离决定,不细述。
Orhographic: Camera.size 决定了 整个屏幕的 UI 坐标区域。
此时通过 Canvas 上的 RectTransfrom 的 localScale 来将 屏幕坐标规约到 UI 坐标区域。
此时如果需要获取需要获取一个UI的屏幕坐标,可以通过下述代码来获得:
World Space:此时没有 UI 坐标系这个概念了,不细述。
一些个人理解
Overlay的 RenderMode 该如何选择?如果UI一致显示界面的最高层,那么使用 Screen-Space Overlay 模式即可;如果需要多个 Canvas 可以通过 Sort Order 来进行分成,Sort Order 越大显示越靠前;
如果UI中间需要嵌入一个3D物体,例如背景UI在最底层,人物 avatar 显示在中间层,上层显示其他 UI界面,则使用 Screen-Space Camera,此时需要多个 Camera ,然后通过 Camera 的 depth 来控制显示的次序,depth 越大越后渲染,越靠前。另外各个 Camera 的 ClearFlags 需要正确设置,背景的设置为 Solid Color,负责擦屏幕;之后的设置为 Depth Only 即可;
如果 UI 界面需要显示在世界中,则需要使用 World Space,例如在 VR 场景中。
Canvas Scalar的 UIScaleMode 该怎么选择?怎么可以让UI可以适配所有设备?
这里介绍一下个人使用经验,因为其他用得不多,优点还不知道:
UIScaleMode 设置成 ScaleWithScreenSize;
Reference Resolution 设置成当前最主流机型的分辨率,同时告诉美术这个就是参照的大小;
Match 设置成0或者1,设置成0的时候就是按照宽度适配,则以后宽度不需要考虑适配,高度上进行适配,此时告诉UI在设计UI的时候高度上一定要设置成高度可以调节的;反之亦可。一般建议横屏设置成宽度不变,竖屏设置成高度不变。
什么时候用 UI 逻辑坐标系,什么时候用 UI世界坐标系?
在游戏制作过程中拼界面的时候用 UI 逻辑坐标系,这个时候需要使用 RectTransform 提供的 pivot, anchorPos等进行UI的对齐和大小设置;
在使用程序控制UI进行动画的时候最好使用 UI世界坐标系。
引题答案
代码工程放在github[https://github.com/tnqiang/UI_Flow_Effect]上,代码解释:
第一步需要将 source 3D 物体从世界坐标转换到屏幕坐标,这个需要3D世界摄像机才能取得;
第二步需要将这个屏幕坐标转换到 UI 世界的世界坐标中,这个需要看 Camara 的 RenderMode 是否是 Overlay,如果是则直接是世界坐标,否则需要UI的摄像机来帮助。
Refs:
uGUI源代码之CanvasScaler[https://bitbucket.org/Unity-Technologies/ui/src/0155c39e05ca5d7dcc97d9974256ef83bc122586/UnityEngine.UI/UI/Core/Layout/CanvasScaler.cs?at=5.2&fileviewer=file-view-default]
相关文章推荐
- 系统自带的搜索控制器和自己封装的带搜索记录的控制器
- JS valueOf与字符串
- Equinox P2的学习
- mysql unique option prefix myisam_recover instead of myisam-recover-options的解决方法
- UITextField限制输入字数高亮状态下输入框内真实类容与textFiled(解决中文输入问题)
- error MS8020 Build tools v140 cannot be found解决办法
- 浙大 PAT Advanced level 1017. Queueing at Bank (25)
- 根据字体多少使UILabel自动调节尺寸
- POJ 2299 Ultra-QuickSort 归并排序
- 利用 druid 的 sql parser 模块解析 sql 语句
- UINavigationcontroller
- FragmentTabHost切换Fragment时避免UI重新加载
- Android基础UI之ViewPager禁止滑动
- 表连接 join和(+)、union和uion all
- easyUi --修改
- easyUi 添加
- iOS UISearchBar去掉灰色部分
- map 按value排序
- UIImageView------Android中的imageview
- UISearchBar 支持空搜索