// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using UltimateXR.Avatar; using UltimateXR.Avatar.Rig; using UltimateXR.Core; using UltimateXR.Core.Components.Composite; using UltimateXR.Extensions.Unity.Math; using UltimateXR.Manipulation; using UnityEngine; namespace UltimateXR.UI { /// /// Component that, added to the tip of an finger, allows it to interact with user interfaces. /// It is normally added only to the index fingers so that other fingers don't generate unwanted interactions, but it /// can be added to any finger. /// public class UxrFingerTip : UxrAvatarComponent { #region Public Types & Data /// /// Gets whether the component belonging to the same hand the finger tip is, is currently /// grabbing something. /// public bool IsHandGrabbing => HandGrabber && HandGrabber.GrabbedObject != null; /// /// Gets the current world position. /// public Vector3 WorldPos => transform.position; /// /// Gets the current world direction. The direction points in the direction the finger would be pointing. It is used to /// filter out interactions where the forward vector is not perpendicular enough to the UI to interact with it. /// public Vector3 WorldDir => transform.forward; /// /// Gets the current world speed the finger tip is travelling at. /// public Vector3 WorldSpeed => _updateCount >= 2 ? (_currentWorldPos - _lastWorldPos) / Time.deltaTime : Vector3.zero; /// /// Gets the hand the finger tip belongs to. /// public UxrHandSide Side { get; private set; } /// /// Gets the component of the hand the finger tip belongs to. /// public UxrGrabber HandGrabber { get; private set; } #endregion #region Public Methods /// /// Checks whether the finger tip is inside a box collider. /// /// Box collider /// /// Whether the operation that the check is for allows the hand to be grabbing something. /// This means if the value is false and the hand is currently grabbing, it will always return false no matter the /// finger tip is inside the box or not /// /// Whether the finger tip is inside public bool IsInside(BoxCollider box, bool allowWhileGrabbing = false) { if (!allowWhileGrabbing && IsHandGrabbing) { return false; } return box != null && transform.position.IsInsideBox(box); } #endregion #region Unity /// /// Initializes the component. /// protected override void Awake() { base.Awake(); UxrAvatarRig.GetHandSide(transform, out UxrHandSide handSide); Side = handSide; } /// /// Resets the internal state. /// protected override void OnEnable() { base.OnEnable(); _updateCount = 0; } /// /// Initializes the component. /// protected override void Start() { base.Start(); if (!(Avatar && Avatar.AvatarController)) { return; } foreach (UxrGrabber grabber in UxrGrabber.GetComponents(Avatar, true)) { if (grabber.Side == Side) { HandGrabber = grabber; break; } } } /// /// Updates the component. /// private void Update() { _lastWorldPos = _currentWorldPos; _currentWorldPos = transform.position; _updateCount++; } #endregion #region Private Types & Data private int _updateCount; private Vector3 _currentWorldPos; private Vector3 _lastWorldPos; #endregion } }