3d 强制添加脚本_Unity游戏开发—UI与3D物体之间的事件响应处理

d319c046d376dbb1e68ffdea3ffa8cd2.png

0.前言

游戏开发中,我们可能经常需要处理UI或者模型触碰事件响应

有时两者会处于叠加关系,但是我们只想让其中一方响应,比如如下图情况

a7c5008a436d52454475afef5c6dd90b.gif
图1

有时我们也想让点击事件有穿透效果

86745069415c9e993ac5657ecc50e7b5.gif
图2

有时候我们也想让鼠标点击屏幕任意位置时模型都有响应,但是会被UI拦截。这种情况常用于处理系统UI或者技能UI不响应鼠标点击事件

1e49063adafb6a4d5918728702bd2797.gif
图3

1.实现思路

实现方法有很多,比如使用OnMouseDown方法,但是这里会有一些弊端,比如实现了这个方法后,UI不会对事件进行拦截。

这里我使用了一套统一的解决方案,可以同时解决以上所有情况,并且思路还比较清晰。

核心思路就是使用PhysicRaycaster组件和EventTrigger

首先GraphicRaycaster只能识别UGUI那一套继承自Graphic基类的组件,3D物体并不能使用。而我们需要一套通用的解决方案,使用PhysicRaycaster的话,所以两者都能响应。

2.单独响应的实现细节

注意,在此我只是提供一种思路,代码并不严谨,都是方便理解随便写的

首先需要把PhysicRaycaster组件挂在到摄像机上

b754ff1bd8d9fd47515864eb464d04b4.png

然后在给UI或者3D物体添加一个脚本,并在脚本中实现IPointHandler接口

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class Interaction : MonoBehaviour,IPointerClickHandler
{
    public InteracitonType interacitonType;
    
    private Image m_Image;
    private int m_Index = 0;
    
    void Start()
    {
        m_Image = GetComponent<Image>();
    }

    public void OnPointerClick(PointerEventData eventData)
    { 
        ChangeUIColorByPointClick();        
    }

    // 当点击UI的时候改变UI颜色
    private void ChangeUIColorByPointClick()
    {
        if (m_Index == 0)
        {
            m_Image.color = Color.red;
        }
        else
        {
            m_Image.color = Color.blue;
        }

        m_Index = m_Index == 0 ? 1 : 0;
    }
}

3D物体的脚本写法同理,改变材质球的颜色,然后把对应的脚本添加到对应的对象上就可以实现图一所示效果,自己只负责自己的响应事件,互不影响。

3.事件穿透的实现细节

使用EventSystem.current.RaycastAll()方法可以检测到当前点击位置的所有对象

接下来去遍历这些对象,只要他们实现了IPointerHandler接口,就是使用ExecuteEvents.Execute方法去执行对应的点击事件

假如UI在最上面,那么就把这段代码加在UI脚本中,代码细节如下

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class Interaction : MonoBehaviour,IPointerClickHandler
{
    public void OnPointerClick(PointerEventData eventData)
    { 
        ChangeModelColorByClickOnUI(eventData);     
    }

    // 当点击模型上方的UI时,模型可以相应到点击事件
    private void ChangeModelColorByClickOnUI(PointerEventData eventData)
    {
        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(eventData, results);
        foreach (RaycastResult raycastResult in results)
        {
            if (raycastResult.gameObject != gameObject)
            {
                ExecuteEvents.Execute(raycastResult.gameObject, eventData, ExecuteEvents.pointerClickHandler);
            }
        }
    }
}

4.UI拦截屏幕滑动事件的实现细节

具体情况对应图3,当使用手或者鼠标滑动屏幕的时候,3D响应,当滑动过程中,触碰到UI或者系统UI的时候,停止对3d的响应。

这里使用GraphicRaycaster判断鼠标是否点击到了UI,当射线检测数量大于0时,说明检测到了UI。这样做的好处是如果项目中以上所有情况都会出现,也不会出现逻辑上的冲突,因为前面提到了GraphicRaycaster只能识别UGUI那一套继承自Graphic基类的组件,3D物体不会出现响应。

代码细节如下

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class Interaction : MonoBehaviour
{
    private GraphicRaycaster m_Raycaster;
    private MeshRenderer m_MeshRender;
    private int m_Index = 0;

    // Start is called before the first frame update
    void Start()
    {
        // 因为是做示例效果,此处并不严谨
        m_Raycaster = FindObjectOfType<GraphicRaycaster>();

        m_MeshRender = GetComponent<MeshRenderer>();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButton(0))
        {
            ChangeModelColorByMouseDown();
        }
    }

    // 当点击屏幕时,模型响应事件,但是点击到UI上时,ui拦截事件,模型不再响应
    private void ChangeModelColorByMouseDown()
    {   PointerEventData data = new PointerEventData(EventSystem.current);
        data.pressPosition = Input.mousePosition;
        data.position = Input.mousePosition;
        List<RaycastResult> results = new List<RaycastResult>();
        m_Raycaster.Raycast(data, results);
        if (results.Count == 0)
        {
            ChangeModelColorByPointClick();
        }
    }

    // 当点击模型时,模型可以响应点击事件
    private void ChangeModelColorByPointClick()
    {
        
        if (m_Index == 0)
        {
            m_MeshRender.material.SetColor("_Color", Color.blue);
        }
        else
        {
            m_MeshRender.material.SetColor("_Color", Color.red);
        }

        m_Index = m_Index == 0 ? 1 : 0;
    }
}

5.写在最后

这些思路都是笔者为了方便解释思路,临时编写的代码,可能有很多不严谨的地方,但是自觉足以给大家提供一个这类问题的统一解决方案,并且实践起来也并不复杂。

大家也可以尝试把这类问题统一封装成一个脚本,提供一个枚举类型,根据实际情况选择对应的方案,达到一劳永逸的效果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值