loudixvmuniu/Assets/GCSeries/Core/Scripts/UI/ZGraphicRaycaster.cs
shenjianxing 6b341ae2b4 适配VR
2025-04-01 16:16:45 +08:00

346 lines
11 KiB
C#

////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2007-2020 , Inc. All Rights Reserved.
//
////////////////////////////////////////////////////////////////////////////////
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using GCSeries.Core.EventSystems;
namespace GCSeries.Core.UI
{
[RequireComponent(typeof(Canvas))]
public class ZGraphicRaycaster : GraphicRaycaster
{
////////////////////////////////////////////////////////////////////////
// MonoBehaviour Callbacks
////////////////////////////////////////////////////////////////////////
protected override void OnEnable()
{
base.OnEnable();
if (!s_instances.Contains(this))
{
s_instances.Add(this);
}
}
protected override void OnDisable()
{
base.OnDisable();
if (s_instances.Contains(this))
{
s_instances.Remove(this);
}
}
protected override void Start()
{
base.Start();
if (this.Canvas.renderMode == RenderMode.WorldSpace &&
this.Canvas.worldCamera == null)
{
Debug.LogWarning(
"No Event Camera found attached to associated world " +
"space canvas. Please make sure to assign an appropriate " +
"camera to your canvas to minimize performance impact " +
"and ensure raycasts are performed correctly.",
this);
}
}
////////////////////////////////////////////////////////////////////////
// Public Properties
////////////////////////////////////////////////////////////////////////
public Canvas Canvas
{
get
{
if (this._canvas == null)
{
this._canvas = this.GetComponent<Canvas>();
}
return this._canvas;
}
}
////////////////////////////////////////////////////////////////////////
// Public Methods
////////////////////////////////////////////////////////////////////////
/// <summary>
/// Gets a list of all enabled ZGraphicRaycaster instances in the scene.
/// </summary>
///
/// <returns>
/// The list of all enabled ZGraphicRaycasters instances in the scene.
/// </returns>
public static IList<ZGraphicRaycaster> GetRaycasters()
{
return s_instances;
}
/// <summary>
/// Performs a raycast against all enabled ZGraphicRaycaster instances
/// in the scene and reports the closest hit.
/// </summary>
///
/// <param name="ray">
/// The starting point and direction of the ray.
/// </param>
/// <param name="result">
/// The raycast result corresponding to the closest hit.
/// </param>
/// <param name="maxDistance">
/// The maximum distance that the hit result is allowed to be from
/// the start of the ray.
/// </param>
/// <param name="layerMask">
/// A layer mask that is used to selectively ignore graphics when
/// casting the ray.
/// </param>
///
/// <returns>
/// True if a graphic was hit. False otherwise.
/// </returns>
public static bool Raycast(
Ray ray, out RaycastResult result, float maxDistance, int layerMask)
{
s_results.Clear();
for (int i = 0; i < s_instances.Count; ++i)
{
s_instances[i].Raycast(ray, s_results, maxDistance, layerMask);
}
if (s_results.Count > 0)
{
result = s_results[0];
return true;
}
result = default(RaycastResult);
return false;
}
/// <summary>
/// Performs a raycast against all enabled ZGraphicRaycaster instances
/// in the scene and reports all hits.
/// </summary>
///
/// <param name="ray">
/// The starting point and direction of the ray.
/// </param>
/// <param name="resultAppendList">
/// The raycast results corresponding to all hits.
/// </param>
/// <param name="maxDistance">
/// The maximum distance that a hit result is allowed to be from
/// the start of the ray.
/// </param>
/// <param name="layerMask">
/// A layer mask that is used to selectively ignore graphics when
/// casting the ray.
/// </param>
///
/// <returns>
/// True if a graphic was hit. False otherwise.
/// </returns>
public static void RaycastAll(
Ray ray,
List<RaycastResult> resultAppendList,
float maxDistance,
int layerMask)
{
for (int i = 0; i < s_instances.Count; ++i)
{
s_instances[i].Raycast(
ray, resultAppendList, maxDistance, layerMask);
}
}
public override void Raycast(
PointerEventData eventData, List<RaycastResult> resultAppendList)
{
ZPointerEventData e = eventData as ZPointerEventData;
if (e != null)
{
this.Raycast(
e.Pointer.PointerRay,
resultAppendList,
float.PositiveInfinity,
~0);
}
else
{
base.Raycast(eventData, resultAppendList);
}
}
////////////////////////////////////////////////////////////////////////
// Private Methods
////////////////////////////////////////////////////////////////////////
private void Raycast(
Ray ray,
List<RaycastResult> resultAppendList,
float maxDistance,
int layerMask)
{
// Potentially reduce the maximum hit distance based on whether
// any 2D or 3D blocking objects have been intersected.
float distance =
this.eventCamera.farClipPlane - this.eventCamera.nearClipPlane;
if (this.blockingObjects == BlockingObjects.ThreeD ||
this.blockingObjects == BlockingObjects.All)
{
RaycastHit hit;
if (Physics.Raycast(ray, out hit, distance, this.m_BlockingMask))
{
maxDistance = Mathf.Min(hit.distance, maxDistance);
}
}
if (this.blockingObjects == BlockingObjects.TwoD ||
this.blockingObjects == BlockingObjects.All)
{
RaycastHit2D hit = Physics2D.GetRayIntersection(
ray, distance, this.m_BlockingMask);
if (hit.collider != null)
{
maxDistance = Mathf.Min(
hit.fraction * distance, maxDistance);
}
}
// Retrieve the list of graphics associated with the canvas.
IList<Graphic> graphics =
GraphicRegistry.GetGraphicsForCanvas(this.Canvas);
// Iterate through each of graphics and perform hit tests.
for (int i = 0; i < graphics.Count; ++i)
{
Graphic graphic = graphics[i];
// Skip the graphic if it's not in the layer mask.
if (((1 << graphic.gameObject.layer) & layerMask) == 0)
{
continue;
}
// Perform a raycast against the graphic.
RaycastResult result;
if (this.Raycast(ray, graphic, out result, maxDistance))
{
resultAppendList.Add(result);
}
}
// Sort the results by depth.
resultAppendList.Sort((x, y) => y.depth.CompareTo(x.depth));
}
private bool Raycast(
Ray ray,
Graphic graphic,
out RaycastResult result,
float maxDistance)
{
result = default(RaycastResult);
// Skip graphics that aren't raycast targets.
if (!graphic.raycastTarget)
{
return false;
}
// Skip graphics that aren't being drawn.
if (graphic.depth == -1)
{
return false;
}
// Skip graphics that are reversed if the ignore reversed
// graphics inspector field is enabled.
if (this.ignoreReversedGraphics &&
Vector3.Dot(ray.direction, -graphic.transform.forward) > 0)
{
return false;
}
// Create a plane based on the graphic's transform.
Plane plane = new Plane(
-graphic.transform.forward, graphic.transform.position);
// Skip graphics that failed the plane intersection test.
float distance = 0.0f;
if (!plane.Raycast(ray, out distance))
{
return false;
}
// Skip graphics that are further away than the max distance.
if (distance > maxDistance)
{
return false;
}
Vector3 worldPosition =
ray.origin + (ray.direction * distance);
Vector3 screenPosition =
this.eventCamera.WorldToScreenPoint(worldPosition);
// Skip graphics that have failed the bounds test.
if (!RectTransformUtility.RectangleContainsScreenPoint(
graphic.rectTransform, screenPosition, this.eventCamera))
{
return false;
}
// Skip graphics that fail the raycast test.
// NOTE: This is necessary to ensure that raycasts against
// masked out areas of the graphic are correctly ignored.
if (!graphic.Raycast(screenPosition, this.eventCamera))
{
return false;
}
result.depth = graphic.depth;
result.distance = distance;
result.worldPosition = worldPosition;
result.worldNormal = plane.normal;
result.screenPosition = screenPosition;
result.gameObject = graphic.gameObject;
result.module = this;
return true;
}
////////////////////////////////////////////////////////////////////////
// Private Members
////////////////////////////////////////////////////////////////////////
private static readonly List<ZGraphicRaycaster> s_instances =
new List<ZGraphicRaycaster>();
private static readonly List<RaycastResult> s_results =
new List<RaycastResult>();
private Canvas _canvas = null;
}
}