346 lines
11 KiB
C#
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;
|
|
}
|
|
}
|