Creating Custom Stimulus Behaviour - kirtonBCIlab/bci-essentials-unity GitHub Wiki

While the provided classes can be used in a number of simple cases, BCI-Essentials Unity is set up to let you create your own stimulus behaviour by extending the provided functionality. There are two main classes to target which are listed below along with the virtual methods which should be targeted. Others exist but may result in unexpected behaviour if overridden.

In most cases, the primary targets are:

  • BCIControllerBehavior:RunStimulusRoutine/UpdateStimulus - define stimulus routine
  • SPO events or methods - define stimulus display

Controller Behaviour - BCIControllerBehavior

Specifies stimulus routines, training, and marker communication to be "driven" by the BCIController.

  • RunStimulusRoutine
    Defines a discrete yielding method to run a single stimulus pass. Not relevant to continual stimulus paradigms.
// P300ControllerBehavior.cs

protected override IEnumerator RunStimulusRoutine()
{
    ...
    RunSingleFlashRoutine()
    ...
}

private IEnumerator RunSingleFlashRoutine()
{
    int totalFlashes = numFlashesPerObjectPerSelection * _selectableSPOs.Count;
    int[] stimOrder = ArrayUtilities.GenerateRNRA_FisherYates(totalFlashes, 0, _selectableSPOs.Count - 1);

    foreach (int stimIndex in stimOrder)
    {
        yield return RunSingleFlash(stimIndex);
    }
}

protected IEnumerator RunSingleFlash(int activeIndex)
=> RunSingleFlash(activeIndex, _selectableSPOs);

protected IEnumerator RunSingleFlash
(
    int activeIndex, List<SPO> stimulusObjects
)
{
    SPO flashingObject = stimulusObjects[activeIndex];
    flashingObject.StartStimulus();
    SendSingleFlashMarker(activeIndex, stimulusObjects.Count);
    yield return new WaitForSecondsRealtime(onTime);

    flashingObject.StopStimulus();
    yield return new WaitForSecondsRealtime(offTime);
}
  • SetUpForStimulusRun
    Configure script members and references in preparation for a stimulus run. Called before RunStimulusRoutine when a run is triggered by StartStimulusRun.

  • CleanUpAfterStimulusRun
    Reset script member and reference configuration after a stimulus run has completed. Called after RunStimulusRoutine when a run is triggered by StartStimulusRun or interrupted by StopStimulusRun.

  • UpdateObjectListConfiguration
    Configure script members based on a newly populated list of selectable objects.

// MIControllerBehavior.cs
protected override void UpdateObjectListConfiguration()
{
    if (SPOCount > 2)
    {
        Debug.LogWarning("Warning: Selecting between more than 2 objects!");
    }
}
  • MakeSelection, MakeSelectionAtEndOfRun
    Select a class or object in response to a prediction from BCI-Essentials Python. Defaults to calling Select on the relevant object, but this behaviour isn't relevant to certain paradigms such as Motor Imagery.

  • Run...TrainingRoutine
    Define yielding training routines by type. The base implementation of automated training should be sufficient in most cases, but other types of training need to be more specific to the use case. Used by StartTraining if the specified type has been implemented.

    • Automated
    • User
    • Iterative
    • Single
  • UpdateClassifier
    Tell BCI-Essentials Python to update the classifier. Unimplemented by default as this behaviour is not relevant to all paradigms.

  • WaitForStimulusToComplete
    Used in the provided automated training implementation to yield until a single round of training stimulus is complete, whether discrete or for a set period of time, as is the case for continual stimulus paradigms.

Specific Paradigm Behaviours

It is also possible to write a controller behaviour based on one of the built-in paradigms, such as P300ControllerBehavior or SSVEPControllerBehavior. The same methods can be overloaded in the same way to customize the implementation of a stimulus run without having to re-implement the rest of a behaviour class.

ContinualStimulusControllerBehavior

Any paradigm using a looping stimulus routine which will continue running until it is stopped, such as SSVEP or Motor Imagery, inherits from ContinualStimulusControllerBehavior. This class provides a skeleton for training, stimulus, and the relevant markers within this frame.

  • SendTrainingMarker / SendClassificationMarker
    Send a single marker denoting the start of a training or classification epoch.
// SSVEPControllerBehavior.cs
protected override void SendClassificationMarker()
=> MarkerWriter.PushSSVEPClassificationMarker(
    SPOCount, epochLength, realFlashingFrequencies
);
  • UpdateStimulus
    Update the presentation state of stimulus objects or other prompt, used in frequency stimulus paradigms to update which objects should be 'on' or 'off' based on their assigned frequencies. Called every frame stimulus is running. Can be used as a contextual stand-in for Update.

Stimulus Presenting Objects - SPO

Flashes, moves, rotates, changed colour, or otherwise displays a change when prompted by a controller behaviour. The example behaviour is to flash 'on' or 'off' between two colours. Arbitrary behaviour can be implemented by extending the base SPO class, or by using the provided Unity events they invoke. Code-based behaviour is implemented by overriding virtual enumerator methods:

  • RunSelectedRoutine
    Provide feedback for the selection of this object

  • RunStartStimulusRoutine
    Turn stimulus 'on'. A single, instant toggle. The overarching stimulus routine is handled by the controller behaviour.

  • RunStopStimulusRoutine
    Turn stimulus 'off'.

  • RunTargetedRoutine
    Display discrete or continuous prompt indicating this object as the one being targeted for training.

  • RunUntargetedRoutine
    Reset display as altered by OnTrainTarget

Procedural SPO Generation - SPOFactory

Programmatically creates a set of stimulus presenting objects for use by a controller behaviour. A simple grid factory is provided and used in samples, but the base class provides a skeleton to implement whatever creation, placement, or initialization of SPO prefabs is desired.

  • InstantiateConfiguredObjects
    Create and configure stimulus objects using assigned prefab.

  • InstantiateObject
    Internal method: creates and returns an instance of the SPO prefab assigned in the inspector
    (tracks created objects for object population and cleanup)

An SPO Factory is a ScriptableObject which are created, edited, and used as a sort of configuration file. Don't forget to add the following attribute yo your custom factory class to let you create an instance in the project view context menu.

[CreateAssetMenu(menuName = "SPO Setup/My SPO Factory", fileName = "My SPO Factory")]
⚠️ **GitHub.com Fallback** ⚠️