// --------------------------------------------------------------------------------------------------------------------
//
// 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
}
}