Jump to content

Contributions:UnityBCI2000: Difference between revisions

From BCI2000 Wiki
Swiftj (talk | contribs)
Tytbutler (talk | contribs)
No edit summary
Line 3: Line 3:


==Tutorial==
==Tutorial==
For information on how to use Unity itself, see the Unity [https://docs.unity3d.com/Manual/index.html manual]. This tutorial assumes knowledge of how to use Unity. It is recommended to know how GameObjects and Components work.
For information on how to use Unity itself, see the Unity [https://docs.unity3d.com/Manual/index.html manual]. This tutorial assumes knowledge of how to use Unity.
More in-depth detail on how UnityBCI2000 works is provided in the README.md file.
More in-depth detail on how UnityBCI2000 works is provided in the README.md file, as well as HTML documentation in the docs directory.


First, download UnityBCI2000 from [https://github.com/neurotechcenter/UnityBCI2000 GitHub]. As of now, UnityBCI2000 is not a full Unity package, but it works the same. Place the files UnityBCI2000.cs and BCI2000RemoteNET.dll within the Assets folder of your Unity project.


Add the UnityBCI2000 script as a component to an empty GameObject. This will serve as the connection to BCI2000 within your project.


First, download UnityBCI2000 from [https://github.com/neurotechcenter/UnityBCI2000 GitHub]. As of now, UnityBCI2000 is not a full Unity package, but it works the same. Place the C# files from the Runtime folder, as well as BCI2000RemoteNET.dll, within the Assets directory of your Unity project.
Specify the location of the BCI2000 executable, the BCI2000 modules you want to start along with their startup arguments, and any parameter files you want to load.


Create an empty GameObject and add the script UnityBCI2000 as a Component. This will serve as the central connection to the BCI2000 Operator. As of now, it is not possible to use multiple scenes with one BCI2000 connection.
Connection to BCI2000 is done by calling UnityBCI2000's methods from other scripts. Within the script object from which you want to control BCI2000, add the UnityBCI2000 component as a member variable using Unity's GameObject.Find and GameObject.GetComponent methods.
For example, if you have UnityBCI2000 attached to a GameObject called "BCI2K", your script would look like this:
<tt>
class ObjScript : MonoBehaviour {
  private UnityBCI2000 bci = GameObject.Find("BCI2K").GetComponent<UnityBCI2000>();
  public void Start(){}
  public void Update(){}
}
</tt>


1. Add a UnityBCI2000.cs to the GameObject as a Script Component.
Events and States can be added to BCI2000 within the Start() methods of your scripts. If you wanted to add an Event called "X" and a State called "Y", your script would look like this:
 
2.Specify the path to the operator using the Operator Path field, as well as the names of the modules to start up alongside it.
 
If you have an instance of the operator already running, specify the IP and port it is listening on using the Telnet Ip and Telnet Port fields instead.
 
You can also set a custom log file location, as well as tell the program to log sent states and received prompts. '''NOTE: There is a known issue with writing to the log file, this is remedied by changing the name of the file to write to. This is an issue with BCI2000RemoteNET, and will be fixed in upcoming updates.'''
 
3. Now, add a Script component of the script BCI2000StateSender to the object which you want to take data from.
 
4. Drag the BCI2000 GameObject from step 1 into the UnityBCI2000Object field of the BCI2000StateSender.
 
BCI2000StateSender comes with some predefined states to send, as well as values to scale them by. These are the global and screen coordinates, as well as whether the object is on screen.
 
===Adding Custom Variables===
Due to the way Unity works, adding custom variables must be done from a custom script, unique to each GameObject, if the GameObjects are not identical. Unfortunately, this means that some knowledge of C# programming is required, but templates for a CustomVariableSupplier script are provided within the README.md file.
 
There are two types of custom variables: custom send variables, which set a state in BCI2000 to some value, and custom get variables, which retrieve the value of a state variable from BCI2000.
 
Custom send variables contain the name of the state to write to, a Func<int> delegate(a piece of code represented by a method or lambda expression which returns the value to send to BCI2000), a scale factor to scale the variable by, in order to avoid truncation due to the fact that BCI2000 state variables are only unsigned integers, and a type.
Types are defined by the enum UnityBCI2000.StateType, which currently holds 5 values, which are signed integers of bit widths 16 and 32, as well as boolean, which is an unsigned integer of bit width 1. Signed numbers will be represented in BCI2000 by two state variables: The magnitude of the number and its sign, which is 0 for positive and 1 for negative.
 
Custom get variables contain the name of the state to read from, as well as an Action<int> delegate(a piece of code which takes an int as a parameter), which will receive the value from BCI2000.
For reference on delegates see the [https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/ C# Programming Guide].
Note: UnityBCI2000 uses reflection in order to reduce the amount of boilerplate code needed to implement a custom state variable, as well as simplify the process of adding and indexing of custom state variables.
 
Template for adding custom variables:


<tt>
<tt>
public class <ClassName> : CustomVariableBase
class ObjScript : MonoBehaviour {
{
  private UnityBCI2000 bci = GameObject.Find("BCI2K").GetComponent<UnityBCI2000>();
    public override void AddCustomVariables()
  public void Start(){
    {
    bci.AddEvent("M");
        customVariables.Add(new CustomSendVariable( //Copy this for more send variables
    bci.AddState("N");
            "[Name]",
  }
            new Func<float>(() => [Code which returns variable to send]),
  public void Update(){}
            [Scale],
            UnityBCI2000.StateType.[Type]
            ));
           
        customVariables.Add(new CustomGetVariable(  //Copy this for more get variables
            "[Name]"
            new Action<int> ((int i) => [Code which uses i])
        ));
 
    }
}
}
</tt>


</tt>
Events and States are accessed through a few methods of UnityBCI2000 called from the Update() method. In order to access their values, use the GetEvent and GetState methods.


Example of a custom variable provider script:
In order to set their values, call SetEvent, PulseEvent, or SetState. For events, SetEvent sets the value of an Event like a state, and PulseEvent sets the value of an Event for a single sample before returning it to its previous value, for use if you want to record an instantaneous event happening.
A script which interacts with events:


<tt>
<tt>
public class CustomVariableSupplier1 : CustomVariableBase
class ObjScript : MonoBehaviour {
{
  private UnityBCI2000 bci = GameObject.Find("BCI2K").GetComponent<UnityBCI2000>();
    public override void AddCustomVariables()
  public void Start(){
    {
    bci.AddEvent("M");
        customVariables.Add(new CustomSendVariable(
    bci.AddState("N");
            "Custom variable 1",
  }
            new Func<float>(() => {return 65 / 5;}),
  public void Update(){
            100,
    int x = bci.GetEvent("MousePosX");
            UnityBCI2000.StateType.SignedInt16
    if (condition) {
            ));
      bci.PulseEvent("thingHappened", 1);
 
        customVariables.Add(new CustomSendVariable(
            "Custom variable 2: Frame count",
            new Func<float>(() => Time.frameCount),
            1,
            UnityBCI2000.StateType.UnsignedInt32
            ));
 
        customVariables.Add(new CustomGetVariable(
            "StateName",
            new Action<int> ((int i) => {score = i})
        ));
     }
     }
    bci.SetEvent("Y", (int) transform.position.y);
  }
}
}
</tt>
</tt>
1. Add a new C# script to the object which you want to take data from.
2. Edit the script to change the inherited class to be CustomVariableBase instead of MonoBehavior.
3. Implement the AddCustomVariables method, and have it add Custom Variables to the customVariables List, by calling customVariables.Add. 
4. Drag this new script into the Custom Variable Supplier field of the BCI2000StateSender Component.

Revision as of 15:37, 2 August 2023

Synopsis

UnityBCI2000 is a tool for integration of Unity applications with BCI2000.

Tutorial

For information on how to use Unity itself, see the Unity manual. This tutorial assumes knowledge of how to use Unity. More in-depth detail on how UnityBCI2000 works is provided in the README.md file, as well as HTML documentation in the docs directory.

First, download UnityBCI2000 from GitHub. As of now, UnityBCI2000 is not a full Unity package, but it works the same. Place the files UnityBCI2000.cs and BCI2000RemoteNET.dll within the Assets folder of your Unity project.

Add the UnityBCI2000 script as a component to an empty GameObject. This will serve as the connection to BCI2000 within your project.

Specify the location of the BCI2000 executable, the BCI2000 modules you want to start along with their startup arguments, and any parameter files you want to load.

Connection to BCI2000 is done by calling UnityBCI2000's methods from other scripts. Within the script object from which you want to control BCI2000, add the UnityBCI2000 component as a member variable using Unity's GameObject.Find and GameObject.GetComponent methods. For example, if you have UnityBCI2000 attached to a GameObject called "BCI2K", your script would look like this: class ObjScript : MonoBehaviour {

 private UnityBCI2000 bci = GameObject.Find("BCI2K").GetComponent<UnityBCI2000>();
 public void Start(){}
 public void Update(){}

}

Events and States can be added to BCI2000 within the Start() methods of your scripts. If you wanted to add an Event called "X" and a State called "Y", your script would look like this:

class ObjScript : MonoBehaviour {

 private UnityBCI2000 bci = GameObject.Find("BCI2K").GetComponent<UnityBCI2000>();
 public void Start(){
   bci.AddEvent("M");
   bci.AddState("N");
 }
 public void Update(){}

}

Events and States are accessed through a few methods of UnityBCI2000 called from the Update() method. In order to access their values, use the GetEvent and GetState methods.

In order to set their values, call SetEvent, PulseEvent, or SetState. For events, SetEvent sets the value of an Event like a state, and PulseEvent sets the value of an Event for a single sample before returning it to its previous value, for use if you want to record an instantaneous event happening. A script which interacts with events:

class ObjScript : MonoBehaviour {

 private UnityBCI2000 bci = GameObject.Find("BCI2K").GetComponent<UnityBCI2000>();
 public void Start(){
   bci.AddEvent("M");
   bci.AddState("N");
 }
 public void Update(){
   int x = bci.GetEvent("MousePosX");
   if (condition) {
     bci.PulseEvent("thingHappened", 1);
   }
   bci.SetEvent("Y", (int) transform.position.y);
 }

}