eye-therapy-2 / Assets / Scripts / Interactables / Pump / XRTwoHandPumpInteractable.cs
XRTwoHandPumpInteractable.cs
Raw
using System;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.XR.Interaction.Toolkit;

[Serializable]
public class PumpEvent : UnityEvent<float> { }

[RequireComponent(typeof(SpringJoint))]
public class XRTwoHandPumpInteractable : XRGrabInteractable
{
    [SerializeField] private float pushDepth = 0.1f;
    [SerializeField] [Range(0, 1)] private float pumpThreshold = 0.1f;
    [SerializeField] private PumpEvent OnPumpUsed = null;

    private Vector3 startPosition;
    private XRPumpHold hold1 = null;
    private XRPumpHold hold2 = null;
    private XRBaseInteractor hand1 = null;
    private XRBaseInteractor hand2 = null;
    private Transform originalParent = null;
    private Rigidbody rb;
    public float Value { get; private set; } = 0f;

    protected override void Awake()
    {
        base.Awake();
        SetupHandHolds();
        trackPosition = false;
        startPosition = transform.localPosition;
        originalParent = transform.parent;
        rb = GetComponent<Rigidbody>();
    }

    private void SetupHandHolds()
    {
        XRPumpHold[] handHolds = GetComponentsInChildren<XRPumpHold>();
        if (handHolds.Length != 2) Debug.LogError(gameObject.name + " : Wrong number of XRHandHolds in children detected");
        hold1 = handHolds[0];
        hold2 = handHolds[1];
        hold1.Setup(this);
        hold2.Setup(this);
    }

    public void SetupGrab(SelectEnterEventArgs args)
    {
        var interactor = args.interactor;
        if (!hand1)
        {
            hand1 = interactor;
            OnSelectEntering(args);
        }
        else
        {
            hand2 = interactor;
            trackPosition = true;
        }
        transform.parent = originalParent;
    }

    public void CancelGrab(SelectExitEventArgs args)
    {
        var interactor = args.interactor;
        trackPosition = false;
        if (!hand2)
        {
            hand1 = null;
            OnSelectExiting(args);
        }
        else
        {
            if (interactor == hand1)
            {
                OnSelectExiting(args);
                hand1 = hand2;
                SelectEnterEventArgs argsNew = new SelectEnterEventArgs();
                args.interactor = hand2;
                args.interactable = this;
                OnSelectEntering(argsNew);
            }
            hand2 = null;
        }
        transform.parent = originalParent;
    }

    public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase updatePhase)
    {
        base.ProcessInteractable(updatePhase);
        transform.localPosition = new Vector3(startPosition.x, Mathf.Clamp(transform.localPosition.y, startPosition.y - pushDepth, startPosition.y), startPosition.z);
        
        if (hand1 && hand2 && updatePhase == XRInteractionUpdateOrder.UpdatePhase.Late && (transform.localPosition.y < startPosition.y - pumpThreshold * pushDepth) && (transform.localPosition.y > startPosition.y - pushDepth))
        {
            Value = Vector3.Dot(transform.InverseTransformDirection(new Vector3(0, -1, 0)), rb.velocity);
            //if (Value > 0.1f) Debug.Log(Value + "===" + transform.localPosition.y); // for debug
            if (Value > 0.1f) OnPumpUsed.Invoke(Value);
        }
        else
        {
            Value = 0f;
        }
    }

#if UNITY_EDITOR
    private void OnValidate()
    {
        interactionLayerMask = 0;
    }
#endif
}