Unity Agent
TestManager.cs (shadow manager)
Inside the Unity _config scene, the TestManager.cs component can be found in the hierarchy under the TestManager object. This serves as a shadow manager object, used to keep synchronization with the MaxMSP controller.
The TestManager component will persist from the _config scene into all subsequent _evaluation and _questionnaire scenes, ensuring that the correct interfaces and the next test item are loaded correctly, until control returns to the _config scene.

Unity Inspector View
TestManager.cs
The TestManager.cs component in the QExE Unity Agent receives configuration information from the QExE Controller at the start of each test. The inspector fields are as follows:
OSCManager is the object responsible for both transmitting and receiving OSC messages over UDP.
btn/Next and btn/StartPlayback are mandatory buttons that must be present on any imported method interface.
Current Item displays information related to the current evaluation item.
Next Item displays the same fields for the upcoming item.
The Questionnaire field shows the questionnaire name and integration mode received from the Controller. The array below allows Unity interfaces to be registered; if a matching interface name is found, it will be instantiated automatically.
The Methodology field shows the test method name received from the Controller, along with the number of parallel audio conditions — used to configure the number of buttons and sliders on the interface. The array below allows method interfaces to be registered in the same way.
SetMethodologyInterface()
- Called on every
onSceneLoad(), with the name of the received test method as an argument. - The method name is compared against the list of interfaces registered in the inspector. If a match is found, the method prefab is instantiated as a child of a designated interface container object.
- Once instantiated, references to
btn/Nextandbtn/StartPlaybackare resolved so that OSC messages can be controlled correctly.
Customization options
- To add a custom method interface, add a new empty field in the
TestManager.csinspector and drag the method prefab into it. Ensure that the QExE Controller'ssetTestMethod()function is updated to recognise the new method name, and that a corresponding.maxpatfile and paradigm calculation function are provided.
... see code ...
private void SetMethodologyInterface(string method){
// Instantiate correct test interface prefab
foreach (GameObject Interface in MethodologyInterfaces){
if (Interface.name == method){
Debug.Log("<color=bright_white><b>QExE: </b></color> Method found. " + Interface.name + ": The method interface provided by the server(JSON file) is listed in the Method Interfaces array. Importing into scene.");
_Methodology = Instantiate(Interface, TestInterface.transform.position, Quaternion.Euler(TestInterface.transform.rotation.eulerAngles.x, TestInterface.transform.rotation.eulerAngles.y, TestInterface.transform.rotation.eulerAngles.z), TestInterface.transform) as GameObject;
}
}
// Get reference to the Next and Startplayback buttons
NextButton = GameObject.Find("btn/Next").GetComponent<Button>();
StartPlaybackButton = GameObject.Find("btn/StartPlayback").GetComponent<Button>();
InterfaceNextButtonFound = true;
InterfacePlayButtonFound = true;
// Set the next button interatable false, so users do not accidently click it to start with.
NextButton.interactable = false;
// Add event listeners
NextButton.onClick.AddListener(() => OnNextButton());
StartPlaybackButton.onClick.AddListener(() => OnStartPlayback());
TestInterface.SetActive(false);
}
SetQuestionnaireInterface()
- Called on every
onSceneLoad()when the current scene is _questionnaire, with the questionnaire name as an argument. - The questionnaire name is compared against the list of interfaces registered in the inspector. If a match is found, the questionnaire prefab is instantiated as a child of a designated container object.
- Once instantiated, a reference to btn/Next is resolved so that OSC messages can be controlled correctly.
Customization options
- To add a custom questionnaire interface, add a new empty field in the TestManager.cs inspector and drag the questionnaire prefab into it. Ensure that the QExE Controller's
setQuestionnaire()function is updated to recognize the new questionnaire name, and that a corresponding.maxpatfile is provided..
... see code ...
private void SetQuestionnaireInterface(string questionnaire)
{
// Instantiate correct test interface prefab
GameObject QuestionnaireInterface = GameObject.Find("QExEQuestionnaireInterface");
foreach (GameObject Interface in QuestionnaireInterfaces)
{
if (Interface.name == questionnaire)
{
Debug.Log("<color=bright_white><b>QExE: </b></color>Questionnaire found. " + Interface.name + ": The questionnaire interface provided by the server (JSON file) is listed in the Questionnaire Interfaces array. Importing into scene.");
_Questionnaire = Instantiate(Interface, QuestionnaireInterface.transform.position, Quaternion.Euler(QuestionnaireInterface.transform.rotation.eulerAngles.x, QuestionnaireInterface.transform.rotation.eulerAngles.y, QuestionnaireInterface.transform.rotation.eulerAngles.z), QuestionnaireInterface.transform) as GameObject;
}
}
// Get reference to the Next and Startplayback buttons
NextButton = GameObject.Find("btn/Next").GetComponent<Button>();
InterfaceNextButtonFound = true;
// Add event listeners
NextButton.onClick.AddListener(() => OnNextButton());
}
TestRecorder.cs
The TestRecorder.cs component can be found in the hierarchy under the TestManager game object. Like TestManager.cs, this script persists across all scenes, streaming all relevant data to a behavior.txt file.

Inspector view of the TestRecorder.cs component. The component receives the results directory of the subject, and creates a new streamwriter to save all OSC information.
QExEPlaybackSync.cs
The PlaybackSync.cs component can be found in the Hierarchy of a scene under QExE XR Set Up > QExEAnimationAudioSync.

Inspector view of the QExEPlaybackSync.cs component. Can be used used to add any number of Unity game objects. The game objects will be deactivated until the user presses play, such that any animations that need to happen in sync with separate audio playback can be triggered simultaneously.
QExEAudioObject.cs
The QExEAudioObject.cs component can be attached to any game object inside a scene, that the user would like to track as an audio object.
Inspector view of the QExEAudioObject.cs component. Can be used used to identify any object as a QExE audio object that should be tracked with position and rotation updates when moved. The class features public methods which can then be used to send event-based triggers to the QExE Controller.
... see code ...
public void TriggerAudio(int sampleIndex, string triggerMsg)
{
var message_trigger = new OSCMessage("/stream/objectstrigger/");
message_trigger.AddValue(OSCValue.Int(sampleIndex+1));
message_trigger.AddValue(OSCValue.String("event"));
message_trigger.AddValue(OSCValue.String(triggerMsg));
if (_transmitter)
{
if (_transmitter.isActiveAndEnabled)
_transmitter.Send(message_trigger);
}
}