Hello everyone,
First of all, I do not know if this question’s place is here or in the Scripting section, but it seems to belong to both. (kind of)
Context: I am working with the Oculus Integration and their Hand Tracking features on a Quest 2 for a standalone Android solution. I have an OVRCameraRig prefab and ROVRHandPrefab and LOVRHandPrefab as hands (both have the same logic). I added a CapsuleCollider on the hands so I can know when I touch something using the IsTrigger boolean and a tag on the hands.
I use a script that allows the mapping and saving of hand gestures into a List.
I have an overriden version of OVRGrabber so I can use the hand tracking capabilities and grab things.
The script that maps the gestures (GestureDetector.cs) is also used to recognize them. I did not make this script, credits to TottalyNotDev and Valem.
It updates variables accordingly in a script called GestureHandler.cs.
Using:
Unity 2019.4.28f1
Oculus Integration 33.0.0
Issue: Everytime I make a gesture that is recognized, the CapsuleCollider on the associated hand gets disabled, and is enabled back again when the gesture gets canceled or not recognized. I do not recall implementing this behaviour and could not find any evidence of it in my code. Am I missing something ? Or maybe it is OVR’s hand tracking’s normal behaviour ?
I have a workaround where I put the colliders on other objects that follow the movements of the hands, but it is not ideal.
Right now my goal is to detect which component disables the CapsuleColliders.
You can find the incriminated code below as well as screenshots of one of my hand prefab.
I am really sorry if the code is not necessarily the clearest.
Wishing everyone a nice day, cheers !
The overriden version of OVRGrabber:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OculusSampleFramework;
using OVRTouchSample;
public class HandTrackingGrabber : OVRGrabber
{
public GestureHandler gestureHandler;
private HandTrackingGrabber otherHand;
private PullGrabbable pullGrabbable = null;
private bool? twoHandsObject = null;
private int appliedGrabs;
private bool isSaisieDuo;
private bool isSaisieMono;
private bool isSaisieNull;
protected override void Start()
{
otherHand = GameObject.FindWithTag((gameObject.CompareTag("LHand") ? "RHand" : "LHand")).GetComponent<HandTrackingGrabber>();
base.Start();
}
public override void Update()
{
base.Update();
CheckSaisie();
}
private void OnTriggerEnter(Collider otherCollider) {
pullGrabbable = otherCollider.GetComponent<PullGrabbable>();
if (pullGrabbable == null) return;
twoHandsObject = pullGrabbable.twoHandedInteraction;
int refCount = 0;
m_grabCandidates.TryGetValue(pullGrabbable, out refCount);
m_grabCandidates[pullGrabbable] = refCount + 1;
}
private void OnTriggerExit(Collider other) {
if (other.GetComponent<PullGrabbable>() == pullGrabbable && pullGrabbable != null) {
int refCount = 0;
bool found = m_grabCandidates.TryGetValue(pullGrabbable, out refCount);
if (!found)
{
return;
}
if (refCount > 1)
{
m_grabCandidates[pullGrabbable] = refCount - 1;
}
else
{
m_grabCandidates.Remove(pullGrabbable);
}
pullGrabbable = null;
twoHandsObject = null;
}
}
public void NoGrab() {
base.GrabEnd();
}
void CheckSaisie() {
appliedGrabs = grabbedObject != null ? (grabbedObject.gameObject.GetComponent<PullGrabbable>().twoHandedInteraction ? 2 : 1) : 0;
isSaisieDuo = (
gestureHandler.L_Saisie
||
gestureHandler.R_Saisie
);
isSaisieMono = (
gestureHandler.L_Saisie
||
gestureHandler.R_Saisie
);
isSaisieNull = (
!gestureHandler.L_Saisie
&&
!gestureHandler.R_Saisie
);
if (isSaisieMono||isSaisieDuo)
{
base.GrabBegin();
}
else if (isSaisieNull)
{
base.GrabEnd();
}
}
}
GestureDetector.cs that maps and recognizes gestures:
/* TotallyNotDevs' GestureDetector */
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using System.Collections;
using System;
[System.Serializable]
public struct Gesture
{
public string name;
public List<Vector3> fingerDatas;
public UnityEvent onRecognized;
}
public class GestureDetector : MonoBehaviour
{
// How much accurate the recognize should be
[Header("Threshold value")]
public float threshold = 0.1f;
// Add the component that refer to the skeleton hand ("OVRCustomHandPrefab_R" or "OVRCustomHandPrefab_L")
[Header("Hand Skeleton")]
public OVRSkeleton skeleton;
// List that will be populated after we save some gestures
[Header("List of Gestures")]
public List<Gesture> gestures;
// List of bones took from the OVRSkeleton
private List<OVRBone> fingerbones = null;
// Boolean for the debugMode duh!
[Header("DebugMode")]
public bool debugMode = true;
// Other boolean to check if are working correctly
private bool hasStarted = false;
private bool hasRecognize = false;
private bool done = false;
// Add an event if you want to make happen when a gesture is not identified
[Header("Not Recognized Event")]
public UnityEvent notRecognize;
void Start()
{
// When the Oculus hand had his time to initialize hand, with a simple coroutine i start a delay of
// a function to initialize the script
StartCoroutine(DelayRoutine(5f, Initialize));
}
// Coroutine used for delay some function
public IEnumerator DelayRoutine(float delay, Action actionToDo)
{
yield return new WaitForSeconds(delay);
actionToDo.Invoke();
}
public void Initialize()
{
// Check the function to know what it does
SetSkeleton();
// After initialize the skeleton set a boolean to true to confirm the initialization
hasStarted = true;
}
public void SetSkeleton()
{
// Populate the private list of fingerbones from the current hand we put in the skeleton
fingerbones = new List<OVRBone>(skeleton.Bones);
}
void Update()
{
// if in debug mode and we press Space, we will save a gesture
if (debugMode && Input.GetKeyDown(KeyCode.Space))
{
// Call the function for save the gesture
Save();
}
//if the initialization was successful
if (hasStarted.Equals(true) && fingerbones.Count >= 2)
{
// start to Recognize every gesture we make
Gesture currentGesture = Recognize();
// we will associate the recognize to a boolean to see if the Gesture
// we are going to make is one of the gesture we already saved
hasRecognize = !currentGesture.Equals(new Gesture());
// and if the gesture is recognized
if (hasRecognize)
{
// we change another boolean to avoid a loop of event
done = true;
// after that i will invoke what put in the Event if is present
currentGesture.onRecognized?.Invoke();
}
// if the gesture we done is no more recognized
else
{
// and we just activated the boolean from earlier
if (done)
{
// we set to false the boolean again, so this will not loop
done = false;
// and finally we will invoke an event when we end to make the previous gesture
notRecognize?.Invoke();
}
}
}
else {
Initialize();
}
}
void Save()
{
// We create a new Gesture struct
Gesture g = new Gesture();
// givin to it a default name
g.name = "New Gesture";
// we create also a new list of Vector 3
List<Vector3> data = new List<Vector3>();
// with a foreach we go through every bone we set at the start
// in the list of fingerbones
foreach (var bone in fingerbones)
{
// and we will going to populate the list of Vector3 we done before with all the trasform found in the fingerbones
// the fingers positions are in base at the hand Root
data.Add(skeleton.transform.InverseTransformPoint(bone.Transform.position));
}
// after the foreach we are going to associate the list of Vector3 to the one we create from the struct "g"
g.fingerDatas = data;
// and in the end we will going to add this new gesture in our list of gestures
gestures.Add(g);
}
Gesture Recognize()
{
// in the Update if we initialized correctly, we create a new Gesture
Gesture currentGesture = new Gesture();
// we set a new float of a positive infinity
float currentMin = Mathf.Infinity;
// we start a foreach loop inside our list of gesture
foreach (var gesture in gestures)
{
// initialize a new float about the distance
float sumDistance = 0;
// and a new bool to check if discart a gesture or not
bool isDiscarded = false;
// then with a for loop we check inside the list of bones we initalized at the start with "SetSkeleton"
for (int i = 0; i < fingerbones.Count; i++)
{
// then we create a Vector3 that is exactly the transform from global position to local position of the current hand
// we are making the gesture
Vector3 currentData = skeleton.transform.InverseTransformPoint(fingerbones[i].Transform.position);
// with a new float we calculate the distance between the current gesture we are making with all the gesture we saved
float distance = Vector3.Distance(currentData, gesture.fingerDatas[i]);
// if the distance is bigger respect the threshold
if (distance > threshold)
{
// then we discart it because or is another gesture or we made bad the gesture we wanted to do
isDiscarded = true;
break;
}
// if the distance is correct we will add it to the first float we have created
sumDistance += distance;
}
// if the gesture we made is not discarted and the distance of the gesture i minor then then Mathf.inifinty
if (!isDiscarded && sumDistance < currentMin)
{
// then we set current min to the distance we have
currentMin = sumDistance;
// and we associate the correct gesture we have just done to the variable we created
currentGesture = gesture;
}
}
// so in the end we can return from the function the exact gesture we want to do
return currentGesture;
}
}
/* TotallyNotDevs' GestureDetector's end */
Component that contains the variables updated and used when recognizing a gesture:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GestureHandler : MonoBehaviour
{
public bool L_Saisie = false;
public bool R_Saisie = false;
public void NoLeft() {
Debug.Log("NoLeft");
if (L_Saisie) {
L_Saisie = false;
}
}
public void NoRight() {
Debug.Log("NoRight");
if (R_Saisie) {
R_Saisie = false;
}
}
public void YesLeftPorte() {
Debug.Log("YesLeft");
if (!L_Saisie) {
L_Saisie = true;
}
}
public void YesRightPorte() {
Debug.Log("YesRight");
if (!R_Saisie) {
R_Saisie = true;
}
}
}
OVRHandPrefab pt1:
OVRHandPrefab pt2:
OVRHandPrefab pt3:


