当前位置: 首页 > 工具软件 > WorldSpace > 使用案例 >

Unity Canvas设置为WorldSpace时,UI事件中鼠标位置信息偏移的问题

缪宪
2023-12-01

需求描述:在Canvas下使用RenderTexture渲染三维场景,同时需要三维场景中需要响应鼠标事件点击,移入,移出等

解决方案:重写RawImage作为RenderTexture载体,实现鼠标事件接口

using UnityEngine.EventSystems;
using UnityEngine.UI;

public class RawImageEx : RawImage,IPointerEnterHandler, IPointerClickHandler, IPointerExitHandler
{
    public void OnPointerClick(PointerEventData eventData)
    {
        RenderTextureEventTransfor.Instance.BroadCast(this.name, RenderTextureEventTransfor.EventType.Click,this.rectTransform, eventData);
    }

    public void OnPointerEnter(PointerEventData eventData)
    {
        RenderTextureEventTransfor.Instance.BroadCast(this.name, RenderTextureEventTransfor.EventType.Enter, this.rectTransform, eventData);
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        RenderTextureEventTransfor.Instance.BroadCast(this.name, RenderTextureEventTransfor.EventType.Exit, this.rectTransform, eventData);
    }
}

上面代码中的RenderTextureEventTransfor是个单例,用来管理事件,里面只有注册,广播两个方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class RenderTextureEventTransfor : SingletonMono<RenderTextureEventTransfor>
{
    public enum EventType
    {
        Click,
        Enter,
        Exit
    }
    private Dictionary<string, Action<RectTransform,PointerEventData>> rtClick = new Dictionary<string, Action<RectTransform,PointerEventData>>();
    private Dictionary<string, Action<RectTransform,PointerEventData>> rtEnter = new Dictionary<string, Action<RectTransform,PointerEventData>>();
    private Dictionary<string, Action<RectTransform,PointerEventData>> rtExit = new Dictionary<string, Action<RectTransform,PointerEventData>>();

    public void Register(string rtName, EventType eventType, Action<RectTransform, PointerEventData> callBack)
    {
        switch (eventType)
        {
            case EventType.Click:
                if (!rtClick.ContainsKey(rtName))
                {
                    rtClick.Add(rtName, callBack);
                }
                else
                {
                    rtClick[rtName] += callBack;
                }
                break;
            case EventType.Enter:
                if (!rtEnter.ContainsKey(rtName))
                {
                    rtEnter.Add(rtName, callBack);
                }
                else
                {
                    rtEnter[rtName] += callBack;
                }
                break;
            case EventType.Exit:
                if (!rtExit.ContainsKey(rtName))
                {
                    rtExit.Add(rtName, callBack);
                }
                else
                {
                    rtExit[rtName] += callBack;
                }
                break;
            default:
                break;
        }

    }
    public void BroadCast(string rtName, EventType eventType,RectTransform rtRect, PointerEventData data)
    {
        switch (eventType)
        {
            case EventType.Click:
                foreach (var item in rtClick)
                {
                    if (item.Key == rtName)
                        item.Value?.Invoke(rtRect,data);
                }
                break;
            case EventType.Enter:
                foreach (var item in rtEnter)
                {
                    if (item.Key == rtName)
                        item.Value?.Invoke(rtRect,data);
                }
                break;
            case EventType.Exit:
                foreach (var item in rtExit)
                {
                    if (item.Key == rtName)
                        item.Value?.Invoke(rtRect,data);
                }
                break;
            default:
                break;
        }

    }
}

需要接收触发事件的地方先进行注册,然后再回调中处理逻辑

    void Awake()
    {
        RenderTextureEventTransfor.Instance.Register(rtName, RenderTextureEventTransfor.EventType.Click, OnTriggerMouseClick);
        RenderTextureEventTransfor.Instance.Register(rtName, RenderTextureEventTransfor.EventType.Exit, OnTriggerMouseExit);
    }

最后在回调中根据传过来的点击事件信息,对鼠标信息做坐标系转换:

    private void OnTriggerMouseClick(RectTransform rtRect, PointerEventData data)
    {
        if(RectTransformUtility.ScreenPointToLocalPointInRectangle(rtRect, data.position, Camera.main,out Vector2 pos))
        {
            //此处需要根据rtRect的锚点来做对应的偏移
            pos += (rtRect.rect.size / 2);
            var rate = pos / (rtRect.rect.size);
            Ray ray = cam.ViewportPointToRay(rate);
            RaycastHit raycastHit;
            if (Physics.Raycast(ray, out raycastHit))
            {
                Debug.DrawLine(ray.origin, raycastHit.point, Color.cyan);
                GameObject go = raycastHit.transform.gameObject;
            }
        }
    }

注意:使用RectTransformUtility.ScreenPointToLocalPointInRectangle(rtRect, data.position, Camera.main,out Vector2 pos)接口做屏幕到rtRect的坐标系转换,其中相机为Canvas的渲染相机,得到的pos是相对rtRect的左下角的位置,如果rtRect的锚点在中心,需要加上其大小的一半,以此类推;使用RenderTexture的渲染相机根据得到的位置来做射线检测:Ray ray = cam.ViewportPointToRay(rate)。

 类似资料: