UI和3D物体重叠的场景

UI和3D物体重叠

此时点击UI,我们需要实现不同的情况:

  1. UI响应,3D物体不响应
  2. UI和3D物体同时响应
  3. UI对鼠标左键右键有不同响应,3D物体也对左键右键有不同的响应,但是打开UI时关闭3D物体对鼠标的响应

打开Unity,新建默认3D工程,在工程内新建Scripts文件夹,在Scripts文件夹文件夹内部新建UIAnd3D文件夹,下面的脚本都在这个文件夹内。

把SampleScene改名为“UIAnd3D”,作为第一个演示场景。

UI和3D物体同时响应

我们给3D物体挂载Click3D脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using UnityEngine;

public class Click3D : MonoBehaviour
{
private int mColorIdx;
private MeshRenderer mRenderer;
private void Start()
{
mColorIdx = 0;
mRenderer = GetComponent<MeshRenderer>();
}
private void OnMouseDown()
{
ChangeMatColor();
}
void ChangeMatColor()
{
if (mColorIdx == 0)
{
mRenderer.material.SetColor("_Color", Color.black);
}
else
{
mRenderer.material.SetColor("_Color", Color.white);
}
mColorIdx = mColorIdx == 0 ? 1 : 0;
}
}

给UI的图片Image挂载UIClick脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class ClickUI : MonoBehaviour, IPointerClickHandler
{
private int mColorIdx;
private Image mImage;

private void Start()
{
mColorIdx = 0;
mImage = GetComponent<Image>();
}
public void OnPointerClick(PointerEventData eventData)
{
ChangeColor();
}
private void ChangeColor()
{
if (mColorIdx == 0)
{
mImage.color = Color.blue;
}
else
{
mImage.color = Color.red;
}
mColorIdx = mColorIdx == 0 ? 1 : 0;
}
}

此时UI和3D物体同时响应点击:

同时响应

Physics Raycaster

UI遮挡3D物体的响应

物理投射器是UGUI配套的Event System提供的组件,能让3D物体也来监听Event System。

给当前场景的Main Camera挂载Physics Raycaster组件

Canvas挂载的是Graphic Raycaster组件,和Physic Paycaster不同的是,前者投射的射线只会响应继承了Graphic基类的UI组件(如Image、Text等)

我们给3D物体挂载Click3DByRaycast脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using UnityEngine;
using UnityEngine.EventSystems;

public class Click3DByRaycast : MonoBehaviour ,IPointerClickHandler
{
private int mColorIdx;
private MeshRenderer mRenderer;

void Start()
{
mColorIdx = 0;
mRenderer = GetComponent<MeshRenderer>();
}

public void OnPointerClick(PointerEventData eventData)
{
ChangeMatColor();
}
void ChangeMatColor()
{
if (mColorIdx == 0)
{
mRenderer.material.SetColor("_Color", Color.black);
}
else
{
mRenderer.material.SetColor("_Color", Color.white);
}
mColorIdx = mColorIdx == 0 ? 1 : 0;
}
}

除了写代码,也可以给场景中的3D物体添加Event Trigger组件来实现监听。

此时我们再运行点击就会发现,UI遮挡了3D物体的响应

UI和3D物体同时响应

即使使用Raycaster,也是可以做到UI和3D物体同时响应的,因为EventSystem内部其实已经有了投射的所有对象。

修改UIClick脚本,我们使用EventSystem.current.RaycastAll来获取投射下的所有物体,再使用ExecuteEvents.Execute执行投射物体的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void OnPointerClick(PointerEventData eventData)
{
ChangeColor();
ExecuteAll(eventData);//+++
}
void ExecuteAll(PointerEventData eventData)//+++
{
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventData, results);//获取当前投射的射线下的所有物体
foreach (RaycastResult result in results)
{
if(result.gameObject != gameObject)
{
//除当前物体的所有射线中的物体,执行pointerClickHandler方法
ExecuteEvents.Execute(result.gameObject, eventData, ExecuteEvents.pointerClickHandler);
}
}
}

需要注意的是,即使不自己获取,我们在使用Raycast点击场景的物体时,EventSystem每次点击也是会获取点击的所有物体,只不过在其内部把所有的物体排序后,只返回在最上面的物体。

此时,UI和3D物体同时响应了点击事件。

判断当前投射物体是不是UI

按照上面的代码,使用Physics Raycaster应该能解决UI和3D物体重叠的问题,但是现实游戏中3D物体是需要响应左键右键的,比如右键点击场景空白处,3D物体需要移动到目的地,而如果空白处上方有一个UI,3D物体则不移动。我们需要判断当前点击的物体是不是UI。

为了演示,我们给场景中的3D方块添加ClickMouse脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class ClickMouse : MonoBehaviour
{
private int mColorIdx;
private MeshRenderer mRenderer;
private GraphicRaycaster mRaycaster;
void Start()
{
mColorIdx = 0;
mRenderer = GetComponent<MeshRenderer>();
mRaycaster = FindObjectOfType<GraphicRaycaster>();
}

void Update()
{
if (Input.GetMouseButtonDown(0))
{
ChangeMatColor();
}
}
void ChangeMatColor()
{
if (mColorIdx == 0)
{
mRenderer.material.SetColor("_Color", Color.black);
}
else
{
mRenderer.material.SetColor("_Color", Color.white);
}
mColorIdx = mColorIdx == 0 ? 1 : 0;
}
}

此时我们不管按什么地方,这个物体都会变色。

修改ClickMouse脚本

这时我们自定义一个PointerEventData,把当前鼠标位置传进去,看看是不是有UI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void Update()
{
if (Input.GetMouseButtonDown(0) && !IsUI())
{
ChangeMatColor();
}
}

private bool IsUI()
{
PointerEventData eventData = new PointerEventData(EventSystem.current);
eventData.pressPosition = Input.mousePosition;//这里的坐标是否需要转换待定
eventData.position = Input.mousePosition;

List<RaycastResult> results = new List<RaycastResult>();
mRaycaster.Raycast(eventData, results);
return results.Count > 0;
}

此时如果我们点击UI,UI响应而3D物体不变色了