OnMouseDown()和OnPointerDown()

其實這倆按理說應該是一樣的,都是射線,但是神奇的是,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

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章