Last Edited: Connor Flick, 4/29/2025
Unity provides many opportunities to track user interactions, engagement, and performance in an objective and quantitative manner. This may include the amount of time it takes to complete a task, how many times a user does something, user accuracy with respect to a target, distance travelled, and more. This type of data collection benefits from the fact that it can be both automated and invisible to the user – there is no additional burden to create the data. This tutorial steps through a basic example that tracks the amount of time between function calls.
First, create a sample HelloWorld scene. Then, add a MonoBehavior script. For this example, the script will be named InteractionTimer.
In InteractionTimer.cs, we will set up a list of interactionTimes as double and another double to track the previously recorded time as prevTime. In the Start function, we set the prevTime to be the current time at application startup, Time.time.
From here, we add another method, AddTime, which finds the difference between the current time and the previous time, adds this difference to interactionTimes, then sets prevTime to be the current time.
With the data tracking in place, we need to create the methods to save this to a spreadsheet. To do this, we first create a ToCSVString method, which will generate the lines of a CSV file containing our data. To do this, we use a StringBuilder, where the first part of the string is the headers of our CSV file. From there, we can append a new line to the string, adding comma separated values for each row of data. Since the data is already stored as a list, we can use a foreach to do this iteration. Once this is complete, we can return the resulting string.
We then create another method, SaveToFile, which will generate a CSV file of the string we created. To do this, we call the ToCSVString method from SaveToFile, then get the path of the application from Application.persistentDataPath and append the name of our CSV file– results.csv, in this example – to the end of the path. We then use a StreamWriter to write to this file, which creates the CSV.
We now have all the parts to generate a CSV file of our data! However, we have nothing calling SaveToFile or AddTime yet, so there’s nothing generating our data or our spreadsheet. To solve the spreadsheet generation problem, we can call SaveToFile at the end of the program lifecycle. To do this, we can create the method OnApplicationQuit and then simply call SaveToFile from there.
Our script is almost complete! Finally, we add an OnTriggerEnter method so that we can call AddTime each time an object enters a triggerable collider that the script is attached to.
At this point, we return to the Unity Editor. Add the script to the cube building block in the demo scene. Now, each time the player’s hand collides with the cube, it will save how much time passed since this last occurred. When the program exits, a spreadsheet of these times will be saved! The full script is given at the bottom of this page.
The Meta Quest 3 is, fundamentally, an Android device, and the files on it can be accessed as such. When a dataset is saved, it’s saved to the same place where the app was originally installed.
For non-Mac users:
Connect the Quest 3 to your PC. After doing so, open the notification center on the Quest 3 and click on the USB notification to allow the PC to access the files on the headset. Accept USB debugging if prompted. Open up the file explorer on your PC and the Quest 3 should be there. Open the device, then follow the given path:
Quest 3 -> Internal -> Android -> data -> [Company Name, typically com.xyz.AppName] -> files -> results.csv
For Mac users:
Download MacDroid. Connect the Quest 3 to your Mac and select an MTP connection on MacDroid. Open the notification center on the Quest 3 and click on the USB notification to allow the Mac to access the files on the headset. Accept USB debugging if prompted. Open up the file explorer on your Mac and the Quest 3 should be there. Open the device, then follow the given path:
Storage -> Emulated -> 0 -> Android -> data -> [Company Name, typically com.xyz.AppName] -> files -> results.csv
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System;
public class InteractionTimer : MonoBehaviour
{
private List<double> interactionTimes = new List<double>();
private double prevTime;
void Start()
{
prevTime = Time.time;
}
void OnApplicationQuit()
{
SaveToFile();
}
void OnTriggerEnter(Collider other)
{
AddTime();
}
void AddTime()
{
double timeDifference = Time.time - prevTime;
interactionTimes.Add(timeDifference);
prevTime = Time.time;
}
string ToCSVString()
{
StringBuilder stringBuilder = new StringBuilder("Idx,Time");
int iter = 0;
foreach (double time in interactionTimes)
{
stringBuilder.Append('\n').Append(iter).Append(',').Append(time);
iter++;
}
return stringBuilder.ToString();
}
void SaveToFile()
{
String content = ToCSVString();
String folder = Application.persistentDataPath;
String filePath = Path.Combine(folder, "results.csv");
using (StreamWriter writer = new StreamWriter(filePath, false))
{
writer.Write(content);
}
Debug.Log($"CSV written to \"{filePath}\"");
}
}