356 lines
14 KiB
C#
356 lines
14 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.Serialization;
|
|
using UnityEngine.UI;
|
|
|
|
public class GraphicRaycasterBend : GraphicRaycaster
|
|
{
|
|
Vector2 Bend(Vector2 uv, float bend)
|
|
{
|
|
uv -= new Vector2(0.5f, 0.5f);
|
|
uv *= new Vector2(2, 2);
|
|
|
|
uv.x *= (float)(1 + Math.Pow(Math.Abs(uv.y) / bend, 2));
|
|
uv.y *= (float)(1 + Math.Pow(Math.Abs(uv.x) / bend, 2));
|
|
|
|
uv /= new Vector2(2.107f, 2.107f);
|
|
return uv + new Vector2(0.5f, 0.5f);
|
|
}
|
|
|
|
private Canvas m_Canvas;
|
|
|
|
private Canvas canvas
|
|
{
|
|
get
|
|
{
|
|
if (m_Canvas != null)
|
|
return m_Canvas;
|
|
|
|
m_Canvas = GetComponent<Canvas>();
|
|
return m_Canvas;
|
|
}
|
|
}
|
|
|
|
[NonSerialized] private List<Graphic> m_RaycastResults = new List<Graphic>();
|
|
|
|
/// <summary>
|
|
/// Perform the raycast against the list of graphics associated with the Canvas.
|
|
/// </summary>
|
|
/// <param name="eventData">Current event data</param>
|
|
/// <param name="resultAppendList">List of hit objects to append new results to.</param>
|
|
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
|
|
{
|
|
if (canvas == null)
|
|
return;
|
|
|
|
var canvasGraphics = GraphicRegistry.GetRaycastableGraphicsForCanvas(canvas);
|
|
if (canvasGraphics == null || canvasGraphics.Count == 0)
|
|
return;
|
|
|
|
int displayIndex;
|
|
var currentEventCamera = eventCamera; // Property can call Camera.main, so cache the reference
|
|
|
|
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay || currentEventCamera == null)
|
|
displayIndex = canvas.targetDisplay;
|
|
else
|
|
displayIndex = currentEventCamera.targetDisplay;
|
|
|
|
var eventPosition = RelativeMouseAtScaled(eventData.position);
|
|
if (eventPosition != Vector3.zero)
|
|
{
|
|
// We support multiple display and display identification based on event position.
|
|
|
|
int eventDisplayIndex = (int)eventPosition.z;
|
|
|
|
// Discard events that are not part of this display so the user does not interact with multiple displays at once.
|
|
if (eventDisplayIndex != displayIndex)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// The multiple display system is not supported on all platforms, when it is not supported the returned position
|
|
// will be all zeros so when the returned index is 0 we will default to the event data to be safe.
|
|
eventPosition = eventData.position;
|
|
|
|
#if UNITY_EDITOR
|
|
if (Display.activeEditorGameViewTarget != displayIndex)
|
|
return;
|
|
eventPosition.z = Display.activeEditorGameViewTarget;
|
|
#endif
|
|
|
|
// We dont really know in which display the event occured. We will process the event assuming it occured in our display.
|
|
}
|
|
|
|
// Convert to view space
|
|
Vector2 pos;
|
|
if (currentEventCamera == null)
|
|
{
|
|
// Multiple display support only when not the main display. For display 0 the reported
|
|
// resolution is always the desktops resolution since its part of the display API,
|
|
// so we use the standard none multiple display method. (case 741751)
|
|
float w = Screen.width;
|
|
float h = Screen.height;
|
|
if (displayIndex > 0 && displayIndex < Display.displays.Length)
|
|
{
|
|
w = Display.displays[displayIndex].systemWidth;
|
|
h = Display.displays[displayIndex].systemHeight;
|
|
}
|
|
|
|
pos = new Vector2(eventPosition.x / w, eventPosition.y / h);
|
|
}
|
|
else
|
|
pos = currentEventCamera.ScreenToViewportPoint(eventPosition);
|
|
|
|
// If it's outside the camera's viewport, do nothing
|
|
if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f)
|
|
return;
|
|
|
|
pos = Bend(pos, 3.03f);
|
|
|
|
float hitDistance = float.MaxValue;
|
|
|
|
Ray ray = new Ray();
|
|
|
|
if (currentEventCamera != null)
|
|
ray = currentEventCamera.ScreenPointToRay(eventPosition);
|
|
|
|
if (canvas.renderMode != RenderMode.ScreenSpaceOverlay && blockingObjects != BlockingObjects.None)
|
|
{
|
|
float distanceToClipPlane = 100.0f;
|
|
|
|
if (currentEventCamera != null)
|
|
{
|
|
float projectionDirection = ray.direction.z;
|
|
distanceToClipPlane = Mathf.Approximately(0.0f, projectionDirection)
|
|
? Mathf.Infinity
|
|
: Mathf.Abs((currentEventCamera.farClipPlane - currentEventCamera.nearClipPlane) /
|
|
projectionDirection);
|
|
}
|
|
#if PACKAGE_PHYSICS
|
|
if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All)
|
|
{
|
|
if (ReflectionMethodsCache.Singleton.raycast3D != null)
|
|
{
|
|
var hits =
|
|
ReflectionMethodsCache.Singleton.raycast3DAll(ray, distanceToClipPlane, (int)m_BlockingMask);
|
|
if (hits.Length > 0)
|
|
hitDistance = hits[0].distance;
|
|
}
|
|
}
|
|
#endif
|
|
#if PACKAGE_PHYSICS2D
|
|
if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All)
|
|
{
|
|
if (ReflectionMethodsCache.Singleton.raycast2D != null)
|
|
{
|
|
var hits =
|
|
ReflectionMethodsCache.Singleton.getRayIntersectionAll(ray, distanceToClipPlane, (int)m_BlockingMask);
|
|
if (hits.Length > 0)
|
|
hitDistance = hits[0].distance;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
m_RaycastResults.Clear();
|
|
|
|
Raycast(canvas, currentEventCamera, eventPosition, canvasGraphics, m_RaycastResults);
|
|
|
|
int totalCount = m_RaycastResults.Count;
|
|
for (var index = 0; index < totalCount; index++)
|
|
{
|
|
var go = m_RaycastResults[index].gameObject;
|
|
bool appendGraphic = true;
|
|
|
|
if (ignoreReversedGraphics)
|
|
{
|
|
if (currentEventCamera == null)
|
|
{
|
|
// If we dont have a camera we know that we should always be facing forward
|
|
var dir = go.transform.rotation * Vector3.forward;
|
|
appendGraphic = Vector3.Dot(Vector3.forward, dir) > 0;
|
|
}
|
|
else
|
|
{
|
|
// If we have a camera compare the direction against the cameras forward.
|
|
var cameraForward = currentEventCamera.transform.rotation * Vector3.forward *
|
|
currentEventCamera.nearClipPlane;
|
|
appendGraphic =
|
|
Vector3.Dot(go.transform.position - currentEventCamera.transform.position - cameraForward,
|
|
go.transform.forward) >= 0;
|
|
}
|
|
}
|
|
|
|
if (appendGraphic)
|
|
{
|
|
float distance = 0;
|
|
Transform trans = go.transform;
|
|
Vector3 transForward = trans.forward;
|
|
|
|
if (currentEventCamera == null || canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
|
distance = 0;
|
|
else
|
|
{
|
|
// http://geomalgorithms.com/a06-_intersect-2.html
|
|
distance = (Vector3.Dot(transForward, trans.position - ray.origin) /
|
|
Vector3.Dot(transForward, ray.direction));
|
|
|
|
// Check to see if the go is behind the camera.
|
|
if (distance < 0)
|
|
continue;
|
|
}
|
|
|
|
if (distance >= hitDistance)
|
|
continue;
|
|
|
|
var castResult = new RaycastResult
|
|
{
|
|
gameObject = go,
|
|
module = this,
|
|
distance = distance,
|
|
screenPosition = eventPosition,
|
|
displayIndex = displayIndex,
|
|
index = resultAppendList.Count,
|
|
depth = m_RaycastResults[index].depth,
|
|
sortingLayer = canvas.sortingLayerID,
|
|
sortingOrder = canvas.sortingOrder,
|
|
worldPosition = ray.origin + ray.direction * distance,
|
|
worldNormal = -transForward
|
|
};
|
|
resultAppendList.Add(castResult);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Vector3 RelativeMouseAtScaled(Vector2 position)
|
|
{
|
|
#if !UNITY_EDITOR && !UNITY_WSA
|
|
// If the main display is now the same resolution as the system then we need to scale the mouse position. (case 1141732)
|
|
if (Display.main.renderingWidth != Display.main.systemWidth || Display.main.renderingHeight != Display.main.systemHeight)
|
|
{
|
|
// The system will add padding when in full-screen and using a non-native aspect ratio. (case UUM-7893)
|
|
// For example Rendering 1920x1080 with a systeem resolution of 3440x1440 would create black bars on each side that are 330 pixels wide.
|
|
// we need to account for this or it will offset our coordinates when we are not on the main display.
|
|
var systemAspectRatio = Display.main.systemWidth / (float)Display.main.systemHeight;
|
|
|
|
var sizePlusPadding = new Vector2(Display.main.renderingWidth, Display.main.renderingHeight);
|
|
var padding = Vector2.zero;
|
|
if (Screen.fullScreen)
|
|
{
|
|
var aspectRatio = Screen.width / (float)Screen.height;
|
|
if (Display.main.systemHeight * aspectRatio < Display.main.systemWidth)
|
|
{
|
|
// Horizontal padding
|
|
sizePlusPadding.x = Display.main.renderingHeight * systemAspectRatio;
|
|
padding.x = (sizePlusPadding.x - Display.main.renderingWidth) * 0.5f;
|
|
}
|
|
else
|
|
{
|
|
// Vertical padding
|
|
sizePlusPadding.y = Display.main.renderingWidth / systemAspectRatio;
|
|
padding.y = (sizePlusPadding.y - Display.main.renderingHeight) * 0.5f;
|
|
}
|
|
}
|
|
|
|
var sizePlusPositivePadding = sizePlusPadding - padding;
|
|
|
|
// If we are not inside of the main display then we must adjust the mouse position so it is scaled by
|
|
// the main display and adjusted for any padding that may have been added due to different aspect ratios.
|
|
if (position.y < -padding.y || position.y > sizePlusPositivePadding.y ||
|
|
position.x < -padding.x || position.x > sizePlusPositivePadding.x)
|
|
{
|
|
var adjustedPosition = position;
|
|
|
|
if (!Screen.fullScreen)
|
|
{
|
|
// When in windowed mode, the window will be centered with the 0,0 coordinate at the top left, we need to adjust so it is relative to the screen instead.
|
|
adjustedPosition.x -= (Display.main.renderingWidth - Display.main.systemWidth) * 0.5f;
|
|
adjustedPosition.y -= (Display.main.renderingHeight - Display.main.systemHeight) * 0.5f;
|
|
}
|
|
else
|
|
{
|
|
// Scale the mouse position to account for the black bars when in a non-native aspect ratio.
|
|
adjustedPosition += padding;
|
|
adjustedPosition.x *= Display.main.systemWidth / sizePlusPadding.x;
|
|
adjustedPosition.y *= Display.main.systemHeight / sizePlusPadding.y;
|
|
}
|
|
|
|
var relativePos = Display.RelativeMouseAt(adjustedPosition);
|
|
|
|
// If we are not on the main display then return the adjusted position.
|
|
if (relativePos.z != 0)
|
|
return relativePos;
|
|
}
|
|
|
|
// We are using the main display.
|
|
return new Vector3(position.x, position.y, 0);
|
|
}
|
|
#endif
|
|
return Display.RelativeMouseAt(position);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The camera that will generate rays for this raycaster.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// - Null if Camera mode is ScreenSpaceOverlay or ScreenSpaceCamera and has no camera.
|
|
/// - canvas.worldCanvas if not null
|
|
/// - Camera.main.
|
|
/// </returns>
|
|
public override Camera eventCamera
|
|
{
|
|
get
|
|
{
|
|
var canvas = this.canvas;
|
|
var renderMode = canvas.renderMode;
|
|
if (renderMode == RenderMode.ScreenSpaceOverlay
|
|
|| (renderMode == RenderMode.ScreenSpaceCamera && canvas.worldCamera == null))
|
|
return null;
|
|
|
|
return canvas.worldCamera ?? Camera.main;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Perform a raycast into the screen and collect all graphics underneath it.
|
|
/// </summary>
|
|
[NonSerialized] static readonly List<Graphic> s_SortedGraphics = new List<Graphic>();
|
|
|
|
private static void Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition,
|
|
IList<Graphic> foundGraphics, List<Graphic> results)
|
|
{
|
|
// Necessary for the event system
|
|
int totalCount = foundGraphics.Count;
|
|
for (int i = 0; i < totalCount; ++i)
|
|
{
|
|
Graphic graphic = foundGraphics[i];
|
|
|
|
// -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
|
|
if (!graphic.raycastTarget || graphic.canvasRenderer.cull || graphic.depth == -1)
|
|
continue;
|
|
|
|
if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera,
|
|
graphic.raycastPadding))
|
|
continue;
|
|
|
|
if (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z >
|
|
eventCamera.farClipPlane)
|
|
continue;
|
|
|
|
if (graphic.Raycast(pointerPosition, eventCamera))
|
|
{
|
|
s_SortedGraphics.Add(graphic);
|
|
}
|
|
}
|
|
|
|
s_SortedGraphics.Sort((g1, g2) => g2.depth.CompareTo(g1.depth));
|
|
totalCount = s_SortedGraphics.Count;
|
|
for (int i = 0; i < totalCount; ++i)
|
|
results.Add(s_SortedGraphics[i]);
|
|
|
|
s_SortedGraphics.Clear();
|
|
}
|
|
} |