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

unity的ugui其实严格意义上是不需要我们去进行适配的,它确实帮助我们做了很多很多的工作,我们只要简单的拖拉几下就可以搞定适配的问题(这点在比ngui强了不是一点半点,不愧是unity提供的从底层开始的解决方案)。
但是总是会有那么些特殊的蛋痛需求,还是需要让我们来考虑适配的问题的。
假设我们要做一个类似iphone的AssistiveTouch悬浮窗时,这个东西有个特点,那就是它不能出屏,出屏之后需要再弹回到屏幕范围内,这个时候,你想不处理适配的问题都不行了。。。

如图那个黑边白球:
unity ugui 与不同屏幕进行像素适配的问题_第1张图片

那么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屏幕能对的上吗?

unity 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
{
    /// 
    /// 按下后超过这个时间则认定为"长按"
    /// 
    public float interval = 0.1f;
    /// 
    /// 是否只调用一次
    /// 
    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();

        // 计算分辨率的比例
        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里,对于这种出屏回弹的功能,其实就有现成的组件的,调参数就好。
这无形中其实增加了使用的门槛,不利于新手入门,尤其是不了解差值动画这个概念的同学,会限制他们制作各种各样精美动画的能力。(不是开玩笑,真有好多人搞不清楚差值动画这个概念。。。)
不过还是那句话,提高自己最重要,你也不能总靠别人不是?

你可能感兴趣的:(Unity,unity,ugui,Assistive,Touch,浮动窗)