BioMotionLab / TUX

A framework for experiments in Unity and VR
https://biomotionlab.github.io/TUX/
Other
29 stars 4 forks source link

Feature Request: Better Way to Create/Access Experiment Design Variables and Runner Variable in the Scripts #49

Closed A-Ivan closed 2 years ago

A-Ivan commented 2 years ago

Currently the way to access the variables in the trial script is to something like:

variable_type variableName = (variable_type) Data["variable_name"];

For example: string colorString = (string)Data["Color"];

Having to manually cast (writing the variable type) and to type in the variable's name in quotes can lead to syntax problems and typos. Because the variable names and their type has already been set using the Design File, it would be great of this information could simply be pulled from the Design file.

This could be done automatically, for instance by having a button at the end of the Design File which would edit the Trial, Block, Runner, and Experiment scripts adding the variables setup in the Design file as private class variables (the user could then cut and paste these variables to specific functions in these files). A warning could appear stating that this would overwrite these files so any edits to them would be lost, asking the user if they would like to continue or cancel.

If this step is part of the setup process, first using the script Helper Tool to setup the Experiment files/scripts, then creating the variables in the Design file, then pressing this button to automatically add the files to the previously generated script files (trial, block, etc.), overwriting these files would not be an issue.

Using the capsule size matching example found in the tutorial with an additional dependent variable.

image

Below is an example of what the Trial file could look like after pressing the button to automatically add the variables to the scripts, I removed some of the comments that usually comes with the file to better clarify the changes.

You can see that the independent variables created in the Design File and the Runner variable (since we already know its class name when its script was created) have automatically been defined in the class. The Independent variable are given initial values in the PreMethod (the user could cut and paste these if they would like to other functions, or remove them if they have no use for them) and the Dependent variable output has been drafted in the PostMethod.

The runner file has also already been setup in the constructor, since we already know its script name.

The participant variables could be added to this if needed, but I have decided to keep them out of the example because they are normally not important for running the experiment itself.

using System.Collections;
using System.Data;
using UnityEngine;
using bmlTUX.Scripts.ExperimentParts;

public class TutorialTrial : Trial {

    TutorialRunner tutorialRunner;

     // Independent Variables
     string colorString;      
     float sizeFloat;
     float distanceFloat; 

    // Required Constructor. Good place to set up references to objects in the unity scene
    public TutorialTrial(ExperimentRunner runner, DataRow data) : base(runner, data) {
        tutorialRunner = (TutorialRunner)runner;  //cast the generic runner to your custom type.
        // GameObject myGameObject = myRunner.MyGameObject;  // get reference to gameObject stored in your custom runner

    }

    // Optional Pre-Trial code. Useful for setting unity scene for trials. Executes in one frame at the start of each trial
    protected override void PreMethod() {        

     // Independent Variables
     colorString = (string)Data["Color"];        
     sizeFloat = (float)Data["Size"];
     distanceFloat = (float)Data["Distance"];  
    }

    // Optional Pre-Trial code. Useful for waiting for the participant to
    // do something before each trial (multiple frames). Also might be useful for fixation points etc.
    protected override IEnumerator PreCoroutine() {
        yield return null; //required for coroutine
    }

    // Main Trial Execution Code.
    protected override IEnumerator RunMainCoroutine() {

        // You might want to do a while-loop to wait for participant response: 
        bool waitingForParticipantResponse = true;
        Debug.Log("Press the spacebar to end this trial.");

        while (waitingForParticipantResponse) {   // keep check each frame until waitingForParticipantResponse set to false.

            if (Input.GetKeyDown(KeyCode.Space)) { // check return key pressed
                waitingForParticipantResponse = false;  // escape from while loop
            }

            yield return null; // wait for next frame while allowing rest of program to run (without this the program will hang in an infinite loop)
        }

    }

    // Optional Post-Trial code. Useful for waiting for the participant to do something after each trial (multiple frames)
    protected override IEnumerator PostCoroutine() {
        yield return null;
    }

    // Optional Post-Trial code. useful for writing data to dependent variables and for resetting everything.
    // Executes in a single frame at the end of each trial
    protected override void PostMethod()
    {
        // Dependent Variable Output
        //Data["MatchedSize"] = someVariableValue;
        //Data["ReactionTime"] = someVariableValue;
    }
}

Another option could be being able to access these variables through the code, something like:

// Independent Variables Runner.DesignFile.GetVariables.IndependentVariables.Color Runner.DesignFile.GetVariables.IndependentVariables.Size Runner.DesignFile.GetVariables.IndependentVariables.Distance

I think the first option, having the variables automatically be added to the scripts and allowing the user to cut/paste and remove the variables as needed works better.

AdamBebko commented 2 years ago

Closing this. See discussion here https://github.com/BioMotionLab/TUX/discussions/54