Recording Sessions¶
The core recording API lives behind XRXPManager.Recorder.
Session lifecycle¶
Typical flow:
- initialize XRXP in the scene
- start a session (creates a User, Environment, and Session)
- add events and tracked data
- stop the session
- let pending traces flush before quit
Minimal example¶
using XRXP;
using System.Collections.Generic;
public class ExampleRecorder : UnityEngine.MonoBehaviour
{
private string _sessionId;
private void Start()
{
// StartSession(comments, userId, environmentProperties, environmentId)
_sessionId = XRXPManager.Recorder.StartSession(
comments: "Participant 001 - observer role",
userId: "participant-001",
environmentProperties: new Dictionary<string, string> {
{ "application_version", "1.2.0" },
{ "difficulty", "medium" }
}
);
XRXPManager.Recorder.AddLogEvent("User", "clicked", "StartButton");
XRXPManager.Recorder.AddQuestion("Comfort", "High");
}
private void OnApplicationQuit()
{
XRXPManager.Recorder.StopSession();
XRXPManager.Recorder.EndTracing();
}
}
Available recording methods¶
StartSession / StopSession¶
StartSession creates the User, Environment, and Session traces and returns a session ID.
// Start a new session
string sessionId = XRXPManager.Recorder.StartSession(
comments: "Role: surgeon", // optional, describes the user's role in this session
userId: "user-042", // optional, auto-generated UUID if empty
environmentProperties: new Dictionary<string, string> {
{ "scene", "OperatingRoom" },
{ "difficulty", "hard" }
},
environmentId: null // optional, reuse an existing environment
);
// Stop the current session (records end time)
XRXPManager.Recorder.StopSession();
Sessions can be nested. Starting a new session while one is already active creates a sub-session whose parent is the current session. Stopping always closes the most recent one. This is useful when a session represents an overall participation and sub-sessions represent individual exercises.
JoinSession¶
Join an existing multi-user session created by another participant.
XRXPManager.Recorder.JoinSession(
sessionId: "session-abc-123",
comments: "Observer role",
userId: "observer-001",
environmentProperties: new Dictionary<string, string> {
{ "device", "Quest3" }
}
);
Log events¶
Record discrete actions using the Actor - Verb - Object pattern, with optional properties.
// Simple event
XRXPManager.Recorder.AddLogEvent("User", "grabbed", "Cube_01");
// Event with properties
XRXPManager.Recorder.AddLogEvent(
"Bob", "eats", "apple",
new Dictionary<string, string> {
{ "color", "red" },
{ "hand", "right" }
}
);
Questions¶
Record survey-style answers with optional properties. Questions use a developer-defined questionId to group answers to the same question across sessions and users.
// Basic question (auto-generates questionId)
XRXPManager.Recorder.AddQuestion("How was your experience?", "Great!");
// Question with explicit questionId for grouping
XRXPManager.Recorder.AddQuestion(
"experience-rating", // questionId - groups answers to same question
"How was your experience?",
"8"
);
// Question with properties
XRXPManager.Recorder.AddQuestion(
"ssq-nausea",
"How nauseous do you feel?",
"3",
new Dictionary<string, string> {
{ "scale", "1-7" },
{ "timepoint", "post-exposure" }
}
);
// Question linked to a specific user
XRXPManager.Recorder.AddQuestion(
"comfort-level",
"Rate your comfort level",
"7",
"participant-042"
);
// Standalone question (no session)
XRXPManager.Recorder.AddStandaloneQuestion(
"demographics-age",
"What is your age group?",
"25-34",
"user-042"
);
QuestionId Best Practices¶
The questionId is a developer-defined identifier that enables cross-session and cross-user analysis:
// Use consistent IDs for the same question across sessions
XRXPManager.Recorder.AddQuestion("ssq-nausea", "How nauseous do you feel?", "2");
XRXPManager.Recorder.AddQuestion("ssq-discomfort", "How uncomfortable do you feel?", "3");
XRXPManager.Recorder.AddQuestion("experience-rating", "Rate your experience", "8");
// Avoid: different IDs for the same question
// XRXPManager.Recorder.AddQuestion("nausea-q1", ...); // Don't do this for the same question
Session-linked vs Standalone¶
- Session-linked: Use
AddQuestion()during active sessions. The question is automatically linked to the current session. - Standalone: Use
AddStandaloneQuestion()for questionnaires outside of session recording (e.g., pre-screening surveys).
// During session - automatically linked
XRXPManager.Recorder.StartSession("Experiment run");
XRXPManager.Recorder.AddQuestion("experience-rating", "Rate your experience", "8"); // session-linked
XRXPManager.Recorder.StopSession();
// Outside session - standalone
XRXPManager.Recorder.AddStandaloneQuestion(
"screening-vision",
"Do you have normal vision?",
"yes",
"user-042"
);
Media events¶
Record files produced during the experiment (screenshots, audio recordings, documents). Media events are linked to the current session and user.
// From a file path
XRXPManager.Recorder.AddMediaEvent(
filePath: "/storage/screenshot_001.png",
mimeType: "image/png",
name: "Task1_Screenshot",
duration: 0 // in ms, relevant for audio/video
);
// From a byte array (auto-saved and uploaded)
byte[] imageBytes = File.ReadAllBytes("capture.png");
XRXPManager.Recorder.AddMediaEvent(
bytes: imageBytes,
mimeType: "image/png",
name: "Task1_Capture",
duration: 0 // optional, in ms for audio/video
);
Internal events¶
Record continuous system data (positions, sensor values). See Tracking data for details.
using XRXP.Recorder.Models;
WorldPosition headPos = new WorldPosition(
Camera.main.transform.position,
Camera.main.transform.rotation
);
XRXPManager.Recorder.AddInternalEvent(
SystemType.WorldPosition, "BodyTracking", "Head", headPos
);
User and Environment properties¶
Add metadata to the current user or environment after session start.
XRXPManager.Recorder.AddUserProperties(new Dictionary<string, string> {
{ "age", "25" },
{ "experience", "beginner" }
});
XRXPManager.Recorder.AddEnvironmentProperties(new Dictionary<string, string> {
{ "lighting", "dim" },
{ "temperature", "22C" }
});
Utility¶
// Check how many traces are still pending
int pending = XRXPManager.Recorder.TransfersState();
// Gracefully flush all remaining traces then stop services
XRXPManager.Recorder.EndTracing();
// Query state
bool active = XRXPManager.Recorder.IsRecording();
string sid = XRXPManager.Recorder.GetSessionId();
string uid = XRXPManager.Recorder.GetUserId();
string eid = XRXPManager.Recorder.GetEnvironmentId();
Best practices¶
- start the session before important interactions begin
- keep event naming consistent across the project
- record discrete actions as log events, not per-frame state
- use
TransfersState()before quitting to ensure no data is lost - always stop sessions cleanly in test and production builds