小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

【新提醒】【轉(zhuǎn)載】Unity UGUI鼠標(biāo)穿透UI問題

 姜家平的黃金屋 2015-08-10
acondess 2015-8-9 13:57
樹靜風(fēng)止: Input.GetMouseButtonDown(0)是只要鼠標(biāo)左鍵按下了就為true,跟UGUI沒半毛錢關(guān)系!
頂!偏題了   0分
回復(fù)金絲吊燕 2015-8-9 17:09
樹靜風(fēng)止: Input.GetMouseButtonDown(0)是只要鼠標(biāo)左鍵按下了就為true,跟UGUI沒半毛錢關(guān)系!
對(duì)啊……如果是要點(diǎn)擊物體,樓主可以用OnMouseDown(),UI的話有Button點(diǎn)擊事件啊……

【轉(zhuǎn)載】Unity UGUI鼠標(biāo)穿透UI問題

熱度 12638 2015-8-6 20:34 |個(gè)人分類:學(xué)習(xí)記錄| 沖突

簡(jiǎn)述

 

最近在用UGUI的時(shí)候遇到了鼠標(biāo)穿透的問題,就是說在UGUI和3D場(chǎng)景混合的情況下,點(diǎn)擊UI區(qū)域同時(shí)也會(huì) 觸發(fā)3D中物體的鼠標(biāo)事件。比如下圖中

這里給Cube加了一個(gè)鼠標(biāo)點(diǎn)擊改變顏色的代碼,如下

復(fù)制代碼
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            GetComponent<Renderer>().material.color = new Color(Random.value, Random.value, Random.value, 1.0f);
        }
    }
復(fù)制代碼

運(yùn)行一下,會(huì)發(fā)現(xiàn)只要有鼠標(biāo)點(diǎn)擊(任何位置點(diǎn)擊),Cube的顏色就會(huì)改變,根據(jù)代碼我們知道這也是必然的,但是問題是如果Cube是一個(gè)3D世界中的mesh或者terrain,而button是UI的話也同樣會(huì)出現(xiàn)同樣的問題。

在游戲開發(fā)中我們的UI是始終出現(xiàn)在屏幕的,如果在一個(gè)戰(zhàn)斗場(chǎng)景中用戶點(diǎn)了UI戰(zhàn)斗場(chǎng)景中的物體也會(huì)作出響應(yīng)肯定是有問題的!

其實(shí)關(guān)于這個(gè)問題網(wǎng)上有不少解決方法了,但是總感覺沒有一個(gè)是適合我的需求,或者說沒有一個(gè)最好的答案。

其中提到最多的是利用EventSystem.current.IsPointerOverGameObject()來判斷,這個(gè)方法的意義是判斷鼠標(biāo)是否點(diǎn)到了GameObject上面,這個(gè)GameObject包括UI也包括3D世界中的任何物體,所以他只能判斷用戶是都點(diǎn)到了東西。對(duì)于本文中的問題意義不是很大。那么這個(gè)問題到底該怎么解決呢?

 

原理

 

解決方法最終還是離不開射線檢測(cè),不過UGUI中已經(jīng)封裝了針對(duì)UI部分的射線碰撞的功能,那就是GraphicRaycaster類。里面有個(gè)Raycast方法如下,最終就是將射線碰撞到的點(diǎn)添加進(jìn)resultAppendList數(shù)組。

復(fù)制代碼
        public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
        {
            if (canvas == null)
                return;

            // Convert to view space
            Vector2 pos;
            if (eventCamera == null)
                pos = new Vector2(eventData.position.x / Screen.width, eventData.position.y / Screen.height);
            else
                pos = eventCamera.ScreenToViewportPoint(eventData.position);

            // If it's outside the camera's viewport, do nothing
            if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f)
                return;

            float hitDistance = float.MaxValue;

            Ray ray = new Ray();

            if (eventCamera != null)
                ray = eventCamera.ScreenPointToRay(eventData.position);

            if (canvas.renderMode != RenderMode.ScreenSpaceOverlay && blockingObjects != BlockingObjects.None)
            {
                float dist = eventCamera.farClipPlane - eventCamera.nearClipPlane;

                if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All)
                {
                    RaycastHit hit;
                    if (Physics.Raycast(ray, out hit, dist, m_BlockingMask))
                    {
                        hitDistance = hit.distance;
                    }
                }

                if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All)
                {
                    RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction, dist, m_BlockingMask);

                    if (hit.collider != null)
                    {
                        hitDistance = hit.fraction * dist;
                    }
                }
            }

            m_RaycastResults.Clear();
            Raycast(canvas, eventCamera, eventData.position, m_RaycastResults);

            for (var index = 0; index < m_RaycastResults.Count; index++)
            {
                var go = m_RaycastResults[index].gameObject;
                bool appendGraphic = true;

                if (ignoreReversedGraphics)
                {
                    if (eventCamera == null)
                    {
                        // If we dont have a camera we know that we should always be facing forward
                        var dir = go.transform.rotation * Vector3.forward;
                        appendGraphic = Vector3.Dot(Vector3.forward, dir) > 0;
                    }
                    else
                    {
                        // If we have a camera compare the direction against the cameras forward.
                        var cameraFoward = eventCamera.transform.rotation * Vector3.forward;
                        var dir = go.transform.rotation * Vector3.forward;
                        appendGraphic = Vector3.Dot(cameraFoward, dir) > 0;
                    }
                }

                if (appendGraphic)
                {
                    float distance = 0;

                    if (eventCamera == null || canvas.renderMode == RenderMode.ScreenSpaceOverlay)
                        distance = 0;
                    else
                    {
                        // http:///a06-_intersect-2.html
                        distance = (Vector3.Dot(go.transform.forward, go.transform.position - ray.origin) / Vector3.Dot(go.transform.forward, ray.direction));

                        // Check to see if the go is behind the camera.
                        if (distance < 0)
                            continue;
                    }

                    if (distance >= hitDistance)
                        continue;

                    var castResult = new RaycastResult
                    {
                        gameObject = go,
                        module = this,
                        distance = distance,
                        index = resultAppendList.Count,
                        depth = m_RaycastResults[index].depth,
                        sortingLayer =  canvas.sortingLayerID,
                        sortingOrder = canvas.sortingOrder
                    };
                    resultAppendList.Add(castResult);
                }
            }
        }
復(fù)制代碼

從這個(gè)方法開始深入查看Unity UGUI源碼你會(huì)發(fā)現(xiàn),其實(shí)每個(gè)組件在創(chuàng)建的時(shí)候已經(jīng)被添加進(jìn)了一個(gè)公共列表,UGUI 源碼中的GraphicRegistry類就是專門干這件事的。再看下Graphic類中的OnEnable方法

復(fù)制代碼
        protected override void OnEnable()
        {
            base.OnEnable();
            CacheCanvas();
            GraphicRegistry.RegisterGraphicForCanvas(canvas, this);

#if UNITY_EDITOR
            GraphicRebuildTracker.TrackGraphic(this);
#endif
            if (s_WhiteTexture == null)
                s_WhiteTexture = Texture2D.whiteTexture;

            SetAllDirty();

            SendGraphicEnabledDisabled();
        }
復(fù)制代碼

看這句GraphicRegistry.RegisterGraphicForCanvas(canvas, this);就是注冊(cè)需要做射線檢測(cè)的UI組件。再看他內(nèi)部是如何工作的

復(fù)制代碼
        public static void RegisterGraphicForCanvas(Canvas c, Graphic graphic)
        {
            if (c == null)
                return;

            IndexedSet<Graphic> graphics;
            instance.m_Graphics.TryGetValue(c, out graphics);

            if (graphics != null)
            {
                graphics.Add(graphic);
                return;
            }

            graphics = new IndexedSet<Graphic>();
            graphics.Add(graphic);
            instance.m_Graphics.Add(c, graphics);
        }
復(fù)制代碼

不過,問題又來了,為什么是添加進(jìn)列表的對(duì)象都是Graphic類型呢?這跟ScrollRect,Button,Slider這些有關(guān)嗎?其實(shí),這就跟UGUI的類繼承關(guān)系有關(guān)了,其實(shí)我們使用的UGUI中的每個(gè)組件都是繼承自Graphic或者依賴一個(gè)繼承自Graphic的組件

看一下UGUI的類層次結(jié)構(gòu)就會(huì)一目了然,如下

看圖就會(huì)更加清楚,在這我們可以把我們用到的UGUI的所有組件分為兩類,1.是直接繼承自Graphic的組件。2.是依賴于1的組件"[RequireComponent(typeof(Griphic))]",仔細(xì)想想會(huì)發(fā)現(xiàn),所有組件都屬于這兩種中的某一種。

所以對(duì)所有Graphic進(jìn)行Raycast其實(shí)就相當(dāng)于對(duì)所有UI組件進(jìn)行Raycast。

結(jié)合上面的知識(shí)所以,解決這個(gè)問題最好的方法是根據(jù),UGUI的射線碰撞來做。這樣會(huì)比較合理。

 

解決方案

 

這里我們直接在使用Input.GetMouseButtonDown(0)的地方加了一個(gè)檢測(cè)函數(shù),CheckGuiRaycastObjects,如下

復(fù)制代碼
    bool CheckGuiRaycastObjects()
    {
        PointerEventData eventData = new PointerEventData(Main.Instance.eventSystem);
        eventData.pressPosition = Input.mousePosition;
        eventData.position = Input.mousePosition;

        List<RaycastResult> list = new List<RaycastResult>();
        Main.Instance.graphicRaycaster.Raycast(eventData, list);
        //Debug.Log(list.Count);
        return list.Count > 0;
    }
復(fù)制代碼

不過在使用時(shí)需要先獲取兩個(gè)加粗顯示的變量,graphicRaycaster和eventSystem。

這兩個(gè)變量分別對(duì)應(yīng)的是Canvas中的GraphicRaycaster組件,和創(chuàng)建UI時(shí)自動(dòng)生成的“EventSystem”中的EventSystem組件,用的是自己制定以下就可以。

然后在使用的時(shí)候可以這樣:

復(fù)制代碼
    void Update () 
    {
        if (CheckGuiRaycastObjects()) return;
        //Debug.Log(EventSystem.current.gameObject.name);
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;

            if (Physics.Raycast(ray, out hit))
            {
                //do some thing
            }
        }
    }    
復(fù)制代碼

 

還有一個(gè)需要注意的地方就是,在做UI的時(shí)候一般會(huì)用一個(gè)Panel做跟目錄,這個(gè)panel也會(huì)被添加到GraphicRegistry中的公共列表中,如果是這樣的話記得把list.Count>0改成list.Count>1,或者直接刪除Panel上的繼承自Graphic的組件。

這樣在結(jié)合著EventSystem.current.IsPointerOverGameObject()來使用就比較好了。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多