unity的ugui其实严格意义上是不需要我们去进行适配的,它确实帮助我们做了很多很多的工作,我们只要简单的拖拉几下就可以搞定适配的问题(这点在比ngui强了不是一点半点,不愧是unity提供的从底层开始的解决方案)。
但是总是会有那么些特殊的蛋痛需求,还是需要让我们来考虑适配的问题的。
假设我们要做一个类似iphone的AssistiveTouch
悬浮窗时,这个东西有个特点,那就是它不能出屏,出屏之后需要再弹回到屏幕范围内,这个时候,你想不处理适配的问题都不行了。。。
如图那个黑边白球:
那么ugui里我们要怎么办呢?
首先我们要考虑2点。
1,我们的ui资源比例是什么?
2,我们要适配的屏幕比例又是什么?
假设我们资源的图是按照960x640(3:2)来绘制的,而我们的屏幕是1920x1080(16:9),我们是不能单纯的用1920x1080屏幕的尺寸来限定运动范围,因为这2种屏幕的比例是不同的,我们在960x640屏幕上 (50,50) 像素的点,跟在1920x1080屏幕上的位置那是2个地方,所以你会蛋痛的发现,你的位置永远的都对不上。。。
但是我们来看看我们知道哪些有用的信息,我们知道2个屏幕的尺寸呀,所以我们可以根据它们之间的差距来转换呀!
思路很简单,就是如果我用 960 / 1920 = ? 宽的比乘以一个1920元素的尺寸或位置,那不就是等于960的尺寸和位置了。
道理很简单,但是谁除谁,也不是能随随便便那么来的,因为我们的屏幕适配是由ugui来处理的,所以我们算比的方式,一定要跟ugui的适配是相一致的,才能保证最后的结果是没有偏差的。
如果ugui是以高为适配的,你算得是宽的比,你觉得这2屏幕能对的上吗?
以上这些重要的信息都是来自于 Canvas Scaler 这个组件上,它负责了ugui中对各种尺寸屏幕的像素适配(这里我用的是Scale with Screen Size模式,自建单独的ui摄像机)
说了这么多,这里就上个我写的浮动球的代码:
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityEngine.Events;
public class UIBtnCloud : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
/// <summary>
/// 按下后超过这个时间则认定为"长按"
/// </summary>
public float interval = 0.1f;
/// <summary>
/// 是否只调用一次
/// </summary>
public bool invokeOnce = false;
// 按下标志
private bool isPointerDown = false;
// 记录时间
private float recordTime;
//是否已经调用过
private bool hadInvoke = false;
// 点击事件
public UnityEvent onClick = new UnityEvent();
// 按住事件
public UnityEvent onPress = new UnityEvent();
private RectTransform m_rectTransform;
private float m_scale;
private float m_minX, m_minY, m_maxX, m_maxY;
void Start()
{
m_rectTransform = gameObject.GetComponent<RectTransform>();
// 计算分辨率的比例
CanvasScaler cs = XXX.GetInstance().CanvasScaler;
m_scale = cs.matchWidthOrHeight == 1 ?
(float)Screen.height / cs.referenceResolution.y :
(float)Screen.width / cs.referenceResolution.x;
// 范围
m_minX = m_rectTransform.sizeDelta.x / 2 * m_scale;
m_minY = m_rectTransform.sizeDelta.y / 2 * m_scale;
m_maxX = Screen.width - m_minX;
m_maxY = Screen.height - m_minY;
}
void Update()
{
// 一次机会已用完
if (invokeOnce && hadInvoke) return;
// 按下
if (isPointerDown)
{
// 算按住
if ((Time.time - recordTime) > interval)
{
hadInvoke = true;
onPress.Invoke();
}
}
}
#region 处理点击
// 按下
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
isPointerDown = true;
recordTime = Time.time;
}
// 抬起
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
{
isPointerDown = false;
hadInvoke = false;
// 算点击
if ((Time.time - recordTime) < interval)
{
onClick.Invoke();
}
}
// 离开
void IPointerExitHandler.OnPointerExit(PointerEventData eventData)
{
isPointerDown = false;
hadInvoke = false;
}
#endregion 处理点击 -------------------------------------
#region 处理拖拽
private void SetRectTransformPos(Vector2 position, Camera camera)
{
Vector3 globalMousePos;
if (RectTransformUtility.ScreenPointToWorldPointInRectangle(m_rectTransform, position, camera, out globalMousePos))
{
m_rectTransform.position = globalMousePos;
}
}
// 开始拖拽
void IBeginDragHandler.OnBeginDrag(PointerEventData eventData)
{
}
// 拖拽中
void IDragHandler.OnDrag(PointerEventData eventData)
{
//transform.position = eventData.position;
this.SetRectTransformPos(eventData.position, eventData.pressEventCamera);
}
// 结束拖拽
void IEndDragHandler.OnEndDrag(PointerEventData eventData)
{
// 位置
float x = m_rectTransform.position.x;
float y = m_rectTransform.position.y;
// 超出范围处理
if (x < m_minX)
{
x = m_minX;
}
else if (x > m_maxX)
{
x = m_maxX;
}
if (y < m_minY)
{
y = m_minY;
}
else if (y > m_maxY)
{
y = m_maxY;
}
Vector2 currentPosition = new Vector2(x, y);
// 设置坐标
this.SetRectTransformPos(currentPosition, eventData.pressEventCamera);
}
#endregion 处理拖拽 -------------------------------------
}
以上脚本挂在普通窗的button上就好了,很简单的。
外加没有做差值效果,只是生把出屏的球给拉回来,需要的同学可以自己给加上。
CanvasScaler cs = XXX.GetInstance().CanvasScaler;
获取 CanvasScaler 组件的方法,根据自己的项目去修改就好了,你的位置肯定不会跟我的一样~
这里我的资源都是按着960x640的标准来制作的(其实并不是,只是我瞎j8设的。。。),你要根据你自己的资源去调整参数,剩下的,就全是程序的事了。
PS:
顺便这里安利一下ugui,确实是比ngui好用,不过缺点是好多东西是需要自己手写的,像ngui里,对于这种出屏回弹的功能,其实就有现成的组件的,调参数就好。
这无形中其实增加了使用的门槛,不利于新手入门,尤其是不了解差值动画这个概念的同学,会限制他们制作各种各样精美动画的能力。(不是开玩笑,真有好多人搞不清楚差值动画这个概念。。。)
不过还是那句话,提高自己最重要,你也不能总靠别人不是?