using System;
using System.Collections.Generic;
using System.Diagnostics;
using Unity.Profiling;
using Unity.XR.CoreUtils;
using Unity.XR.CoreUtils.Bindings.Variables;
using Unity.XR.CoreUtils.Collections;
using UnityEngine.Scripting.APIUpdating;
using UnityEngine.XR.Interaction.Toolkit.Filtering;
using UnityEngine.XR.Interaction.Toolkit.Gaze;
using UnityEngine.XR.Interaction.Toolkit.Interactables.Visuals;
using UnityEngine.XR.Interaction.Toolkit.Interactors;
using UnityEngine.XR.Interaction.Toolkit.Interactors.Visuals;
using UnityEngine.XR.Interaction.Toolkit.Utilities;
using UnityEngine.XR.Interaction.Toolkit.Utilities.Internal;
namespace UnityEngine.XR.Interaction.Toolkit.Interactables
{
///
/// Abstract base class from which all interactable behaviors derive.
/// This class hooks into the interaction system (via ) and provides base virtual methods for handling
/// hover, selection, and focus.
///
[MovedFrom("UnityEngine.XR.Interaction.Toolkit")]
[SelectionBase]
[DefaultExecutionOrder(XRInteractionUpdateOrder.k_Interactables)]
public abstract partial class XRBaseInteractable : MonoBehaviour, IXRActivateInteractable, IXRHoverInteractable, IXRSelectInteractable, IXRFocusInteractable, IXRInteractionStrengthInteractable, IXROverridesGazeAutoSelect
{
const float k_InteractionStrengthHover = 0f;
const float k_InteractionStrengthSelect = 1f;
///
/// Options for how to process and perform movement of an Interactable.
///
///
/// Each method of movement has tradeoffs, and different values may be more appropriate
/// for each type of Interactable object in a project.
///
///
public enum MovementType
{
#if UNITY_2023_3_OR_NEWER // Change between Rigidbody.linearVelocity and Rigidbody.velocity
///
/// Move the Interactable object by setting the velocity and angular velocity of the Rigidbody.
/// Use this if you don't want the object to be able to move through other Colliders without a Rigidbody
/// as it follows the Interactor, however with the tradeoff that it can appear to lag behind
/// and not move as smoothly as .
///
///
/// Unity sets the velocity values during the FixedUpdate function. This Interactable will move at the
/// framerate-independent interval of the Physics update, which may be slower than the Update rate.
/// If the Rigidbody is not set to use interpolation or extrapolation, as the Interactable
/// follows the Interactor, it may not visually update position each frame and be a slight distance
/// behind the Interactor or controller due to the difference between the Physics update rate
/// and the render update rate.
///
///
///
#else
///
/// Move the Interactable object by setting the velocity and angular velocity of the Rigidbody.
/// Use this if you don't want the object to be able to move through other Colliders without a Rigidbody
/// as it follows the Interactor, however with the tradeoff that it can appear to lag behind
/// and not move as smoothly as .
///
///
/// Unity sets the velocity values during the FixedUpdate function. This Interactable will move at the
/// framerate-independent interval of the Physics update, which may be slower than the Update rate.
/// If the Rigidbody is not set to use interpolation or extrapolation, as the Interactable
/// follows the Interactor, it may not visually update position each frame and be a slight distance
/// behind the Interactor or controller due to the difference between the Physics update rate
/// and the render update rate.
///
///
///
#endif
VelocityTracking,
///
/// Move the Interactable object by moving the kinematic Rigidbody towards the target position and orientation.
/// Use this if you want to keep the visual representation synchronized to match its Physics state,
/// and if you want to allow the object to be able to move through other Colliders without a Rigidbody
/// as it follows the Interactor.
///
///
/// Unity will call the movement methods during the FixedUpdate function. This Interactable will move at the
/// framerate-independent interval of the Physics update, which may be slower than the Update rate.
/// If the Rigidbody is not set to use interpolation or extrapolation, as the Interactable
/// follows the Interactor, it may not visually update position each frame and be a slight distance
/// behind the Interactor or controller due to the difference between the Physics update rate
/// and the render update rate. Collisions will be more accurate as compared to
/// since with this method, the Rigidbody will be moved by settings its internal velocity rather than
/// instantly teleporting to match the Transform pose.
///
///
///
Kinematic,
///
/// Move the Interactable object by setting the position and rotation of the Transform every frame.
/// Use this if you want the visual representation to be updated each frame, minimizing latency,
/// however with the tradeoff that it will be able to move through other Colliders without a Rigidbody
/// as it follows the Interactor.
///
///
/// Unity will set the Transform values each frame, which may be faster than the framerate-independent
/// interval of the Physics update. The Collider of the Interactable object may be a slight distance
/// behind the visual as it follows the Interactor due to the difference between the Physics update rate
/// and the render update rate. Collisions will not be computed as accurately as
/// since with this method, the Rigidbody will be forced to instantly teleport poses to match the Transform pose
/// rather than moving the Rigidbody through setting its internal velocity.
///
///
///
Instantaneous,
}
///
/// Options for how to calculate an Interactable distance to a location in world space.
///
///
public enum DistanceCalculationMode
{
///
/// Calculates the distance using the Interactable's transform position.
/// This option has low performance cost, but it may have low distance calculation accuracy for some objects.
///
TransformPosition,
///
/// Calculates the distance using the Interactable's colliders list using the shortest distance to each.
/// This option has moderate performance cost and should have moderate distance calculation accuracy for most objects.
///
///
ColliderPosition,
///
/// Calculates the distance using the Interactable's colliders list using the shortest distance to the closest point of each
/// (either on the surface or inside the Collider).
/// This option has high performance cost but high distance calculation accuracy.
///
///
/// The Interactable's colliders can only be of type , , , or convex .
///
///
///
ColliderVolume,
}
///
public event Action registered;
///
public event Action unregistered;
///
/// Overriding callback of this object's distance calculation.
/// Use this to change the calculation performed in without needing to create a derived class.
///
/// When a callback is assigned to this property, the execution calls it to perform the
/// distance calculation instead of using its default calculation (specified by in this base class).
/// Assign to this property to restore the default calculation.
///
///
/// The assigned callback will be invoked to calculate and return the distance information of the point on this
/// Interactable (the first parameter) closest to the given location (the second parameter).
/// The given location and returned distance information are in world space.
///
///
///
public Func getDistanceOverride { get; set; }
[SerializeField]
XRInteractionManager m_InteractionManager;
///
/// The that this Interactable will communicate with (will find one if ).
///
public XRInteractionManager interactionManager
{
get => m_InteractionManager;
set
{
m_InteractionManager = value;
if (Application.isPlaying && isActiveAndEnabled)
RegisterWithInteractionManager();
}
}
[SerializeField]
#pragma warning disable IDE0044 // Add readonly modifier -- readonly fields cannot be serialized by Unity
List m_Colliders = new List();
#pragma warning restore IDE0044
///
/// (Read Only) Colliders to use for interaction with this Interactable (if empty, will use any child Colliders).
///
public List colliders => m_Colliders;
[SerializeField]
InteractionLayerMask m_InteractionLayers = 1;
///
/// Allows interaction with Interactors whose Interaction Layer Mask overlaps with any Layer in this Interaction Layer Mask.
///
///
///
///
///
public InteractionLayerMask interactionLayers
{
get => m_InteractionLayers;
set => m_InteractionLayers = value;
}
[SerializeField]
DistanceCalculationMode m_DistanceCalculationMode = DistanceCalculationMode.ColliderPosition;
///
/// Specifies how this Interactable calculates its distance to a location, either using its Transform position, Collider
/// position or Collider volume.
///
///
///
///
public DistanceCalculationMode distanceCalculationMode
{
get => m_DistanceCalculationMode;
set => m_DistanceCalculationMode = value;
}
[SerializeField]
InteractableSelectMode m_SelectMode = InteractableSelectMode.Single;
///
public InteractableSelectMode selectMode
{
get => m_SelectMode;
set => m_SelectMode = value;
}
[SerializeField]
InteractableFocusMode m_FocusMode = InteractableFocusMode.Single;
///
public InteractableFocusMode focusMode
{
get => m_FocusMode;
set => m_FocusMode = value;
}
[SerializeField]
GameObject m_CustomReticle;
///
/// The reticle that appears at the end of the line when valid.
///
public GameObject customReticle
{
get => m_CustomReticle;
set => m_CustomReticle = value;
}
[SerializeField]
bool m_AllowGazeInteraction;
///
/// Enables interaction with .
///
public bool allowGazeInteraction
{
get => m_AllowGazeInteraction;
set => m_AllowGazeInteraction = value;
}
[SerializeField]
bool m_AllowGazeSelect;
///
/// Enables to select this .
///
///
public bool allowGazeSelect
{
get => m_AllowGazeSelect;
set => m_AllowGazeSelect = value;
}
[SerializeField]
bool m_OverrideGazeTimeToSelect;
///
public bool overrideGazeTimeToSelect
{
get => m_OverrideGazeTimeToSelect;
set => m_OverrideGazeTimeToSelect = value;
}
[SerializeField]
float m_GazeTimeToSelect = 0.5f;
///
public float gazeTimeToSelect
{
get => m_GazeTimeToSelect;
set => m_GazeTimeToSelect = value;
}
[SerializeField]
bool m_OverrideTimeToAutoDeselectGaze;
///
public bool overrideTimeToAutoDeselectGaze
{
get => m_OverrideTimeToAutoDeselectGaze;
set => m_OverrideTimeToAutoDeselectGaze = value;
}
[SerializeField]
float m_TimeToAutoDeselectGaze = 3f;
///
public float timeToAutoDeselectGaze
{
get => m_TimeToAutoDeselectGaze;
set => m_TimeToAutoDeselectGaze = value;
}
[SerializeField]
bool m_AllowGazeAssistance;
///
/// Enables gaze assistance with this interactable.
///
public bool allowGazeAssistance
{
get => m_AllowGazeAssistance;
set => m_AllowGazeAssistance = value;
}
[SerializeField]
HoverEnterEvent m_FirstHoverEntered = new HoverEnterEvent();
///
public HoverEnterEvent firstHoverEntered
{
get => m_FirstHoverEntered;
set => m_FirstHoverEntered = value;
}
[SerializeField]
HoverExitEvent m_LastHoverExited = new HoverExitEvent();
///
public HoverExitEvent lastHoverExited
{
get => m_LastHoverExited;
set => m_LastHoverExited = value;
}
[SerializeField]
HoverEnterEvent m_HoverEntered = new HoverEnterEvent();
///
public HoverEnterEvent hoverEntered
{
get => m_HoverEntered;
set => m_HoverEntered = value;
}
[SerializeField]
HoverExitEvent m_HoverExited = new HoverExitEvent();
///
public HoverExitEvent hoverExited
{
get => m_HoverExited;
set => m_HoverExited = value;
}
[SerializeField]
SelectEnterEvent m_FirstSelectEntered = new SelectEnterEvent();
///
public SelectEnterEvent firstSelectEntered
{
get => m_FirstSelectEntered;
set => m_FirstSelectEntered = value;
}
[SerializeField]
SelectExitEvent m_LastSelectExited = new SelectExitEvent();
///
public SelectExitEvent lastSelectExited
{
get => m_LastSelectExited;
set => m_LastSelectExited = value;
}
[SerializeField]
SelectEnterEvent m_SelectEntered = new SelectEnterEvent();
///
public SelectEnterEvent selectEntered
{
get => m_SelectEntered;
set => m_SelectEntered = value;
}
[SerializeField]
SelectExitEvent m_SelectExited = new SelectExitEvent();
///
public SelectExitEvent selectExited
{
get => m_SelectExited;
set => m_SelectExited = value;
}
[SerializeField]
FocusEnterEvent m_FirstFocusEntered = new FocusEnterEvent();
///
public FocusEnterEvent firstFocusEntered
{
get => m_FirstFocusEntered;
set => m_FirstFocusEntered = value;
}
[SerializeField]
FocusExitEvent m_LastFocusExited = new FocusExitEvent();
///
public FocusExitEvent lastFocusExited
{
get => m_LastFocusExited;
set => m_LastFocusExited = value;
}
[SerializeField]
FocusEnterEvent m_FocusEntered = new FocusEnterEvent();
///
public FocusEnterEvent focusEntered
{
get => m_FocusEntered;
set => m_FocusEntered = value;
}
[SerializeField]
FocusExitEvent m_FocusExited = new FocusExitEvent();
///
public FocusExitEvent focusExited
{
get => m_FocusExited;
set => m_FocusExited = value;
}
[SerializeField]
ActivateEvent m_Activated = new ActivateEvent();
///
public ActivateEvent activated
{
get => m_Activated;
set => m_Activated = value;
}
[SerializeField]
DeactivateEvent m_Deactivated = new DeactivateEvent();
///
public DeactivateEvent deactivated
{
get => m_Deactivated;
set => m_Deactivated = value;
}
readonly HashSetList m_InteractorsHovering = new HashSetList();
///
public List interactorsHovering => (List)m_InteractorsHovering.AsList();
///
public bool isHovered { get; private set; }
readonly HashSetList m_InteractorsSelecting = new HashSetList();
///
public List interactorsSelecting => (List)m_InteractorsSelecting.AsList();
///
public IXRSelectInteractor firstInteractorSelecting { get; private set; }
///
public bool isSelected { get; private set; }
readonly HashSetList m_InteractionGroupsFocusing = new HashSetList();
///
public List interactionGroupsFocusing => (List)m_InteractionGroupsFocusing.AsList();
///
public IXRInteractionGroup firstInteractionGroupFocusing { get; private set; }
///
public bool isFocused { get; private set; }
///
public bool canFocus => m_FocusMode != InteractableFocusMode.None;
[SerializeField]
[RequireInterface(typeof(IXRHoverFilter))]
List