using System; namespace UnityEngine.XR.Hands.Gestures { /// /// A set of targets for the values of a single /// . /// [Serializable] public class XRFingerShapeCondition { /// /// A configuration for a and its /// desired value in a condition. /// [Serializable] public struct Target { [SerializeField] [Tooltip("The finger shape type that is being checked in this condition.")] XRFingerShapeType m_ShapeType; /// /// The that is being checked in /// this condition. /// public XRFingerShapeType shapeType { get => m_ShapeType; set => m_ShapeType = value; } /// /// The desired value to compare the specified /// to in this condition. /// public float desired { get => m_Desired; set => m_Desired = Mathf.Clamp01(value); } [SerializeField] [Range(0f, 1f)] [Tooltip("The maximum the value can differ from the Desired value for the condition to be met.")] float m_UpperTolerance; /// /// The maximum the value can differ from the /// for the condition to be met. /// public float upperTolerance { get => m_UpperTolerance; set => m_UpperTolerance = Mathf.Clamp01(value); } [Range(0f, 1f)] [SerializeField] [Tooltip("The minimum the value can differ from the Desired value for the condition to be met.")] float m_LowerTolerance; /// /// The minimum the value can differ from the /// for the condition to be met. /// public float lowerTolerance { get => m_LowerTolerance; set => m_LowerTolerance = Mathf.Clamp01(value); } [HideInInspector, SerializeField, Obsolete("Deprecated. Use upperTolerance and lowerTolerance instead.")] float m_Tolerance; /// /// The deprecated maximum the value can differ from the /// for the condition to be met. /// [Obsolete("Deprecated. Use upperTolerance and lowerTolerance instead.")] public float tolerance { get => m_Tolerance; set { Debug.LogWarning("Deprecated. Use upperTolerance and lowerTolerance instead. Both of those properties will be set via the deprecated tolerance value."); m_Tolerance = Mathf.Clamp01(value); upperTolerance = m_Tolerance; lowerTolerance = m_Tolerance; // Reset tolerance value to zero in order to prevent further deprecated upgrade functionality m_Tolerance = 0f; } } [Range(0f, 1f)] [SerializeField] [Tooltip("The desired value for the finger shape.")] float m_Desired; } [SerializeField] [Tooltip("The finger to check for this condition.")] XRHandFingerID m_FingerID = XRHandFingerID.Index; [SerializeField] [Tooltip("The targets to check for this condition.")] Target[] m_Targets; /// /// Finger shape targets to check for this finger. Setting this value /// will update the needed for this /// condition. /// public Target[] targets { get => m_Targets; set { m_Targets = value; m_TypesNeededDirty = true; } } /// /// The finger ID that this condition applies to. /// public XRHandFingerID fingerID { get => m_FingerID; set => m_FingerID = value; } XRFingerShapeTypes m_TypesNeeded; bool m_TypesNeededDirty = true; /// /// Checks if the matches the targets in /// this condition. /// /// /// The target values are checked in the order listed. /// The method will return as soon as it finds /// one value that is different from the target by more than the tolerance. /// /// /// The hand joint updated event argument that contains the latest hand data. /// /// /// Returns if all the finger shape conditions /// are met. Otherwise, returns . /// public bool CheckCondition(XRHandJointsUpdatedEventArgs eventArgs) { UpdateTypesNeededIfDirty(); if (m_TypesNeeded == XRFingerShapeTypes.None) return true; var fingerShape = eventArgs.hand.CalculateFingerShape(m_FingerID, m_TypesNeeded); for (var index = 0; index < m_Targets.Length; ++index) { float value; bool hasValue; var target = targets[index]; switch (target.shapeType) { case XRFingerShapeType.FullCurl: hasValue = fingerShape.TryGetFullCurl(out value); break; case XRFingerShapeType.BaseCurl: hasValue = fingerShape.TryGetBaseCurl(out value); break; case XRFingerShapeType.TipCurl: hasValue = fingerShape.TryGetTipCurl(out value); break; case XRFingerShapeType.Pinch: hasValue = fingerShape.TryGetPinch(out value); break; case XRFingerShapeType.Spread: hasValue = fingerShape.TryGetSpread(out value); break; default: throw new ArgumentOutOfRangeException($"Finger shape type {target.shapeType} is invalid for finger shape target condition."); } if (!hasValue || value < (target.desired - target.lowerTolerance) || value > (target.desired + target.upperTolerance)) { return false; } } return true; } internal void UpdateTypesNeededIfDirty() { if (!m_TypesNeededDirty) return; m_TypesNeeded = XRFingerShapeTypes.None; for (var index = 0; index < m_Targets.Length; index++) m_TypesNeeded |= (XRFingerShapeTypes)(1 << (int)m_Targets[index].shapeType); m_TypesNeededDirty = false; } } }