using UnityEngine.UI;
using UnityEngine.XR.Interaction.Toolkit.Interactables;
using UnityEngine.XR.Interaction.Toolkit.Interactables.Visuals;
using UnityEngine.XR.Interaction.Toolkit.Interactors.Visuals;
using UnityEngine.XR.Interaction.Toolkit.Locomotion.Teleportation;
namespace UnityEngine.XR.Interaction.Toolkit.Samples.StarterAssets
{
///
/// A custom reticle for a that displays its progress towards evaluating
/// a destination anchor and an indicator pointing in the direction of the destination anchor.
///
public class MultiAnchorTeleportReticle : MonoBehaviour, IXRInteractableCustomReticle
{
[SerializeField]
[Tooltip("Filled image that displays the progress towards evaluating a destination anchor.")]
Image m_TimerProgressFilledImage;
///
/// image that displays the progress towards evaluating a destination anchor.
///
public Image timerProgressFilledImage
{
get => m_TimerProgressFilledImage;
set => m_TimerProgressFilledImage = value;
}
[SerializeField]
[Tooltip("Object that is rotated about its Z axis to point at the destination anchor.")]
GameObject m_DestinationIndicator;
///
/// Object that is rotated about its Z axis to point at the destination anchor.
///
public GameObject destinationIndicator
{
get => m_DestinationIndicator;
set => m_DestinationIndicator = value;
}
[SerializeField]
[Tooltip("Object that is rotated about its Z axis to point at the potential destination while still evaluating.")]
GameObject m_PotentialDestinationIndicator;
///
/// Object that is rotated about its Z axis to point at the potential destination while still evaluating.
///
public GameObject potentialDestinationIndicator
{
get => m_PotentialDestinationIndicator;
set => m_PotentialDestinationIndicator = value;
}
[SerializeField]
[Tooltip("The amount of time, in seconds, between updates to the indicator pointing at the potential destination.")]
float m_PotentialIndicatorUpdateFrequency = 0.1f;
///
/// The amount of time, in seconds, between updates to the indicator pointing at the potential destination.
///
public float potentialIndicatorUpdateFrequency
{
get => m_PotentialIndicatorUpdateFrequency;
set => m_PotentialIndicatorUpdateFrequency = value;
}
TeleportationMultiAnchorVolume m_AnchorVolume;
float m_LastPotentialIndicatorUpdateTime;
///
public void OnReticleAttached(XRBaseInteractable interactable, IXRCustomReticleProvider reticleProvider)
{
m_AnchorVolume = interactable as TeleportationMultiAnchorVolume;
m_PotentialDestinationIndicator.SetActive(false);
m_DestinationIndicator.SetActive(false);
m_TimerProgressFilledImage.type = Image.Type.Filled;
m_TimerProgressFilledImage.fillAmount = 0f;
if (m_AnchorVolume == null)
return;
m_AnchorVolume.destinationAnchorChanged += OnDestinationAnchorChanged;
}
///
public void OnReticleDetaching()
{
if (m_AnchorVolume == null)
return;
m_AnchorVolume.destinationAnchorChanged -= OnDestinationAnchorChanged;
m_AnchorVolume = null;
}
///
/// See .
///
protected void Update()
{
if (m_AnchorVolume == null)
return;
var destinationAnchor = m_AnchorVolume.destinationAnchor;
if (destinationAnchor != null)
{
PointAtTarget(m_DestinationIndicator.transform, destinationAnchor.position);
return;
}
m_TimerProgressFilledImage.fillAmount = m_AnchorVolume.destinationEvaluationProgress;
if (Time.time - m_LastPotentialIndicatorUpdateTime >= m_PotentialIndicatorUpdateFrequency)
UpdatePotentialDestinationIndicator();
}
void UpdatePotentialDestinationIndicator()
{
m_LastPotentialIndicatorUpdateTime = Time.time;
if (!m_AnchorVolume.destinationEvaluationSettings.Value.pollForDestinationChange)
{
m_PotentialDestinationIndicator.SetActive(false);
return;
}
var potentialDestinationIndex = m_AnchorVolume.destinationEvaluationFilter.GetDestinationAnchorIndex(m_AnchorVolume);
var anchors = m_AnchorVolume.anchorTransforms;
if (potentialDestinationIndex < 0 || potentialDestinationIndex >= anchors.Count)
{
m_PotentialDestinationIndicator.SetActive(false);
return;
}
var potentialDestination = anchors[potentialDestinationIndex];
if (potentialDestination == null)
{
m_PotentialDestinationIndicator.SetActive(false);
return;
}
m_PotentialDestinationIndicator.SetActive(true);
PointAtTarget(m_PotentialDestinationIndicator.transform, potentialDestination.position);
}
void OnDestinationAnchorChanged(TeleportationMultiAnchorVolume anchorVolume)
{
var destinationAnchor = anchorVolume.destinationAnchor;
if (destinationAnchor != null)
{
m_TimerProgressFilledImage.fillAmount = 1f;
m_PotentialDestinationIndicator.SetActive(false);
m_DestinationIndicator.SetActive(true);
PointAtTarget(m_DestinationIndicator.transform, destinationAnchor.position);
}
else
{
m_TimerProgressFilledImage.fillAmount = 0f;
m_DestinationIndicator.SetActive(false);
}
}
static void PointAtTarget(Transform indicatorTransform, Vector3 targetPosition)
{
indicatorTransform.rotation = Quaternion.LookRotation(indicatorTransform.forward, targetPosition - indicatorTransform.position);
}
}
}