// Object Touch Auto Interact|Interactables|35050 namespace VRTK { using UnityEngine; using System.Collections.Generic; /// /// Allows for Interact Grab or Interact Use interactions to automatically happen upon touching an Interactable Object. /// /// /// **Required Components:** /// * `VRTK_InteractableObject` - The Interactable Object component to detect interactions on. This must be applied on the same GameObject as this script if one is not provided via the `Interactable Object` parameter. /// /// **Script Usage:** /// * Place the `VRTK_ObjectTouchAutoInteract` script on either: /// * The GameObject of the Interactable Object to detect interactions on. /// * Any other scene GameObject and provide a valid `VRTK_InteractableObject` component to the `Interactable Object` parameter of this script. /// [AddComponentMenu("VRTK/Scripts/Interactions/Interactables/VRTK_ObjectTouchAutoInteract")] public class VRTK_ObjectTouchAutoInteract : VRTK_InteractableListener { /// /// Situation when auto interaction can occur. /// public enum AutoInteractions { /// /// Auto interaction can never occur on touch. /// Never, /// /// Auto interaction will occur on touch even if the specified interaction button is not held down. /// NoButtonHeld, /// /// Auto interaction will only occur on touch if the specified interaction button is held down. /// ButtonHeld } [Header("Auto Grab")] [Tooltip("Determines when a grab on touch should occur.")] public AutoInteractions grabOnTouchWhen = AutoInteractions.Never; [Tooltip("After being ungrabbed, another auto grab on touch can only occur after this time.")] public float regrabDelay = 0.1f; [Tooltip("If this is checked then the grab on touch check will happen every frame and not only on the first touch of the Interactable Object.")] public bool continuousGrabCheck = false; [Header("Auto Use")] [Tooltip("Determines when a use on touch should occur.")] public AutoInteractions useOnTouchWhen = AutoInteractions.Never; [Tooltip("After being unused, another auto use on touch can only occur after this time.")] public float reuseDelay = 0.1f; [Tooltip("If this is checked then the use on touch check will happen every frame and not only on the first touch of the Interactable Object.")] public bool continuousUseCheck = false; [Header("Custom Settings")] [Tooltip("The Interactable Object that the auto interaction will occur on. If this is blank then the script must be on the same GameObject as the Interactable Object script.")] public VRTK_InteractableObject interactableObject; protected float regrabTimer; protected float reuseTimer; protected List touchers = new List(); protected virtual void OnEnable() { regrabTimer = 0f; reuseTimer = 0f; touchers.Clear(); EnableListeners(); } protected virtual void OnDisable() { TearDownListeners(); } protected virtual void Update() { if (interactableObject != null && (continuousGrabCheck || continuousUseCheck)) { for (int i = 0; i < touchers.Count; i++) { if (continuousGrabCheck) { CheckGrab(touchers[i]); } if (continuousUseCheck) { CheckUse(touchers[i]); } } } } protected override bool SetupListeners(bool throwError) { interactableObject = (interactableObject != null ? interactableObject : GetComponentInParent()); if (interactableObject != null) { interactableObject.InteractableObjectTouched += InteractableObjectTouched; interactableObject.InteractableObjectUntouched += InteractableObjectUntouched; interactableObject.InteractableObjectUngrabbed += InteractableObjectUngrabbed; interactableObject.InteractableObjectUnused += InteractableObjectUnused; return true; } else if (throwError) { VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "VRTK_ObjectTouchAutoInteract", "VRTK_InteractableObject", "the same or parent")); } return false; } protected override void TearDownListeners() { if (interactableObject != null) { interactableObject.InteractableObjectTouched -= InteractableObjectTouched; interactableObject.InteractableObjectUntouched -= InteractableObjectUntouched; interactableObject.InteractableObjectUngrabbed -= InteractableObjectUngrabbed; interactableObject.InteractableObjectUnused -= InteractableObjectUnused; } } protected virtual void InteractableObjectTouched(object sender, InteractableObjectEventArgs e) { ManageTouchers(e.interactingObject, true); CheckGrab(e.interactingObject); CheckUse(e.interactingObject); } protected virtual void InteractableObjectUntouched(object sender, InteractableObjectEventArgs e) { ManageTouchers(e.interactingObject, false); } protected virtual void InteractableObjectUngrabbed(object sender, InteractableObjectEventArgs e) { regrabTimer = regrabDelay + Time.time; } protected virtual void InteractableObjectUnused(object sender, InteractableObjectEventArgs e) { reuseTimer = reuseDelay + Time.time; } protected virtual void ManageTouchers(GameObject interactingObject, bool add) { if (add) { VRTK_SharedMethods.AddListValue(touchers, interactingObject, true); } else { touchers.Remove(interactingObject); } } protected virtual void CheckGrab(GameObject interactingObject) { if (grabOnTouchWhen != AutoInteractions.Never && regrabTimer < Time.time) { VRTK_InteractGrab interactGrabScript = interactingObject.GetComponentInChildren(); if (interactGrabScript != null && (grabOnTouchWhen == AutoInteractions.NoButtonHeld || (grabOnTouchWhen == AutoInteractions.ButtonHeld && interactGrabScript.IsGrabButtonPressed()))) { interactGrabScript.AttemptGrab(); } } } protected virtual void CheckUse(GameObject interactingObject) { if (useOnTouchWhen != AutoInteractions.Never && reuseTimer < Time.time) { VRTK_InteractUse interactUseScript = interactingObject.GetComponentInChildren(); if (interactUseScript != null && (useOnTouchWhen == AutoInteractions.NoButtonHeld || (useOnTouchWhen == AutoInteractions.ButtonHeld && interactUseScript.IsUseButtonPressed()))) { if (!interactableObject.holdButtonToUse && interactableObject.IsUsing()) { interactableObject.ForceStopInteracting(); } else { interactUseScript.AttemptUse(); } } } } } }