UIGU source code analysis 18: ray

Source code 18: Ray

Originally, I wanted to analyze each component of UGUI and then look at its ray-related implementation, but in addition to the previous EventSystem having RaycastAll, Graphic also has many ray-related functions, so I felt it was necessary to analyze it first.

RaycasterManager

 internal static class RaycasterManager
    {
        private static readonly List<BaseRaycaster> s_Raycasters = new List<BaseRaycaster>();

        public static void AddRaycaster(BaseRaycaster baseRaycaster)
        {
            if (s_Raycasters.Contains(baseRaycaster))
                return;

            s_Raycasters.Add(baseRaycaster);
        }

        public static List<BaseRaycaster> GetRaycasters()
        {
            return s_Raycasters;
        }

        public static void RemoveRaycasters(BaseRaycaster baseRaycaster)
        {
            if (!s_Raycasters.Contains(baseRaycaster))
                return;
            s_Raycasters.Remove(baseRaycaster);
        }
    }

As the name suggests, RaycasterManager is a Raycaster manager responsible for caching all BaseRaycaster

BaseRaycaster

    /// <summary>
    /// Base class for any RayCaster.
    /// </summary>
    /// <remarks>
    /// A Raycaster is responsible for raycasting against scene elements to determine if the cursor is over them. Default Raycasters include PhysicsRaycaster, Physics2DRaycaster, GraphicRaycaster.
    /// Custom raycasters can be added by extending this class.
    /// </remarks>
public abstract class BaseRaycaster : UIBehaviour
{
	...
}

See the note above: The ray caster is responsible for casting scene elements to determine whether the mouse is on them. The default casters include:

PhysicsRaycaster Physics2DRaycaster GraphicRaycaster

Custom raycasters can be added by extending this class.

BaseRaycaster is an abstract class that has several functions and properties that must be implemented when extending the ray caster:

       /// <summary>
        /// Raycast against the scene.
        /// </summary>
        /// <param name="eventData">Current event data.</param>
        /// <param name="resultAppendList">List of hit Objects.</param>
        public abstract void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList);
        
                /// <summary>
        /// The camera that will generate rays for this raycaster.
        /// </summary>
        public abstract Camera eventCamera { get; }

Raycast is the basic method of BaseRaycaster. If you pass in a PointerEventData, Raycast will add the object that receives the ray detection to the resultAppendList.

eventCamera is the camera used to generate the ray.

Some other properties/functions:

  • The top-level Raycaster in the rootRaycaster Hierarchy

  • OnEnable and OnDisable are added/removed from RaycasterManager when the component is activated and closed.


PhysicsRaycaster

The name means: PhysicsRaycaster is responsible for ray detection related to 3D physical components

    /// <summary>
    /// Simple event system using physics raycasts.
    /// </summary>
    [AddComponentMenu("Event/Physics Raycaster")]
    [RequireComponent(typeof(Camera))]
    /// <summary>
    /// Raycaster for casting against 3D Physics components.
    /// </summary>
    public class PhysicsRaycaster : BaseRaycaster
    {
        /// <summary>
        /// Const to use for clarity when no event mask is set
        /// </summary>
        protected const int kNoEventMaskSet = -1;

        protected Camera m_EventCamera;

        /// <summary>
        /// Layer mask used to filter events. Always combined with the camera's culling mask if a camera is used.
        /// </summary>
        [SerializeField]
        protected LayerMask m_EventMask = kNoEventMaskSet;

        /// <summary>
        /// The max number of intersections allowed. 0 = allocating version anything else is non alloc.
        /// </summary>
        [SerializeField]
        protected int m_MaxRayIntersections = 0;
        protected int m_LastMaxRayIntersections = 0;
		
		#if PACKAGE_PHYSICS
        RaycastHit[] m_Hits;
		#endif
		...
}
       public override Camera eventCamera
        {
            get
            {
                if (m_EventCamera == null)
                    m_EventCamera = GetComponent<Camera>();
                return m_EventCamera ?? Camera.main;
            }
        }

Implemented the eventCamera attribute in BaseRaycaster. It is very simple to get the Camera component on the object. If not, just get the main camera directly.

    public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
        {
#if PACKAGE_PHYSICS
            Ray ray = new Ray();
            int displayIndex = 0;
            float distanceToClipPlane = 0;
            if (!ComputeRayAndDistance(eventData, ref ray, ref displayIndex, ref distanceToClipPlane))
                return;

            int hitCount = 0;

            if (m_MaxRayIntersections == 0)
            {
                if (ReflectionMethodsCache.Singleton.raycast3DAll == null)
                    return;

                m_Hits = ReflectionMethodsCache.Singleton.raycast3DAll(ray, distanceToClipPlane, finalEventMask);
                hitCount = m_Hits.Length;
            }
            else
            {
                if (ReflectionMethodsCache.Singleton.getRaycastNonAlloc == null)
                    return;
                if (m_LastMaxRayIntersections != m_MaxRayIntersections)
                {
                    m_Hits = new RaycastHit[m_MaxRayIntersections];
                    m_LastMaxRayIntersections = m_MaxRayIntersections;
                }

                hitCount = ReflectionMethodsCache.Singleton.getRaycastNonAlloc(ray, m_Hits, distanceToClipPlane, finalEventMask);
            }

            if (hitCount != 0)
            {
                if (hitCount > 1)
                    System.Array.Sort(m_Hits, 0, hitCount, RaycastHitComparer.instance);

                for (int b = 0, bmax = hitCount; b < bmax; ++b)
                {
                    var result = new RaycastResult
                    {
                        gameObject = m_Hits[b].collider.gameObject,
                        module = this,
                        distance = m_Hits[b].distance,
                        worldPosition = m_Hits[b].point,
                        worldNormal = m_Hits[b].normal,
                        screenPosition = eventData.position,
                        displayIndex = displayIndex,
                        index = resultAppendList.Count,
                        sortingLayer = 0,
                        sortingOrder = 0
                    };
                    resultAppendList.Add(result);
                }
            }
#endif
        }

The Raycast abstract method of BaseRaycaster is rewritten here. It actually uses the API corresponding to the ReflectionMethodsCache class to reflect the Physics module to obtain the 3D object illuminated by the ray, and then packages it into a RaycastResult and adds it to the resultAppendList. Personally, I feel that this part of the code is written like this as the source code of UGUI to reduce the coupling between the UI and the Physics module.

Physics2DRaycaster and PhysicsRaycaster are similar and will not be described in detail here.


GraphicRaycaster

    [AddComponentMenu("Event/Graphic Raycaster")]
    [RequireComponent(typeof(Canvas))]
    /// <summary>
    /// A derived BaseRaycaster to raycast against Graphic elements.
    /// </summary>
    public class GraphicRaycaster : BaseRaycaster
    {
    	...
    }

When using GraphicRaycaster, it needs to be bound to the Canvas component.

In addition to rewriting the properties and methods in BaseRaycaster, some additional fields related to the UI are defined.

  • ignoreReversedGraphics Whether to ignore reversed Graphics. The default is ignored.

  • Rewrite eventCamera to get the specified camera UI in ScreenSpaceOverlay mode or ScreenSpaceCamera. If no camera is specified, NUll will be returned directly. Otherwise, UICamera will be returned directly.

  • Override sortOrderPriority to get the sortOrder of Canvas

  • renderOrderPriority gets the renderOrder of Canvas

The end is the overridden Raycast method.


We have previously analyzed the input process of the input module:

    protected PointerEventData GetTouchPointerEventData(Touch input, out bool pressed, out bool released)
    {
        PointerEventData pointerData;
        var created = GetPointerData(input.fingerId, out pointerData, true);

        pointerData.Reset();

        pressed = created || (input.phase == TouchPhase.Began);
        released = (input.phase == TouchPhase.Canceled) || (input.phase == TouchPhase.Ended);

        if (created)
            pointerData.position = input.position;

        if (pressed)
            pointerData.delta = Vector2.zero;
        else
            pointerData.delta = input.position - pointerData.position;

        pointerData.position = input.position;

        pointerData.button = PointerEventData.InputButton.Left;

        if (input.phase == TouchPhase.Canceled)
        {
            pointerData.pointerCurrentRaycast = new RaycastResult();
        }
        else
        {
            eventSystem.RaycastAll(pointerData, m_RaycastResultCache);

            var raycast = FindFirstRaycast(m_RaycastResultCache);
            pointerData.pointerCurrentRaycast = raycast;
            m_RaycastResultCache.Clear();
        }
        return pointerData;
    }

After obtaining the PointerData data, the RaycastAll method is called to obtain the cache of the ray results. And take out the first valid cached result and assign it to PointData.

   /// <summary>
    /// Raycast into the scene using all configured BaseRaycasters.
    /// </summary>
    /// <param name="eventData">Current pointer data.</param>
    /// <param name="raycastResults">List of 'hits' to populate.</param>
    public void RaycastAll(PointerEventData eventData, List<RaycastResult> raycastResults)
    {
        raycastResults.Clear();
        var modules = RaycasterManager.GetRaycasters();
        var modulesCount = modules.Count;
        for (int i = 0; i < modulesCount; ++i)
        {
            var module = modules[i];
            if (module == null || !module.IsActive())
                continue;

            module.Raycast(eventData, raycastResults);
        }

        raycastResults.Sort(s_RaycastComparer);
    }

raycastAll will cause all BaseRaycasters to emit rays, cache them in raycastResults, and finally sort them.


GraphicRaycaster.Raycaster

GraphicRaycaster is usually bound to Canvas.

(D:\UnityProjectSpace\BlogRecord\UGUI Source Code Analysis\Image\image-20220416233206344-16501231335821.png)]

When detecting rays, GraphicRaycaster emits rays and detects all Graphic elements under the current Canvas. After obtaining m_RaycastResults, it will be filtered again, and Graphics with ignoreReversedGraphics checked will be eliminated. The final result obtained is returned to PointerEventData.

The code is quite long so I won’t post it.

Guess you like

Origin blog.csdn.net/NippyLi/article/details/124223219