Unity ugui与不同屏幕进行像素适配的问题

发表于2018-08-15
评论0 6.7k浏览
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的标准来制作的,你要根据你自己的资源去调整参数,剩下的,就全是程序的事了。来自:https://blog.csdn.net/WPAPA/article/details/52209834

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