using UnityEngine; using UnityEngine.EventSystems; namespace Unity.XR.XREAL.Samples { public class XREALLaserReticle : MonoBehaviour { /// Values that represent reticle states. public enum ReticleState { /// An enum constant representing the hide option. Hide, /// An enum constant representing the normal option. Normal, /// An enum constant representing the hover option. Hover, } /// The raycaster. [SerializeField] private XREALLaser m_Laser; /// The default visual. [SerializeField] private GameObject m_DefaultVisual; /// The hover visual. [SerializeField] private GameObject m_HoverVisual; /// Flag to indicate whether the reticle object should be forcibly hidden [SerializeField] private bool m_ForceHideReticle = false; /// The hit target. private GameObject m_HitTarget; /// True if is visible, false if not. private bool m_IsVisible = true; /// The default distance. public float defaultDistance = 2.5f; /// The reticle size ratio. public float reticleSizeRatio = 0.02f; [SerializeField] private Camera m_MainCamera; /// Gets the main camera. private Camera MainCamera { get => m_MainCamera; } public bool ForceHideReticle { get => m_ForceHideReticle; set => m_ForceHideReticle = value; } private void Awake() { if (m_MainCamera == null) m_MainCamera = Camera.main; SwitchReticleState(ReticleState.Hide); } void Start() { if (m_MainCamera == null) { m_MainCamera = Camera.main; } } protected virtual void LateUpdate() { if (m_ForceHideReticle || m_Laser == null || m_Laser.RayOriginTransform == null || m_Laser.AttachTransform == null) { SwitchReticleState(ReticleState.Hide); return; } Vector3 position = m_Laser.RayOriginTransform.position + m_Laser.AttachTransform.forward * defaultDistance; Quaternion rotation = m_Laser.AttachTransform.rotation; var result = m_Laser.GetCurrentRaycast(out RaycastHit hit, out RaycastResult raycastResult, out bool isUIHitClosest); if (result) { SwitchReticleState(ReticleState.Hover); if (isUIHitClosest) { position = raycastResult.worldPosition; rotation = Quaternion.LookRotation(raycastResult.worldNormal, m_Laser.AttachTransform.forward); } else { position = hit.point; rotation = Quaternion.LookRotation(hit.normal, m_Laser.AttachTransform.forward); } } else { SwitchReticleState(ReticleState.Normal); } transform.position = position; transform.rotation = rotation; if (MainCamera) transform.localScale = Vector3.one * reticleSizeRatio * (transform.position - MainCamera.transform.position).magnitude; } private void OnDisable() { SwitchReticleState(ReticleState.Hide); } /// Switch reticle state. /// The state. private void SwitchReticleState(ReticleState state) { switch (state) { case ReticleState.Hide: m_DefaultVisual.SetActive(false); m_HoverVisual.SetActive(false); break; case ReticleState.Normal: m_DefaultVisual.SetActive(true); m_HoverVisual.SetActive(false); break; case ReticleState.Hover: m_DefaultVisual.SetActive(false); m_HoverVisual.SetActive(true); break; default: break; } } /// Sets a visible. /// True if is visible, false if not. public void SetVisible(bool isVisible) { this.m_IsVisible = isVisible; } } }