其實這倆按理說應該是一樣的,都是射線,但是神奇的是,OnMouseDown只需要在物體上加上Collider就能被觸發,但是OnPointerDown()要加的東西就多了。
如下圖所示,我只有Cube和Light,然後MainCamera上啥都沒有:
難道OnMouseDown()默認在內部已經加上了射線了嗎?感覺OnMouseDown()的相關源碼被封裝的比較緊密,內部實現無法窺探。很顯然它是屬於Mono的。
https://docs.unity3d.com/ScriptReference/MonoBehaviour.html
https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnMouseDown.html
然後就是OnPointerDown(),這個就是屬於EventSystem那塊兒的了 。
Physics Raycaster(放在Camera下)、EventSystem組件(隨意,最好自己搞一個EventSystem Object放在Hierarchy面板下)一個都不能少,然後TestMouse.cs中這個類還得繼承:
另外我發現如果是UI Canvas的話,應該把Graphic Raycaster組件放在Canvas上而不是放在Camera上。這樣不論是Canvas的渲染模式如何,都能判斷到點擊:
二更:
通過發射射線檢測是否擊中3D物體或者UI元素或者2D物體,並觸發點擊事件:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class SimuInputModule : PointerInputModule //模擬click事件
{
private GameObject simu_selectedObject;
private PointerEventData simu_InputpointerEvent;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(Input.GetMouseButtonDown(0))
{
Vector2 pos = new Vector2(Input.mousex,Input,mousey);
Debug.log(pos);
RaycastHitSimu(pos);
}
}
void RaycastHitSimu(Vector2 pos)
{
if(!RaycastHit3dObject(pos))
{
if(!RaycastHit2dObject(pos))
{
RaycastHitUI(pos);
}
}
}
//在該點發射攝像,判斷射線集中了哪個物體---物理射線
private bool RaycastHit3dObject(Vector2 pos)
{
bool tmp = false;
Debug.Log("檢測帶collider的3d物體");
Ray ray = Camera.main.ScreenPointToRay(pos);
Debug.Log(ray);
RaycastHit hit;
if(Physics.Raycast(ray,out hit))
{
//Debug.DrawLine(ray.origin,hit.point);
//simu_selectedObject = hit.collider.gameObject; //獲取點擊的物體
simu_selectedObject = hit.transform.gameObject;
Debug.Log(simu_selectedObject.name);
DistributeSimuClickEvent();
tmp = true;
}
return tmp;
}
private bool RaycastHit2dObject(Vector2 pos)
{
bool tmp = false;
Debug.Log("檢測帶collider的2d物體");
Debug.Log(Camera.main.ScreenToWorldPoint(pos));
//Vector2.zero表示2d物體應該在(0,0,1)的方向
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(pos), Vector2.zero);
Debug.Log(hit);
if(hit.collider!=null)
{
simu_selectedObject = hit.collider.gameObject;
Debug.Log(simu_selectedObject.name);
DistributeSimuClickEvent();
tmp = true;
}
return tmp;
}
//在該點發射射線,判斷UI元素是否被擊中,應該怎麼寫?
private bool RaycastHitUI(Vector2 pos)
{
Debug.Log("檢測UI元素");
bool tmp = false;
//Code to be place in a MonoBehaviour with a GraphicRaycaster component
GraphicRaycaster gr = this.GetComponent<GraphicRaycaster>(); //只能掛載到大的Canvas下
Debug.Log(gr);
//Create the PointerEventData with null for the EventSystem
PointerEventData ped = new PointerEventData(null);
//Set required parameters, in this case, mouse position
ped.position = pos;
//Create list to receive all results
List<RaycastResult> results = new List<RaycastResult>();
//Raycast it
gr.Raycast(ped, results);
Debug.Log(results.Count);
if(results.Count>0)
{
tmp = true;
for(int i=0;i<results.Count;i++){
Debug.Log(results[i].gameObject);
simu_selectedObject = results[i].gameObject;
DistributeSimuClickEvent();
}
}
return tmp;
}
//在物體或者UI上分發(觸發)點擊事件,這樣遊戲邏輯裏的OnMouseDown()或者OnPointerClick()就能直接被觸發
void DistributeSimuClickEvent()
{
ExecuteEvents.Execute(simu_selectedObject,new PointerEventData(eventSystem),ExecuteEvents.pointerClickHandler);
}
//這個函數會每幀都執行,一直在執行
public override void Process()
{
//if (simu_selectedObject == null)
//{
//Debug.Log("沒有選中物體!");
// return;
// }
//ExecuteEvents.Execute (m_TargetObject, new BaseEventData (eventSystem), ExecuteEvents.moveHandler);
//else
//{
// ExecuteEvents.Execute(simu_selectedObject, new PointerEventData(eventSystem), ExecuteEvents.pointerClickHandler);
// }
}
}
場景中有些東西需要強調一下:
1、Camera下面需要Physics Raycaster、Physics 2D Raycaster,這樣纔可以發射3d物理射線以及2d物理射線,注意Camera的位置
2、GameManager是我創建的一個Canvas,當然它也相當於一個新的EventSystem。注意所有的UI元素都應該在它下面,只有這樣才能找到那些需要檢測到鼠標點擊的UI。MyCanvas檢測在UI射線檢測中檢測不到,估計是兩個Canvas就會衝突。
3、2D物體要想被射線檢測到和3D還是有一定區別的,注意我現在場景中的2D的位置:
4、注意整體的位置,要想2d或者3d物體以及UI元素都被攝像機看到,位置需要注意一下,然後就是Canvas的渲染模式是Screen Space-Camera模式
ok,那接下來看看這些3d物體、2d物體、UI元素是否可以被觸發到模擬點擊事件:
//該文件放在2d物體上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class Test2D : MonoBehaviour,IPointerClickHandler
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("2D OnPointerClick");
}
}
//該文件放在button上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class TestButton : MonoBehaviour,IPointerClickHandler
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("Button OnPointerClick");
}
}
//該文件放在3d物體上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class TestMouse : MonoBehaviour,IPointerClickHandler
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
void OnMouseDown()
{
Debug.Log("OnMouseDown");
}
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("OnPointerClick");
}
}
//該文件放在MyCanvas上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class TestCanvas : MonoBehaviour,IPointerClickHandler
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
void OnMouseDown()
{
Debug.Log("Canvas OnMouseDown");
}
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("Canvas OnPointerClick");
}
}
測試結果如下(場景如下):
點擊綠色的3dCube的時候,(404.7,889.5)是屏幕座標,Origin將近與Camera的位置,Dir不清楚是幹啥 的:
點擊黃色的2d星星的時候,(747.2,710.6)是屏幕座標,(0,1,-60)是ScreenToWorldPoint(pos)以後的座標,其實還是將近與Camera的位置,也就是說從相機的位置,向Vector2.Zero的方向發射線:
點擊紅色的Button的時候,你會發現,臥槽,爲什麼還是2d????:
我發現是把2d的星星移動到(0,0,-1)的位置才導致這樣的,如果把星星移到其他地方,就會檢測到UI,但是檢測不到2d星星了。
觀察上面的三次輸出,不知道你有沒有發現,Origin:Dir的變化:
可以看到Origin基本都一樣,是Camera的位置,而Dir的正負值的分佈讓人懷疑Canvas的座標是這樣的:
所以感覺還是射線發射的原理問題,在檢測2d的腳本里添加了這個:
發現不論是點擊2d星星還是點擊紅色button,還是點擊任何Canvas的灰色位置,hit.point都一樣。都是(0,1),而Camera的位置就是(0,1,-60)。這說明在判斷2d射線的時候,攝像機是在自己的位置,非常直的發射的射線。也就是不管你真實的鼠標點擊在哪裏,只要程序運行到判斷2d的位置,攝像機都會發射一條直着的射線。
畢竟我的程序判斷順序是:
1---3d
2---2d
3---UI
所以還是這句話的問題,鬼知道爲什麼一定要Vector2.zero,這樣的話,我把2d的星星移到其他位置就檢測不到星星了:
比如說這樣的情況,我移動了星星,然後點擊星星,這樣程序跑到檢測2d的時候,攝像機還是從(0,1,-60)很直很直的發射射線,但是沒有打到2d collider上,UI元素上也沒有東西:
(注意這裏我是讓攝像機正交模擬一下打射線,實際運行的時候攝像機是Perspective的)
這時候點擊button是可以的:
歸根接底還是2d判斷的方法很奇怪,必須讓2d物體在(0,0)或者(0,1)和攝像機(x,y)位置很相近的地方,還是那塊兒代碼有問題。
我把判斷流程變化了一下,雖然可以了。但是2d的星星依然需要處於Canva中間的位置:
三更:
關於2d物體檢測的方法應該是這樣的。原理很簡單,ray表示一條射線,從攝像機開始發射,然後ray的發射方向是根據鼠標點擊的位置而改動的,然後變量改成2d 的就行了。這樣整體程序就完美了。星星移動到哪裏都可以。:
參考博客:
http://blog.sina.com.cn/s/blog_15875f12e0102y12m.html
https://www.cnblogs.com/isayes/p/6370168.html