UI和3D物体重叠的场景

此时点击UI,我们需要实现不同的情况:
- UI响应,3D物体不响应
- UI和3D物体同时响应
- 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) { 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物体不变色了