Skip to content

Commit c4d464f

Browse files
committed
New TrialManager structure for flexible experiment creation
1 parent b138b2e commit c4d464f

4 files changed

Lines changed: 171 additions & 39 deletions

File tree

Assets/Scripts/ScriptableObjects/SessionSettings.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ public enum FeedbackType
7171
public float minDotLifetime;
7272
public float maxDotLifetime;
7373
public bool buddyDotsEnabled;
74+
public float outerStimulusStart;
75+
public float innerStimulusStart;
76+
public float attentionCueStart;
77+
78+
public float inputStart;
79+
public float inputDuration;
80+
public float fixationBreakCheckStart;
81+
public float fixationBreakCheckDuration;
7482

7583
// IMPORTANT: Any changes made in this function should be cross-checked with both the corresponding JSON
7684
// and the UXF data-points collection
@@ -94,7 +102,9 @@ public void LoadFromUxfJson()
94102
innerStimulusNoisePercentage = Convert.ToSingle(sessionSettingsDict["InnerStimulusNoisePercentage"]);
95103
innerStimulusRadius = Convert.ToSingle(sessionSettingsDict["InnerStimulusRadiusDegrees"]);
96104
innerStimulusSpawnRadius = Convert.ToSingle(sessionSettingsDict["InnerStimulusSpawnRadius"]);
105+
outerStimulusStart = Convert.ToSingle(sessionSettingsDict["OuterStimulusStartMs"]);
97106
outerStimulusDuration = Convert.ToSingle(sessionSettingsDict["OuterStimulusDurationMs"]);
107+
innerStimulusStart = Convert.ToSingle(sessionSettingsDict["InnerStimulusStartMs"]);
98108
innerStimulusDuration = Convert.ToSingle(sessionSettingsDict["InnerStimulusDurationMs"]);
99109
stimulusDepth = Convert.ToSingle(sessionSettingsDict["StimulusDepthMeters"]);
100110
interTrialDelay = Convert.ToSingle(sessionSettingsDict["InterTrialDelaySeconds"]);
@@ -112,7 +122,8 @@ public void LoadFromUxfJson()
112122
coarseAdjustEnabled = Convert.ToBoolean(sessionSettingsDict["CoarseAdjustment"]);
113123
choosableAngles = ParseFloatList((List<object>) sessionSettingsDict["ChoosableAngles"]);
114124

115-
attentionCueDuration = Convert.ToSingle(sessionSettingsDict["AttentionCueDuration"]);
125+
attentionCueStart = Convert.ToSingle(sessionSettingsDict["AttentionCueStartMs"]);
126+
attentionCueDuration = Convert.ToSingle(sessionSettingsDict["AttentionCueDurationMs"]);
116127
attentionCueDepth = Convert.ToSingle(sessionSettingsDict["AttentionCueDepth"]);
117128
attentionCueDistance = Convert.ToSingle(sessionSettingsDict["AttentionCueLengthDegrees"]);
118129
pulseFrequency = Convert.ToSingle(sessionSettingsDict["PulseFrequency"]);
@@ -126,6 +137,11 @@ public void LoadFromUxfJson()
126137

127138
fixationErrorTolerance = Convert.ToSingle(sessionSettingsDict["FixationErrorToleranceRadiusDegrees"]);
128139
buddyDotsEnabled = Convert.ToBoolean(sessionSettingsDict["EnableBuddyDots"]);
140+
141+
inputStart = Convert.ToSingle(sessionSettingsDict["InputStartMs"]);
142+
inputDuration = Convert.ToSingle(sessionSettingsDict["InputDurationMs"]);
143+
fixationBreakCheckStart = Convert.ToSingle(sessionSettingsDict["FixationBreakCheckStartMs"]);
144+
fixationBreakCheckDuration = Convert.ToSingle(sessionSettingsDict["FixationBreakCheckDurationMs"]);
129145
}
130146

131147
private static List<float> ParseFloatList(IEnumerable<object> list)

Assets/Scripts/Trial Manager/TrialManager.cs

Lines changed: 135 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections;
3+
using System.Data;
34
using DotStimulus;
45
using EyeTracker;
56
using ScriptableObjects;
@@ -126,7 +127,7 @@ public void EndTrial(Trial trial)
126127
Session.instance.CurrentBlock.CreateTrial();
127128
_trialCount++;
128129
}
129-
130+
130131
StartCoroutine(FeedBackRoutine());
131132
}
132133

@@ -269,63 +270,167 @@ private IEnumerator TrialRoutine(Trial trial)
269270
yield return WaitForFixation(sessionSettings.fixationTime,
270271
Mathf.Tan(sessionSettings.fixationErrorTolerance * Mathf.PI / 180 * sessionSettings.stimulusDepth));
271272

273+
var trialDuration = GetTrialDuration();
274+
275+
var outerStimulusRoutine = StartCoroutine(OuterStimulusRoutine());
276+
var innerStimulusRoutine = StartCoroutine(InnerStimulusRoutine());
277+
var inputRoutine = StartCoroutine(InputRoutine());
278+
var fixationBreakCheckRoutine = StartCoroutine(FixationBreakCheckRoutine());
279+
var attentionCueRoutine = AttentionCueRoutine();
280+
272281
if (sessionSettings.sessionType == SessionSettings.SessionType.Training)
282+
StartCoroutine(attentionCueRoutine);
283+
284+
var elapsedTime = 0.0f;
285+
while (!_isFixationBroken && elapsedTime < trialDuration / 1000)
273286
{
274-
attentionCue.SetActive(true);
275-
yield return new WaitForSeconds(sessionSettings.attentionCueDuration);
276-
attentionCue.SetActive(false);
287+
elapsedTime += Time.deltaTime;
288+
yield return null;
277289
}
290+
291+
StopCoroutine(outerStimulusRoutine);
292+
StopCoroutine(innerStimulusRoutine);
293+
StopCoroutine(inputRoutine);
294+
StopCoroutine(fixationBreakCheckRoutine);
295+
StopCoroutine(attentionCueRoutine);
296+
297+
outerStimulus.SetActive(false);
298+
innerStimulus.SetActive(false);
299+
stimulusSpacer.SetActive(false);
278300

279-
outerStimulus.SetActive(true);
280-
innerStimulus.SetActive(true);
281-
stimulusSpacer.SetActive(true);
282-
yield return CheckFixationBreakWithDelay(sessionSettings.innerStimulusDuration / 1000,
283-
Mathf.Tan(sessionSettings.fixationErrorTolerance * Mathf.PI / 180 * sessionSettings.stimulusDepth));
284-
285-
// End routine if fixation was broken
301+
// Start trial over if fixation was broken
286302
if (_isFixationBroken)
303+
{
304+
BeginTrial(trial);
287305
yield break;
306+
}
307+
308+
trial.End();
309+
}
288310

289-
laserManager.ActivateLaser();
290-
innerStimulus.SetActive(false);
311+
private float GetTrialDuration()
312+
{
313+
var endTimes = new[]
314+
{
315+
sessionSettings.outerStimulusStart + sessionSettings.outerStimulusDuration,
316+
sessionSettings.innerStimulusStart + sessionSettings.innerStimulusDuration,
317+
sessionSettings.fixationBreakCheckStart + sessionSettings.fixationBreakCheckDuration,
318+
sessionSettings.inputStart + sessionSettings.inputDuration
319+
};
291320

292-
// Inner stimulus renders one frame longer after it's disabled, so wait for next frame.
293-
yield return null;
294-
stimulusSpacer.SetActive(false);
295-
fixationDot.SetActive(false);
321+
return Mathf.Max(endTimes);
322+
}
323+
324+
private IEnumerator InputRoutine()
325+
{
326+
var elapsedTime = 0.0f;
327+
while (elapsedTime < sessionSettings.inputStart / 1000)
328+
{
329+
elapsedTime += Time.deltaTime;
330+
yield return null;
331+
}
296332
_waitingForInput = true;
333+
laserManager.ActivateLaser();
297334

298-
// Delta time is subtracted here to account for the extra frame we wait for a few lines above
299-
yield return new WaitForSeconds((sessionSettings.outerStimulusDuration - sessionSettings.innerStimulusDuration) / 1000 - Time.deltaTime);
300-
335+
elapsedTime = 0.0f;
336+
while (elapsedTime < sessionSettings.inputDuration / 1000)
337+
{
338+
elapsedTime += Time.deltaTime;
339+
yield return null;
340+
}
341+
laserManager.DeactivateBothLasers();
301342
_waitingForInput = false;
343+
}
344+
345+
private IEnumerator OuterStimulusRoutine()
346+
{
347+
var elapsedTime = 0.0f;
348+
while (elapsedTime < sessionSettings.outerStimulusStart / 1000)
349+
{
350+
elapsedTime += Time.deltaTime;
351+
yield return null;
352+
}
353+
354+
elapsedTime = 0.0f;
355+
outerStimulus.SetActive(true);
356+
while (elapsedTime < sessionSettings.outerStimulusDuration / 1000)
357+
{
358+
elapsedTime += Time.deltaTime;
359+
yield return null;
360+
}
302361
outerStimulus.SetActive(false);
303-
trial.End();
304362
}
363+
364+
private IEnumerator InnerStimulusRoutine()
365+
{
366+
var elapsedTime = 0.0f;
367+
while (elapsedTime < sessionSettings.innerStimulusStart / 1000)
368+
{
369+
elapsedTime += Time.deltaTime;
370+
yield return null;
371+
}
372+
innerStimulus.SetActive(true);
373+
stimulusSpacer.SetActive(true);
305374

306-
private IEnumerator CheckFixationBreakWithDelay(float fixationTime, float maxFixationError)
375+
elapsedTime = 0.0f;
376+
while (elapsedTime < sessionSettings.innerStimulusDuration / 1000)
377+
{
378+
elapsedTime += Time.deltaTime;
379+
yield return null;
380+
}
381+
innerStimulus.SetActive(false);
382+
yield return null;
383+
stimulusSpacer.SetActive(false);
384+
}
385+
386+
private IEnumerator AttentionCueRoutine()
387+
{
388+
var elapsedTime = 0.0f;
389+
while (elapsedTime < sessionSettings.attentionCueStart / 1000)
390+
{
391+
elapsedTime += Time.deltaTime;
392+
yield return null;
393+
}
394+
attentionCue.SetActive(true);
395+
396+
elapsedTime = 0.0f;
397+
while (elapsedTime < sessionSettings.attentionCueDuration / 1000)
398+
{
399+
elapsedTime += Time.deltaTime;
400+
yield return null;
401+
}
402+
attentionCue.SetActive(false);
403+
}
404+
405+
private IEnumerator FixationBreakCheckRoutine()
307406
{
308-
var time = 0.0f;
309-
while (time < fixationTime)
407+
var elapsedTime = 0.0f;
408+
while (elapsedTime < sessionSettings.attentionCueStart / 1000)
409+
{
410+
elapsedTime += Time.deltaTime;
411+
yield return null;
412+
}
413+
414+
elapsedTime = 0.0f;
415+
while (elapsedTime < sessionSettings.attentionCueDuration / 1000)
310416
{
311417
if (Physics.Raycast(cameraTransform.position,
312418
cameraTransform.TransformDirection(_eyeTracker.GetLocalGazeDirection()), out var hit))
313419
{
314420
Debug.DrawRay(cameraTransform.position,
315421
hit.distance * cameraTransform.TransformDirection(_eyeTracker.GetLocalGazeDirection()),
316422
Color.yellow);
317-
if ((hit.point - fixationDot.transform.position).magnitude > maxFixationError)
423+
var fixationError = Mathf.Tan(sessionSettings.fixationErrorTolerance * Mathf.PI / 180 *
424+
sessionSettings.stimulusDepth);
425+
if ((hit.point - fixationDot.transform.position).magnitude > fixationError)
318426
{
319-
StopCoroutine(_trialRoutine);
320427
innerStimulus.SetActive(false);
321428
outerStimulus.SetActive(false);
322429
_isFixationBroken = true;
323-
324-
BeginTrial(Session.instance.CurrentTrial);
325-
break;
430+
yield break;
326431
}
327432
}
328-
time += Time.deltaTime;
433+
elapsedTime += Time.deltaTime;
329434
yield return null;
330435
}
331436
}

Assets/StreamingAssets/TEMPLATE.json

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,33 @@
1010
"StimulusDotSizeArcMinutes": 14,
1111
"MinDotLifetimeSeconds": 0.2,
1212
"MaxDotLifetimeSeconds": 0.5,
13+
14+
"OuterStimulusStartMs": 1000,
1315
"OuterStimulusDurationMs": 5000,
1416
"OuterStimulusRadiusDegrees": 55,
1517
"OuterStimulusNoisePercentage": 100,
18+
19+
"InnerStimulusStartMs": 1000,
1620
"InnerStimulusDurationMs": 500,
1721
"InnerStimulusRadiusDegrees": 5,
18-
"InnerStimulusNoisePercentage": 100,
22+
"InnerStimulusNoisePercentage": 50,
1923
"InnerStimulusSpawnRadius": 30,
2024

25+
"InputStartMs": 1500
26+
"InputDurationMs": 4500
27+
"FixationBreakCheckStartMs": 0
28+
"FixationBreakCheckDurationMs": 1500
29+
30+
"AttentionCueStartMs": 0
31+
"AttentionCueDurationMs": 1000,
32+
"AttentionCueDepth": 0.57,
33+
"AttentionCueLengthDegrees": 80,
34+
"PulseFrequency": 12,
35+
"SampleRate": 44100
36+
2137
"EnableBuddyDots": true,
2238

23-
"StimulusSpacingMeters": 0.001,
39+
"StimulusSpacingMeters": 0.05,
2440

2541
"StimulusDepthMeters": 2.0,
2642

@@ -37,11 +53,5 @@
3753
"CoherenceStaircase": [0, 40, 80, 120, 160, 200, 240, 280, 320, 338],
3854
"StaircaseIncreaseThreshold": 3,
3955
"StaircaseDecreaseThreshold": 1,
40-
41-
"AttentionCueDuration": 1,
42-
"AttentionCueDepth": 0.57,
43-
"AttentionCueLengthDegrees": 80,
44-
"PulseFrequency": 12,
45-
"SampleRate": 44100
4656
}
4757
}

ProjectSettings/ProjectSettings.asset

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ PlayerSettings:
127127
bundleVersion: 0.1
128128
preloadedAssets:
129129
- {fileID: 6405742922248523384, guid: efde690a735af1f4a89b7bfd2c31a41a, type: 2}
130+
- {fileID: 0}
130131
metroInputSource: 0
131132
wsaTransparentSwapchain: 0
132133
m_HolographicPauseOnTrackingLoss: 1

0 commit comments

Comments
 (0)