zoukankan      html  css  js  c++  java
  • (十)自定义GraphicRaycaster

    1.前言

    之前的文章中曾分析过unity事件系统中的rayCaster,其中GraphicRaycaster中曾分析过,射线在此类中的作用仅仅是为了检测与3D或者2D的碰撞距离,而ui检测时则使用如下代码:

    RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera)
    

    但是在VR中由于双目Camera问题,有时候会存在各种问题,比如射线检测时采用哪个Camera问题。所以比较容易解决的办法是直接采用射线检测。那么问题来了,如何解决ui的rect与射线的碰撞问题。unity为我们提供了PlaneRaycast方法。

    2.Plane射线检测

    Plane是结构体变量,首先在ui位置通过ui的位置和方向定义一个plane,然后定义一个射线ray。调用Plane的raycast放可以检测plane与ray是否相交,相交则返回相交位置与射线源点的距离center,然后通过center就可以获取到碰撞点。由于plane定义的是无限大的平面,所以检测碰撞点是否在ui的rect范围内,如果是则可以确定是在点击范围内。完整代码如下如下:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlaneCast : MonoBehaviour
    {
        public RectTransform targetPlane;
        public GameObject cube;
    
        Plane plane;
    
        void CheckOnce()
        {
            Ray ray = new Ray(transform.position, transform.forward);
    
            float center;
            if(plane.Raycast(ray, out center))
            {
                Debug.Log("Ray casted with d " + center);
                Vector3 position =  ray.GetPoint(center);
    
                if(targetPlane.rect.Contains(position))
                {
                    GameObject go = Instantiate(cube);
                    go.transform.position = position;
                    Debug.Log("Target contains");
                }
            }
        }
    
    	// Use this for initialization
    	void Start ()
        {
            plane = new Plane(targetPlane.forward, targetPlane.position);
            CheckOnce();
        }
    	
    	// Update is called once per frame
    	void Update ()
        {
    		if(Input.GetMouseButtonDown(0))
            {
                CheckOnce();
            }
    	}
    }
    
    

    3.GraphicRaycaster

    基于上述方法,对GraphicRaycaster做简单修改,即在原来通过判断点击点是否在UI范围内的方法改为射线检测。不过在计算时,如下代码中所有的坐标包括射线都转换到ui的局部坐标系内,这样plane就可以定义为Plane plane = new Plane(new Vector3(0, 0, -1), 0),而不用获取到ui的位置和方向。代码如下:

    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.EventSystems;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace VrInput
    {
        [RequireComponent(typeof(Canvas))]
        [DisallowMultipleComponent]
        public class UIRaycaster : GraphicRaycaster
        {
            struct GraphicHit
            {
                public Graphic graph;
                public Vector3 worldPos;
            }
            private Canvas _canvas = null;
            private Canvas canvas
            {
                get { return _canvas != null ? _canvas : _canvas = GetComponent<Canvas>(); }
            }
    
            private Camera _eventCamera = null;
            public override Camera eventCamera
            {
                get { return _eventCamera != null ? _eventCamera : _eventCamera = Camera.main; }
            }
    
            public GameObject RayEmitter;
    
            public bool DrawDebugRay = false;
            public float Distance = 2000;
    
            public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
            {
                if (enabled == false || RayEmitter == null)
                    return;
                Ray ray = new Ray(RayEmitter.transform.position, RayEmitter.transform.forward);
    
                float hitDistance = float.MaxValue;
    
                if (blockingObjects != BlockingObjects.None)
                {
                    float dist = Distance;
                    if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All)
                    {
                        var hits = Physics.RaycastAll(ray, dist, m_BlockingMask);
                        if (hits.Length > 0 && hits[0].distance < hitDistance)
                        {
                            hitDistance = hits[0].distance;
                        }
                    }
    
                    if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All)
                    {
                        var hits = Physics2D.GetRayIntersectionAll(ray, dist, m_BlockingMask);
    
                        if (hits.Length > 0 && hits[0].fraction * dist < hitDistance)
                        {
                            hitDistance = hits[0].fraction * dist;
                        }
                    }
                }
    
                List<GraphicHit> sortedGraphics = new List<GraphicHit>();
    
                var list = GraphicRegistry.GetGraphicsForCanvas(canvas);
                for (int i = 0; i < list.Count; ++i)
                {
                    GraphicHit hit;
                    hit.graph = null;
                    hit.worldPos = Vector3.zero;
    
                    Graphic g = list[i];
    
                    if (null == g || g.depth == -1 || !g.enabled || !g.raycastTarget || g.canvasRenderer.cull)
                    {
                        continue;
                    }
    
                    if (!RayGraphicIntersectFlat(ray, g, hitDistance, ref hit))
                    {
                        continue;
                    }
    
                    sortedGraphics.Add(hit);
                }
    
                sortedGraphics.Sort((g1, g2) => g2.graph.depth.CompareTo(g1.graph.depth));
    
                if (sortedGraphics.Count == 0)
                {
                    return;
                }
                if (DrawDebugRay)
                    Debug.DrawLine(ray.origin, sortedGraphics[0].worldPos, Color.green);
    
                for (int i = 0; i < sortedGraphics.Count; ++i)
                {
                    var castResult = new RaycastResult
                    {
                        gameObject = sortedGraphics[i].graph.gameObject,
                        module = this,
                        distance = (sortedGraphics[i].worldPos - ray.origin).magnitude,
                        index = resultAppendList.Count,
                        depth = sortedGraphics[i].graph.depth,
                        worldPosition = sortedGraphics[i].worldPos,
                        sortingLayer = canvas.sortingLayerID,
                        sortingOrder = canvas.sortingOrder,
                    };
                    resultAppendList.Add(castResult);
                }
            }
            private bool RayGraphicIntersectFlat(Ray ray, Graphic graphic, float dist, ref GraphicHit hit)
            {
                hit.graph = null;
    
                Ray localRay = ray;
    
                Matrix4x4 worldToLocal = graphic.transform.worldToLocalMatrix;
    
                localRay.origin = worldToLocal.MultiplyPoint(ray.origin);
                localRay.direction = worldToLocal.MultiplyVector(ray.direction);
    
                localRay.direction.Normalize();
    
                Rect rc = graphic.rectTransform.rect;
    
                float t = -1;
    
                if (!RayRectIntersect(localRay, rc, dist, out t))
                {
                    return false;
                }
    
                Matrix4x4 localToWorld = worldToLocal.inverse;
    
                hit.graph = graphic;
                hit.worldPos = localToWorld.MultiplyPoint(localRay.GetPoint(t));
    
                //Use Graphic.Raycast to detected whether the hit position has been discard by Mask2D or Mask
                return graphic.Raycast(eventCamera.WorldToScreenPoint(hit.worldPos), eventCamera);
            }
    
            public bool RayRectIntersect(Ray ray, Rect rc, float dist, out float t)
            {
    
                Plane plane = new Plane(new Vector3(0, 0, -1), 0);
    
                if (!plane.Raycast(ray, out t))
                {
                    return false;
                }
    
                if (t < 0 || t > dist)
                {
                    return false;
                }
    
                return rc.Contains(ray.GetPoint(t));
            }
        }
    }
    
    

    4.结语。。

    没有结语。。。。。。。。。。。。

  • 相关阅读:
    LeetCode
    算法
    GitHub
    GitHub
    git
    将博客搬家至CSDN
    base64与图片互转
    windows下mongodb数据库搭建过程遇到问题
    mongodb数据插入语句与navicat导入mongodb的json结构
    Visual C++安装失败解决:Error 0x80240017: Failed to execute MSU package.
  • 原文地址:https://www.cnblogs.com/llstart-new0201/p/12681826.html
Copyright © 2011-2022 走看看