141 lines
6.7 KiB
C#
141 lines
6.7 KiB
C#
using System.Collections.Generic;
|
||
using Rive.Utils;
|
||
using UnityEngine;
|
||
|
||
namespace Rive.Components
|
||
{
|
||
/// <summary>
|
||
/// Utility class for performing hit testing (raycasting) on RivePanels to detect which widgets are under a given point.
|
||
/// This is used to handle input events and determine which widgets should receive pointer interactions.
|
||
/// </summary>
|
||
public class PanelRaycaster
|
||
{
|
||
/// <summary>
|
||
/// Populates the raycastResults list with the widgets in a RivePanel that are hit by the given normalized local point in the panel.
|
||
/// </summary>
|
||
/// <param name="rivePanel"> The RivePanel to check for hit widgets. </param>
|
||
/// <param name="normalizedPointInPanel"> The normalized local point in the panel to check for hit widgets. The coordinates are in the range [0,1] where (0,0) is the bottom-left corner and (1,1) is the top-right corner. </param>
|
||
/// <param name="eventCamera"> The camera used by the event system or canvas </param>
|
||
/// <param name="raycastResults"> The list to populate with hit widgets. </param>
|
||
public static void RaycastAll(IRivePanel rivePanel, Vector2 normalizedPointInPanel, List<IRiveWidget> raycastResults)
|
||
{
|
||
RectTransform panelRectTransform = rivePanel.WidgetContainer;
|
||
if (panelRectTransform == null)
|
||
{
|
||
DebugLogger.Instance.LogError("Panel RectTransform is null.");
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
for (int i = rivePanel.Widgets.Count - 1; i >= 0; i--)
|
||
{
|
||
var widget = rivePanel.Widgets[i];
|
||
if (widget == null || !widget.Enabled || widget.RenderObject == null || widget.HitTestBehavior == HitTestBehavior.None)
|
||
continue;
|
||
|
||
Vector2 normalizedWidgetPoint;
|
||
bool isWithinWidgetBounds = TryGetNormalizedPointInWidget(rivePanel, normalizedPointInPanel, widget, out normalizedWidgetPoint);
|
||
|
||
if (ProcessHitTestBehavior(widget, normalizedWidgetPoint, raycastResults, isWithinWidgetBounds))
|
||
{
|
||
return;
|
||
}
|
||
|
||
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Processes the hit test behavior of a widget and adds it to the raycastResults list if it should be hit.
|
||
/// </summary>
|
||
/// <param name="widget"> The widget to process. </param>
|
||
/// <param name="normalizedPointInWidgetRect"> The normalized point in the widget's rect to check for a hit. The coordinates are in the range [0,1] where (0,0) is the bottom-left corner and (1,1) is the top-right corner. </param>
|
||
/// <param name="raycastResults"> The list to populate with hit widgets. </param>
|
||
/// <returns> True if should should block other widgets from being hit, false otherwise. </returns>
|
||
private static bool ProcessHitTestBehavior(IRiveWidget widget, Vector2 normalizedPointInWidgetRect, List<IRiveWidget> raycastResults, bool isWithinWidgetBounds)
|
||
{
|
||
switch (widget.HitTestBehavior)
|
||
{
|
||
case HitTestBehavior.Opaque:
|
||
|
||
raycastResults.Add(widget);
|
||
// Block other widgets from being hit if the pointer is within the widget
|
||
return isWithinWidgetBounds;
|
||
case HitTestBehavior.Translucent:
|
||
bool foundHit = widget.HitTest(normalizedPointInWidgetRect);
|
||
|
||
if (foundHit)
|
||
{
|
||
raycastResults.Add(widget);
|
||
return isWithinWidgetBounds;
|
||
}
|
||
break;
|
||
#pragma warning disable CS0618 // Transparent hit testing is deprecated but kept for backward compatibility
|
||
case HitTestBehavior.Transparent:
|
||
raycastResults.Add(widget);
|
||
// Continue checking other widgets
|
||
return false;
|
||
#pragma warning restore CS0618
|
||
case HitTestBehavior.None:
|
||
// Do not add to raycastResults
|
||
return false;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Tries to get the normalized local point in the widget from the normalized local point in the panel.
|
||
/// </summary>
|
||
/// <param name="rivePanel"> The RivePanel that contains the widget. </param>
|
||
/// <param name="normalizedPointInPanel"> The normalized local point in the panel. The coordinates are in the range [0,1] where (0,0) is the bottom-left corner and (1,1) is the top-right corner.</param>
|
||
/// <param name="widget"> The widget to get the normalized local point in. </param>
|
||
/// <param name="normalizedWidgetPoint"> The normalized point in the widget's rect. </param>
|
||
/// <returns> True if the normalized local point is within the widget's bounds, false otherwise. </returns>
|
||
public static bool TryGetNormalizedPointInWidget(IRivePanel rivePanel, Vector2 normalizedPointInPanel, IRiveWidget widget, out Vector2 normalizedWidgetPoint)
|
||
{
|
||
normalizedWidgetPoint = Vector2.zero;
|
||
|
||
RectTransform panelRectTransform = rivePanel.WidgetContainer;
|
||
if (panelRectTransform == null)
|
||
{
|
||
DebugLogger.Instance.LogError("Panel RectTransform is null.");
|
||
return false;
|
||
}
|
||
|
||
var panelRect = panelRectTransform.rect;
|
||
if (panelRect.width <= 0f || panelRect.height <= 0f)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// We need to turn the normalized point (0..1) into a local point on the panel.
|
||
// Unity’s RectTransform coordinates are based on the pivot, not always the center.
|
||
// So when the pivot changes, the rect’s xMin/yMin shift too. If we ignore that, input gets offset.
|
||
// Using xMin/yMin + (normalized * size) lets us rebuild the original local point again, so everything stays lined up.
|
||
Vector2 panelLocalPoint = new Vector2(
|
||
panelRect.xMin + (normalizedPointInPanel.x * panelRect.width),
|
||
panelRect.yMin + (normalizedPointInPanel.y * panelRect.height)
|
||
);
|
||
|
||
Vector3 worldPoint = panelRectTransform.TransformPoint(panelLocalPoint);
|
||
|
||
Vector3 widgetLocalPoint = widget.RectTransform.InverseTransformPoint(worldPoint);
|
||
|
||
normalizedWidgetPoint = new Vector2(
|
||
(widgetLocalPoint.x - widget.RectTransform.rect.xMin) / widget.RectTransform.rect.width,
|
||
(widgetLocalPoint.y - widget.RectTransform.rect.yMin) / widget.RectTransform.rect.height
|
||
);
|
||
|
||
if (widget.RectTransform.rect.Contains(widgetLocalPoint))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
}
|
||
}
|