Hello there,
I am designing an eye-tracking / head-tracking application in VR with the Varjo Aero HMD in Unity with Unity Experiment Framework (UXF; see here) (for a bit of background on what I’m trying to achieve : The saccade main sequence revised: A fast and repeatable tool for oculomotor analysis | Behavior Research Methods).
I am sampling the eye-tracking data at 200 Hz as per the HMD specifications. For the head-tracking data, I’m casting a ray from the VR camera forward to get the hit point of the head position in the VR environment AND I adjusted the fixed timestep in Unity’s Time settings to control the physics update rate (to reach ~ 200 Hz),allowing me to sample data more frequently and that works - to an extent. The head-tracking system here relies on the tracker class from UXF : unity-experiment-framework/Assets/UXF/Scripts/Etc/Tracker.cs at master · immersivecognition/unity-experiment-framework · GitHub
The problem is that the head-tracking data is the same for every ~ 4 samples (see image below), suggesting that, while I’m collecting data at 200 Hz, the raycast data is updated at ~ 50 Hz.
My questions :
Is this because I’m doing something wrong or is it because I’m reaching some sort of limits I’m not aware of ? Is there by any chance a way to circumvent this ?
using System;
using System.Collections.Generic;
using UnityEngine;
using UXF;
public class test_HeadTracker : Tracker
{
public Session session;
public override string MeasurementDescriptor => "MS";
public override IEnumerable<string> CustomHeader => new string[] {"DateTime","DateTime_ticks","Participant_ID", "Session_#", "Block_#", "Trial_#","head_x", "head_y","head_z", "pos_x", "pos_y", "pos_z", "ROI", "target_status", "Position" };
public Camera vrCamera;
public GameObject target;
// Get the current block number
private int blockNumber; // Add 1 to start from 1-based index
private string blockNumberString;
// Get the current trial number within the block
private int trialNumber; // Add 1 to start from 1-based index
private string trialNumberString;
// Get the current trial number within the block
private int sessionNumber; // Add 1 to start from 1-based index
private string sessionNumberString;
private long dateTime;
private string formattedDateTime;
private string participantID;
protected override UXFDataRow GetCurrentValues()
{
var row = new UXFDataRow();
RecordHeadData(row);
return row;
}
void RecordHeadData(UXFDataRow row)
{
Ray ray = new Ray(vrCamera.transform.position, vrCamera.transform.forward);
RaycastHit hit;
// Get the current block number
blockNumber = session.currentBlockNum; // Add 1 to start from 1-based index
blockNumberString = blockNumber.ToString();
// Get the current trial number within the block
trialNumber = session.currentTrialNum; // Add 1 to start from 1-based index
trialNumberString = trialNumber.ToString();
// Get the current trial number within the block
sessionNumber = session.number; // Add 1 to start from 1-based index
sessionNumberString = sessionNumber.ToString();
participantID = session.ppid;
int ecc_pos = session.CurrentTrial.settings.GetInt("Position");
// Perform a raycast and check if it hits an object
if (Physics.Raycast(ray, out hit))
{
// Store the hit point in the HitPoint property
Vector3 HitPoint = hit.point;
String ROI = hit.collider.name;
Vector3 cameraPosition = vrCamera.transform.position;
dateTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
formattedDateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffffff");
row.Add(("DateTime", formattedDateTime));
row.Add(("DateTime_ticks", dateTime));
row.Add(("Participant_ID", participantID));
row.Add(("Session_#", blockNumberString));
row.Add(("Block_#", blockNumberString));
row.Add(("Trial_#", trialNumberString));
row.Add(("head_x", cameraPosition.x));
row.Add(("head_y", cameraPosition.y));
row.Add(("head_z", cameraPosition.z));
row.Add(("pos_x", HitPoint.x));
row.Add(("pos_y", HitPoint.y));
row.Add(("pos_z", HitPoint.z));
row.Add(("ROI", ROI));
row.Add(("target_status", target.GetComponent<MeshRenderer>().enabled));
row.Add(("Position", ecc_pos));
}
else
{
Vector3 HitPoint = Vector3.zero;
String ROI = "";
Vector3 cameraPosition = vrCamera.transform.position;
dateTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
formattedDateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffffff");
row.Add(("DateTime", formattedDateTime));
row.Add(("DateTime_ticks", dateTime));
row.Add(("Participant_ID", participantID));
row.Add(("Session_#", blockNumberString));
row.Add(("Block_#", blockNumberString));
row.Add(("Trial_#", trialNumberString));
row.Add(("head_x", cameraPosition.x));
row.Add(("head_y", cameraPosition.y));
row.Add(("head_z", cameraPosition.z));
row.Add(("pos_x", HitPoint.x));
row.Add(("pos_y", HitPoint.y));
row.Add(("pos_z", HitPoint.z));
row.Add(("ROI", ROI));
row.Add(("target_status", target.GetComponent<MeshRenderer>().enabled));
row.Add(("Position", ecc_pos));
}
}
}