Skip to content

Instantly share code, notes, and snippets.

@DanMillerDev
Created March 14, 2025 21:28
Show Gist options
  • Save DanMillerDev/0cea91d1d0f6875f742abd95cfbd57c7 to your computer and use it in GitHub Desktop.
Save DanMillerDev/0cea91d1d0f6875f742abd95cfbd57c7 to your computer and use it in GitHub Desktop.
A script to set an objects position and rotation to a specific XR Joint based on the ID, handles usage for game object and physics rigidbodies
using System;
using UnityEngine;
using UnityEngine.XR.Hands;
using UnityEngine.XR.Management;
public class HandJointFollow : MonoBehaviour
{
[SerializeField]
XRHandJointID m_JointToTrack;
public enum Handedness { Left, Right }
public enum MovementType { SetPositionAndRotation, MovePosition }
[SerializeField]
Handedness m_Handedness;
[SerializeField]
MovementType m_MovementType;
[SerializeField]
bool m_DisableGameObjectOnTrackingLost = false;
XRHandJoint m_TrackedJoint;
XRHandSubsystem m_XRHandSubsystem;
bool m_PoseSet = false;
void Start()
{
GetHandsSubsystem();
}
void Update()
{
if (!CheckHandSubsystem())
return;
var updateSuccessFlags = m_XRHandSubsystem.TryUpdateHands(XRHandSubsystem.UpdateType.Dynamic);
var rootPose = m_Handedness == Handedness.Right ? XRHandSubsystem.UpdateSuccessFlags.RightHandRootPose : XRHandSubsystem.UpdateSuccessFlags.LeftHandRootPose;
if ((updateSuccessFlags & rootPose) != 0)
{
m_TrackedJoint = m_Handedness == Handedness.Right ? m_XRHandSubsystem.rightHand.GetJoint(m_JointToTrack) : m_XRHandSubsystem.leftHand.GetJoint(m_JointToTrack);
if(m_TrackedJoint.TryGetPose(out Pose pose))
{
if (m_MovementType == MovementType.MovePosition)
{
// object needs to have a ridigbody
if (transform.TryGetComponent<Rigidbody>(out var rb))
{
rb.MovePosition(pose.position);
rb.MoveRotation(pose.rotation);
m_PoseSet = true;
}
else
{
var childenRB = transform.GetComponentInChildren<Rigidbody>();
if (childenRB != null)
{
childenRB.MovePosition(pose.position);
childenRB.MoveRotation(pose.rotation);
m_PoseSet = true;
}
}
}
// for objects without RB, will be used as fallback if no RB is found
if (!m_PoseSet)
{
transform.SetPositionAndRotation(pose.position, pose.rotation);
}
}
else if(m_DisableGameObjectOnTrackingLost)
{
gameObject.SetActive(false);
}
}
}
void GetHandsSubsystem()
{
var xrGeneralSettings = XRGeneralSettings.Instance;
if (xrGeneralSettings == null)
{
Debug.LogError("XR general settings not set");
return;
}
var manager = xrGeneralSettings.Manager;
if (manager != null)
{
var loader = manager.activeLoader;
if (loader != null)
{
m_XRHandSubsystem = loader.GetLoadedSubsystem<XRHandSubsystem>();
if (!CheckHandSubsystem())
return;
m_XRHandSubsystem.Start();
}
}
}
bool CheckHandSubsystem()
{
if (m_XRHandSubsystem == null)
{
#if UNITY_EDITOR
if (m_DisableGameObjectOnTrackingLost)
{
gameObject.SetActive(false);
}
#else
Debug.LogError("Could not find Hand Subsystem");
#endif
return false;
}
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment