當場景中的3D物體需要響應點擊,但同時有UI顯示時,存在判斷點擊是在3D物體上還是UI上的問題,辦法如下:
1. 射線檢測所有2D 3D物體,有2D物體被檢測到時表明當前有UI。但無論Physics2D.Raycast()還是Physics.Raycast()都只能檢測到含有Collider組件的物體,普通UI如Image Button等一般用射線是不起作用的。EventSystem.current.RaycastAll()可以將當前屏幕上的所有可檢測的物體全部檢測到,該方法需要自己構造一個PointerEventData參數傳入檢測的點坐標:
1 PointerEventData pointerData = new PointerEventData (EventSystem.current); 2 pointerData.position = Input.mousePosition; 3 List<RaycastResult> results = new List<RaycastResult> (); 4 EventSystem.current.RaycastAll (pointerData, results);
2. 在3D物體的Camera上掛上PhysicsRaycaster組件即可讓3D物體上的腳本響應IPointerClickHandler等事件了(原理同Canvas上都掛有GraphicRaycaster組件一下)。
當3D物體之上有UI時則會自動被UI事件所攔截,因為一般UI的顯示級別(Camera或overlay)是高於場景中的3D物體的。
這樣就當點擊在UI上時只響應UI事件了,如果同時也要響應3D物體上的事件則只能再次用射線檢測點擊處是否有3D物體(3D物體一般都有Collider)。
3. 詳見www.xuanyusong.com/archives/3327,如下代碼
1 void Update ()
2 { 3 if (Input.GetMouseButtonDown (0) || (Input.touchCount > 0 && Input.GetTouch (0).phase == TouchPhase.Began)) 4 { 5 #if UNITY_IOS || UNITY_ANDROID 6 if (EventSystem.current.IsPointerOverGameObject (Input.GetTouch (0).fingerId)) 7 #else 8 if (EventSystem.current.IsPointerOverGameObject ()) 9 #endif 10 Debug.Log ("當前觸摸在UI上"); 11 12 else 13 Debug.Log ("當前沒有觸摸在UI上"); 14 } 15 }
但EventSystem.current.IsPointerOverGameObject ()只能檢測到有event存在的組件如Button等,普通Image等UI是不能被檢測的,故該方法僅用於簡單幾個按鈕等可交互的UI與3D物體間的判斷,當一個大的UI窗口打開時點擊在非按鈕上如背景圖時則失效。
另:
讓3D物體響應點擊有個MonoBehaviour自帶的消息函數MonoBehaviour.OnMouseDown()可簡單實現,但腳本中最好不要有OnMouseXXX的消息處理函數,此類函數會影響游戲性能。(當存在此類函數時build打包結束時有黃色警告提示性能影響)
Graphics Raycaster的Raycast是個虛函數,可以寫個Graphics Raycaster的派生類,在默認的Raycast操作執行完以后,用自定義的layer進行篩選,把不需要響應的gameobject去掉。這樣就可以實現只響應某個layer的需求了。新手引導中只要把需要響應的gameobject設置為特定layer就行了。
實現ICanvasRaycastFilter接口並掛在UI組件上,當有任何UI事件觸發時可以實現自定義的判斷,如:
1 public class CustomRay : MonoBehaviour, ICanvasRaycastFilter 2 { 3 public bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera) 4 { 5 //.... 6 ... 7 return true; 8 //.... 9 ... 10 return false; 11 } 12 }