Event system of UGUI source code analysis in Unity (5)-RayCaster (Part 1)

Event system of UGUI source code analysis in Unity (5)-RayCaster (Part 1)

What I want to share today is the raycaster (RayCaster) in the event system .

Unity uses raycasters to collect and identify game objects that were clicked.

The principle of ray casting is very simple. It is to launch a ray at the position clicked on the screen, collect the objects penetrated by the ray according to some rules, and then sort these objects according to some rules, select the object closest to the screen, and finally in this object Perform various event operations on it.

Therefore, the research on ray casting is to develop around this principle. With the direction, we will not fall into various codes and become confused.

Due to the large amount of content, we will introduce it in two articles under the mountain.

related classes

The classes related to ray casting are as follows:

  • Ray: rays
  • RaycastResult: projection result
  • RaycasterManager: Projector Manager
  • BaseRaycaster: raycasting base class
  • GraphicRaycaster : BaseRaycaster: graphics projector
  • PhysicsRaycaster : BaseRaycaster: Projector for 3D objects, the Camera component needs to exist on the object at the same time
  • Physics2DRaycaster : PhysicsRaycaster: For the projector of 2D objects, the Camera component needs to exist on the object at the same time

Ray

A ray is a finite line segment emitted from a certain point in a certain direction, and Unity uses the relevant interface of the Camera to emit the ray.

The principle of the camera emitting rays is very simple, that is , sending a ray through the specified point from the corresponding position of the camera 's near plane (whether it is a perspective projection or a parallel projection).

In principle, the emission point is calculated on the viewport according to the proportion, which is determined according to the proportion of the target point in its own coordinate system.

The resulting ray is described using world coordinates .

  • public Ray ViewportPointToRay(Vector3 position): Send a ray from the camera, through the specified point of ViewRect (viewport, the general coordinates are between 0 and 1)
  • public Ray ScreenPointToRay(Vector3 position): Send a ray from the camera, passing through the specified point of the Screen coordinates

Ray itself is a structure, which provides the attributes of the coordinates of the emission point and the emission direction, and also provides a coordinate interface for obtaining the end point of the ray with a specified length. Note that the coordinates mentioned here are all world coordinates .

public Vector3 origin
{
    get => this.m_Origin;
    set => this.m_Origin = value;
}

// 这个方向是标准化过后的
public Vector3 direction
{
    get => this.m_Direction;
    set => this.m_Direction = value.normalized;
}

public Vector3 GetPoint(float distance) => this.m_Origin + this.m_Direction * distance;

RaycastResult

The projection result itself is a structure that encapsulates projection-related data.

// 投射击中(射线穿过)的对象
private GameObject m_GameObject;

// 相关的投射器
public BaseRaycaster module;

// 击中距离(从射线起始点到击中点)
public float distance;

// 排序索引
public float index;

// 排序深度
public int depth;

// 排序sortingLayer
public int sortingLayer;

// 排序sortingOrder
public int sortingOrder;

// 击中点的世界坐标
public Vector3 worldPosition;

// 击中点的世界法线
public Vector3 worldNormal;

// 击中点的屏幕坐标
public Vector2 screenPosition;

// 是否有效
public bool isValid
{
    get { return module != null && gameObject != null; }
}

RaycasterManager

OnEnable/OnDisableRaycasterManager is a static class that maintains all projectors through a static list, and is added to and removed from the list during the lifecycle function of the projector .

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;
    }

    // 这个名字明显不需要带"s", 看来Unity的程序大佬也喜欢复制黏贴嘛, 哈哈
    public static void RemoveRaycasters(BaseRaycaster baseRaycaster)
    {
        if (!s_Raycasters.Contains(baseRaycaster))
            return;
        s_Raycasters.Remove(baseRaycaster);
    }
}

There is not much code and it is relatively simple, so I will post it directly.

BaseRaycaster

BaseRaycaster is an abstract class that inherits from UIBehaviour and provides some basic behaviors. The most important thing is to define the projection interface , camera properties and the behavior of adding and removing the projection manager . It also provides some basic properties for other modules to use.

public abstract class BaseRaycaster : UIBehaviour
{
    // 核心的投射接口
    public abstract void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList);
    
    // 用于生成射线的摄像机
    public abstract Camera eventCamera { get; }
    
    protected override void OnEnable()
    {
        base.OnEnable();
        RaycasterManager.AddRaycaster(this);
    }

    protected override void OnDisable()
    {
        RaycasterManager.RemoveRaycaster(this);
        base.OnDisable();
    }
    
    // 这个也参与之前文章提到的信息展示
    public override string ToString()
    {
        return "Name: " + gameObject + "\n" +
            "eventCamera: " + eventCamera + "\n" +
            "sortOrderPriority: " + sortOrderPriority + "\n" +
            "renderOrderPriority: " + renderOrderPriority;
    }
    
    // 排序优先级, 用于射线排序比较, 在EventSystem组件中的比较函数中使用
    public virtual int sortOrderPriority
    {
        get { return int.MinValue; }
    }

    // 渲染顺序优先级, 用于射线排序比较, 在EventSystem组件中的比较函数中使用
    public virtual int renderOrderPriority
    {
        get { return int.MinValue; }
    }
}

Methods used for casting in EventSystem

In the previous article, we said that EventSystem provides some methods for projection. It is not clear why it is placed here. Personally, it is more appropriate to put it in RaycasterManager .

// 投射结果比较器, 用于排序RaycastResult
// 总体上是按照: 优先级越大, 深度越大, 越后渲染, 距离摄像机越近, 索引越小, 则越靠前
private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs)
{
    // 优先比较投射器
    if (lhs.module != rhs.module)
    {
        var lhsEventCamera = lhs.module.eventCamera;
        var rhsEventCamera = rhs.module.eventCamera;
        
        // 两者摄像机都存在且深度不同时, 使用摄像机的深度排序
        if (lhsEventCamera != null && rhsEventCamera != null && lhsEventCamera.depth != rhsEventCamera.depth)
        {
            // need to reverse the standard compareTo
            if (lhsEventCamera.depth < rhsEventCamera.depth)
                return 1;
            if (lhsEventCamera.depth == rhsEventCamera.depth)
                return 0;

            return -1;
        }

        // sortOrderPriority排序
        if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
            return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);

        // renderOrderPriority排序
        if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
            return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
    }

    // sortingLayer排序
    if (lhs.sortingLayer != rhs.sortingLayer)
    {
        // Uses the layer value to properly compare the relative order of the layers.
        var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
        var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
        return rid.CompareTo(lid);
    }

    // sortingOrder排序
    if (lhs.sortingOrder != rhs.sortingOrder)
        return rhs.sortingOrder.CompareTo(lhs.sortingOrder);

    // depth排序
    if (lhs.depth != rhs.depth)
        return rhs.depth.CompareTo(lhs.depth);

    // distance排序[从小到大]
    if (lhs.distance != rhs.distance)
        return lhs.distance.CompareTo(rhs.distance);

    // index排序[从小到大]
    return lhs.index.CompareTo(rhs.index);
}

private static readonly Comparison<RaycastResult> s_RaycastComparer = RaycastComparer;

// 使用事件数据对所有投射器进行投射, 获取排序后的投射结果, 主要使用位置属性
public void RaycastAll(PointerEventData eventData, List<RaycastResult> raycastResults)
{
    raycastResults.Clear();
    var modules = RaycasterManager.GetRaycasters();
    for (int i = 0; i < modules.Count; ++i)
    {
        var module = modules[i];
        if (module == null || !module.IsActive())
            continue;

        module.Raycast(eventData, raycastResults);
    }

    raycastResults.Sort(s_RaycastComparer);
}

Summarize

Today I introduced the basic part of the ray caster, the next article will introduce several specific casters, I hope it can help everyone.

Guess you like

Origin blog.csdn.net/woodengm/article/details/123582306