Search Unity

Bug XR Interaction Toolkit - Two handed Grabbable Questions

Discussion in 'VR' started by Project-NSX, Oct 4, 2022.

  1. Project-NSX

    Project-NSX

    Joined:
    Apr 11, 2019
    Posts:
    25
    Hi all

    I have an object (drone controller) with one handle on either side (pictured below):

    upload_2022-10-4_15-17-33.png

    I'd like for this object to be holdable with both hands by the player. Having seen tutorials on YouTube and online I can see that they mostly refer to holding a two-handed weapon such as an assault rifle, which obviously has a main handle and a secondary handle.

    So far for this object, I have it switch the attach transform depending on which interactor selects the object, and then a second object with an XR Simple Interactable component on it will activate, allowing the player to select the other handle using their other interactor.

    The part I need help with is just setting the rotation of the object, as the code I have is from an old project where a sledgehammer was the two-handed object.
    Unfortunately, I've been struggling with vector mathematics with this one so haven't found a solution yet. and any help to solve this will be appreciated. Also, this is the only way I could think of to solve this problem, but if you have any suggestions I'd love to hear them!

    Code snippet below and below that I'll leave the full code in case you want to see how it works.
    I realise this needs refactoring but want to get it working properly before I go splitting methods and classes out.

    Code (CSharp):
    1.  
    2.     // Set object rotation based on the first and second hand.
    3.     private Quaternion GetTwoHandRotation()
    4.     {
    5.         Quaternion targetRotation;
    6.  
    7.         if (twoHandRotationType == TwoHandRotationType.None)
    8.         {
    9.             targetRotation = Quaternion.LookRotation(secondInteractor.attachTransform.position - selectingInteractor.attachTransform.position);
    10.         }
    11.         else if (twoHandRotationType == TwoHandRotationType.First)
    12.         {
    13.             targetRotation = Quaternion.LookRotation(secondInteractor.attachTransform.position - selectingInteractor.attachTransform.position, selectingInteractor.transform.up);
    14.         }
    15.         else
    16.         {
    17.             targetRotation = Quaternion.LookRotation(secondInteractor.attachTransform.position - selectingInteractor.attachTransform.position, secondInteractor.transform.up);
    18.         }
    19.         return targetRotation;
    20.     }
    21. ...
    22.  






    Full Code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.XR.Interaction.Toolkit;
    5. using HighlightPlus;
    6.  
    7. public class DroneRemote : XRGrabInteractable
    8. {
    9.     #region Variables - Grab
    10.     public enum TwoHandRotationType { None, First, Second }
    11.     [SerializeField] private List<XRSimpleInteractable> secondHandGrabpoints = new List<XRSimpleInteractable>();
    12.     [SerializeField] private LocomotionController locomotionController;
    13.     [SerializeField] private TwoHandRotationType twoHandRotationType;
    14.     [SerializeField] private Transform leftAttachTransform;
    15.     [SerializeField] private Transform rightAttachTransform;
    16.     [SerializeField] private GameObject leftRemoteHandle;
    17.     [SerializeField] private GameObject rightRemoteHandle;
    18.  
    19.     private XRBaseInteractor secondInteractor;
    20.     private Quaternion attachIntialRotation;
    21.     private Quaternion intialRotationOffset;
    22.     #endregion
    23.  
    24.     #region Variables - Other
    25.     [SerializeField] private DroneController droneController;
    26.     private bool highlighted = false;
    27.     private HighlightEffect highlightEffect;
    28.     private AudioSource audioSource;
    29.     private bool alreadyPlaying = false;
    30.     #endregion
    31.    
    32.     private void Start()
    33.     {
    34.         highlightEffect = this.GetComponent<HighlightEffect>();
    35.  
    36.         // Add listeners for grab on both secondary handles.
    37.         foreach (var item in secondHandGrabpoints)
    38.         {
    39.             item.selectEntered.AddListener(OnSecondHandGrab);
    40.             item.selectExited.AddListener(OnSecondHandRelease);
    41.         }
    42.     }
    43.  
    44.     protected override void OnDisable()
    45.     {
    46.         base.OnDisable();
    47.     }
    48.  
    49.     protected override void OnEnable()
    50.     {
    51.         base.OnEnable();
    52.     }
    53.  
    54.     // Get two handed rotation if second handle is held
    55.     public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase updatePhase)
    56.     {
    57.         if (secondInteractor && selectingInteractor && selectingInteractor.gameObject.layer != LayerMask.NameToLayer("Snap Zone"))
    58.         {
    59.             selectingInteractor.attachTransform.rotation = GetTwoHandRotation();
    60.         }
    61.         base.ProcessInteractable(updatePhase);
    62.     }
    63.  
    64.     // Set object rotation based on the first and second hand.
    65.     private Quaternion GetTwoHandRotation()
    66.     {
    67.         Quaternion targetRotation;
    68.  
    69.         if (twoHandRotationType == TwoHandRotationType.None)
    70.         {
    71.             targetRotation = Quaternion.LookRotation(secondInteractor.attachTransform.position - selectingInteractor.attachTransform.position);
    72.         }
    73.         else if (twoHandRotationType == TwoHandRotationType.First)
    74.         {
    75.             targetRotation = Quaternion.LookRotation(secondInteractor.attachTransform.position - selectingInteractor.attachTransform.position, selectingInteractor.transform.up);
    76.         }
    77.         else
    78.         {
    79.             targetRotation = Quaternion.LookRotation(secondInteractor.attachTransform.position - selectingInteractor.attachTransform.position, secondInteractor.transform.up);
    80.         }
    81.         return targetRotation;
    82.     }
    83.  
    84.  
    85.     public void OnSecondHandGrab(SelectEnterEventArgs args)
    86.     {
    87.         secondInteractor = args.interactor;
    88.  
    89.         intialRotationOffset = Quaternion.Inverse(GetTwoHandRotation()) * secondInteractor.attachTransform.rotation;
    90.     }
    91.  
    92.     public void OnSecondHandRelease(SelectExitEventArgs args)
    93.     {
    94.         secondInteractor = null;
    95.  
    96.         if (selectingInteractor)
    97.         {
    98.             selectingInteractor.attachTransform.localRotation = attachIntialRotation;
    99.         }
    100.     }
    101.  
    102.     // Activate second handle depending on which hand was used to select the object.
    103.     protected override void OnSelectEntered(SelectEnterEventArgs args)
    104.     {
    105.         attachIntialRotation = args.interactor.attachTransform.localRotation;
    106.  
    107.         if (args.interactor.gameObject.CompareTag("Left Ray") || args.interactor.gameObject.CompareTag("Left Direct"))
    108.         {
    109.             attachTransform = leftAttachTransform;
    110.  
    111.             rightRemoteHandle.SetActive(true);              
    112.         }
    113.         else if(args.interactor.gameObject.CompareTag("Right Ray") || args.interactor.gameObject.CompareTag("Right Direct"))
    114.         {
    115.             attachTransform = rightAttachTransform;
    116.             leftRemoteHandle.SetActive(true);
    117.  
    118.         }
    119.  
    120.         EnableDroneController();
    121.         DisablePlayerControls();
    122.        
    123.         if(audioSource != null)
    124.         {
    125.             if(audioSource.clip != null)
    126.             {
    127.                 PlaySound(audioSource);
    128.             }
    129.         }
    130.  
    131.         base.OnSelectEntered(args);
    132.     }
    133.  
    134.     protected override void OnSelectExited(SelectExitEventArgs args)
    135.     {
    136.         rightRemoteHandle.SetActive(false);
    137.         leftRemoteHandle.SetActive(false);
    138.         secondInteractor = null;
    139.  
    140.         args.interactor.attachTransform.localRotation = attachIntialRotation;
    141.  
    142.  
    143.         EnablePlayerControls();
    144.         DisableDroneController();
    145.  
    146.         base.OnSelectExited(args);
    147.     }
    148.    
    149.     // When hover is entered, check if teleport should be disabled and swap material
    150.     protected override void OnHoverEntered(HoverEnterEventArgs args)
    151.     {
    152.         if (args.interactable.isHovered && !args.interactable.isSelected)
    153.         {
    154.             Highlight();
    155.         }
    156.         else
    157.         {
    158.             Unhighlight();
    159.         }
    160.         base.OnHoverEntered(args);
    161.     }
    162.  
    163.     // When hover is exited, check if teleport should be enabled and change materials
    164.     protected override void OnHoverExited(HoverExitEventArgs args)
    165.     {
    166.         // Material Swap Check
    167.         if (args.interactable.isHovered && !args.interactable.isSelected)
    168.         {
    169.             Highlight();
    170.         }
    171.         else
    172.         {
    173.             Unhighlight();
    174.         }
    175.  
    176.         base.OnHoverExited(args);
    177.     }
    178.  
    179.     public void Highlight()
    180.     {
    181.         if (highlighted == false)
    182.         {
    183.             highlightEffect.highlighted = true;
    184.             highlighted = true;
    185.         }
    186.     }
    187.  
    188.     public void Unhighlight()
    189.     {
    190.         highlightEffect.highlighted = false;
    191.         highlighted = false;
    192.     }
    193.  
    194.  
    195.     public void EnableDroneController()
    196.     {
    197.         droneController.droneEnabled = true;
    198.     }
    199.  
    200.     public void DisableDroneController()
    201.     {
    202.         droneController.droneEnabled = false;
    203.     }
    204.  
    205.     public void DisablePlayerControls()
    206.     {
    207.         locomotionController.DisableTeleport();
    208.         locomotionController.DisableTurn();
    209.     }
    210.  
    211.     public void EnablePlayerControls()
    212.     {
    213.         locomotionController.EnableTeleport();
    214.         locomotionController.EnableTurn();
    215.     }
    216.  
    217.     public void PlaySound(AudioSource source)
    218.     {
    219.         StartCoroutine(playSound(source));
    220.     }
    221.  
    222.     IEnumerator playSound(AudioSource source)
    223.     {
    224.         if (alreadyPlaying == false)
    225.         {
    226.             alreadyPlaying = true;
    227.             source.Play();
    228.             yield return new WaitForSeconds(source.clip.length);
    229.             alreadyPlaying = false;
    230.         }
    231.     }
    232. }
    233.  
     

    Attached Files: