Skip to content

Commit 111f857

Browse files
committed
Improve README for Unity
1 parent 11e2ff1 commit 111f857

2 files changed

Lines changed: 121 additions & 77 deletions

File tree

README-Unity.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Using LSL in Unity
2+
3+
The [LSL4Unity repository](https://github.com/labstreaminglayer/LSL4Unity) provides a more featureful integration of LSL into Unity. However, it is not as well-maintained as this repository. The recommended approach to integrating LSL into a Unity project is to use liblsl-Csharp's LSL.cs, and use LSL4Unity as a reference for more advanced use of LSL in Unity.
4+
5+
## Getting Started in Unity
6+
7+
LSL.cs includes a Unity interface to liblsl as a [Unity native plug-in](https://docs.unity3d.com/Manual/NativePlugins.html). The following instructions outline how to add a cube to a Unity scene that uses the LSL native plugin to pull in samples that modify its movement vector. These instructions were written while using Unity 2020.1.0f1.
8+
9+
1. Download the latest [liblsl release](https://github.com/sccn/liblsl/releases) for your platform(s) (e.g. liblsl-1.14.0-Win64.zip) and extract the library (e.g. lsl.dll) into the project's Assets/Plugins/lib folder (you may have to create this folder). If you plan to build for more than one target platform then you may wish to further subdivide the folders.
10+
* If you will deploy to Android, the easiest way to get the lib files is to follow the [liblsl-Android/AndroidStudio](https://github.com/labstreaminglayer/liblsl-Android/tree/master/AndroidStudio#getting-started) instructions to build an apk, then open the apk and copy the contents of the lib folder.
11+
1. Drag and drop LSL.cs into the project's Assets/Plugins folder.
12+
1. In Unity, use the Project view and navigate to the Assets/Plugins/lib folder. For each library file:
13+
* Set the platforms for the plug-in. [See here](https://docs.unity3d.com/Manual/PluginsForDesktop.html).
14+
1. In Unity, use the menu to place a cube in the scene: GameObject > 3D Object > Cube
15+
16+
### Control a game object from an inlett
17+
18+
1. When the cube is selected, in the Inspector click on "Add Component", and create a new script called LSLInput.
19+
1. In the Project viewer, double click on LSLInput.cs. This should launch Visual Studio or another IDE.
20+
1. Fill in the script. Use [LSL4Unity AInlet](https://github.com/labstreaminglayer/LSL4Unity/blob/master/Scripts/AInlet.cs) for inspiration.
21+
* There is [currently a bug]() that prevents liblsl in Unity from resolving streams from _other_ computers while running in editor, and also the built product but only when using ContinuousResolver. For this reason we recommend using liblsl.resolve_stream instead.
22+
```cs
23+
using System.Collections;
24+
using System.Collections.Generic;
25+
using UnityEngine;
26+
using LSL;
27+
28+
public class LSLInput : MonoBehaviour
29+
{
30+
public string StreamType = "EEG";
31+
public float scaleInput = 0.1f;
32+
liblsl.StreamInfo[] streamInfos;
33+
liblsl.StreamInlet streamInlet;
34+
float[] sample;
35+
private int channelCount = 0;
36+
37+
void Update()
38+
{
39+
if (streamInlet == null)
40+
{
41+
streamInfos = liblsl.resolve_stream("type", StreamType, 1, 0.0);
42+
if (streamInfos.Length > 0)
43+
{
44+
streamInlet = new liblsl.StreamInlet(streamInfos[0]);
45+
channelCount = streamInlet.info().channel_count();
46+
streamInlet.open_stream();
47+
}
48+
}
49+
50+
if (streamInlet != null)
51+
{
52+
sample = new float[channelCount];
53+
double lastTimeStamp = streamInlet.pull_sample(sample, 0.0f);
54+
if (lastTimeStamp != 0.0)
55+
{
56+
Process(sample, lastTimeStamp);
57+
while ((lastTimeStamp = streamInlet.pull_sample(sample, 0.0f)) != 0)
58+
{
59+
Process(sample, lastTimeStamp);
60+
}
61+
}
62+
}
63+
}
64+
void Process(float[] newSample, double timeStamp)
65+
{
66+
var inputVelocity = new Vector3(scaleInput * (newSample[0] - 0.5f), scaleInput * (newSample[1] - 0.5f), scaleInput * (newSample[2] -0.5f));
67+
gameObject.transform.position = gameObject.transform.position + inputVelocity;
68+
}
69+
}
70+
71+
```
72+
1. Elsewhere, run one of the LSL outlet examples. For example, from a conda environment with pylsl installed: `python -m pylsl.examples.SendData`
73+
1. Run the Unity game and watch that cube shake!
74+
75+
### Unity Outlet example
76+
77+
1. Attach a new component called LSLPosOutput to the cube.
78+
1. Edit it as follows:
79+
```cs
80+
using System.Collections;
81+
using System.Collections.Generic;
82+
using UnityEngine;
83+
using LSL;
84+
85+
public class LSLOutput : MonoBehaviour
86+
{
87+
private liblsl.StreamOutlet outlet;
88+
private float[] currentSample;
89+
90+
public string StreamName = "Unity.ExampleStream";
91+
public string StreamType = "Unity.StreamType";
92+
public string StreamId = "MyStreamID-Unity1234";
93+
94+
// Start is called before the first frame update
95+
void Start()
96+
{
97+
liblsl.StreamInfo streamInfo = new liblsl.StreamInfo(StreamName, StreamType, 3, Time.fixedDeltaTime * 1000, liblsl.channel_format_t.cf_float32);
98+
liblsl.XMLElement chans = streamInfo.desc().append_child("channels");
99+
chans.append_child("channel").append_child_value("label", "X");
100+
chans.append_child("channel").append_child_value("label", "Y");
101+
chans.append_child("channel").append_child_value("label", "Z");
102+
outlet = new liblsl.StreamOutlet(streamInfo);
103+
currentSample = new float[3];
104+
}
105+
106+
// Update is called once per frame
107+
void FixedUpdate()
108+
{
109+
Vector3 pos = gameObject.transform.position;
110+
currentSample[0] = pos.x;
111+
currentSample[1] = pos.y;
112+
currentSample[2] = pos.z;
113+
outlet.push_sample(currentSample);
114+
}
115+
}
116+
```

README.md

Lines changed: 5 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,15 @@
11
# C# bindings
22

3-
The is the C# interface to the lab streaming layer. To use it, you need to include the file LSL.cs in
4-
your project, and make sure that the appropriate lsl library (e.g. lsl.dll) is findable (e.g., in your application's
5-
root directory or in a system path).
3+
The is the C# interface to the lab streaming layer. To use it, you need to include the file LSL.cs in your project, and make sure that the appropriate lsl library (e.g. lsl.dll) is findable (e.g., in your application's root directory or in a system path).
64

7-
If LSL.cs fails to find the lsl shared library for your target platform, edit LSL.cs and update the following line with the name for your system: `const string libname = "lsl";`
5+
If LSL.cs fails to find the lsl shared library for your target platform, edit LSL.cs and update the following line with the library filename for your system: `const string libname = "lsl";`
86

97
# C# Example Programs
108

11-
The examples folder contains example C# code for sending and receiving data streams. The examples are described in the [online documentation](https://labstreaminglayer.readthedocs.io/dev/examples.html#id2).
9+
The examples folder contains example C# code for sending and receiving data streams. The examples are described in details in the [online documentation](https://labstreaminglayer.readthedocs.io/dev/examples.html#id2).
1210

13-
These example applications can be debugged from within the IDE (i.e. Visual Studio). However, the built products are DLL files, not EXE files. The DLL files can be run at console with `dotnet my_application` (from within same folder as my_application.DLL). This will work anywhere the .NET Core Runtime works. To make a more portable but platform-dependent product, use `dotnet publish -C Debug -r win10-x64` (or Release instead of Debug) and this will generate an EXE file.
11+
These example applications can be debugged from within the IDE (i.e. Visual Studio). However, the built products are DLL files, not EXE files. The DLL files can be run at console with `dotnet my_application` (from within same folder as my_application.DLL). This will work anywhere the .NET Core Runtime works. To make a self-contained but platform-dependent product, use `dotnet publish -C Debug -r win10-x64` (or `Release` instead of `Debug`) and this will generate an EXE file.
1412

1513
# Unity
1614

17-
The [LSL4Unity repository](https://github.com/labstreaminglayer/LSL4Unity) provides a more featureful integration of LSL into Unity. However, it is not as well-maintained as this repository. The recommended approach to integrating LSL into a Unity project is to use this repository's LSL.cs, and use LSL4Unity as a reference for more advanced use of LSL in Unity.
18-
19-
## Getting Started in Unity
20-
21-
These instructions were written while using Unity 2020.1.0f1.
22-
23-
1. Drag and drop LSL.cs into the project Assets folder.
24-
1. Download the latest [liblsl release](https://github.com/sccn/liblsl/releases) for your platform and extract the library (e.g. liblsl64.dll) into the project root folder.
25-
1. In Unity, use the menu to place a cube in the scene: GameObject > 3D Object > Cube
26-
1. When the cube is selected, in the Inspector click on "Add Component", and create a new script called LSLInput.
27-
1. In the Project viewer, double click on LSLInput.cs. This should launch Visual Studio or another IDE.
28-
1. Fill in the script. Use [LSL4Unity AInlet](https://github.com/labstreaminglayer/LSL4Unity/blob/master/Scripts/AInlet.cs) for inspiration.
29-
```
30-
using System.Collections;
31-
using System.Collections.Generic;
32-
using UnityEngine;
33-
using LSL;
34-
35-
public class LSLInput : MonoBehaviour
36-
{
37-
public string StreamType = "EEG";
38-
public float scaleInput = 0.1f;
39-
liblsl.StreamInfo[] streamInfos;
40-
liblsl.StreamInlet streamInlet;
41-
liblsl.ContinuousResolver resolver;
42-
float[] sample;
43-
private int channelCount = 0;
44-
45-
// Start is called before the first frame update
46-
void Start()
47-
{
48-
resolver = new liblsl.ContinuousResolver("type", StreamType);
49-
StartCoroutine(ResolveExpectedStream());
50-
}
51-
52-
IEnumerator ResolveExpectedStream()
53-
{
54-
var streamInfos = resolver.results();
55-
yield return new WaitUntil(() => streamInfos.Length > 0);
56-
streamInlet = new liblsl.StreamInlet(streamInfos[0]);
57-
channelCount = streamInlet.info().channel_count();
58-
streamInlet.open_stream();
59-
yield return null;
60-
}
61-
62-
// Update is called once per frame
63-
void Update()
64-
{
65-
if (streamInlet != null)
66-
{
67-
sample = new float[channelCount];
68-
double lastTimeStamp = streamInlet.pull_sample(sample, 0.0f);
69-
if (lastTimeStamp != 0.0)
70-
{
71-
Process(sample, lastTimeStamp);
72-
while ((lastTimeStamp = streamInlet.pull_sample(sample, 0.0f)) != 0)
73-
{
74-
Process(sample, lastTimeStamp);
75-
}
76-
}
77-
}
78-
}
79-
void Process(float[] newSample, double timeStamp)
80-
{
81-
var inputVelocity = new Vector3(scaleInput * (newSample[0] - 0.5f), scaleInput * (newSample[1] - 0.5f), scaleInput * (newSample[2] -0.5f));
82-
gameObject.transform.position = gameObject.transform.position + inputVelocity;
83-
}
84-
}
85-
```
86-
1. Elsewhere, run one of the LSL outlet examples. For example, from a conda environment with pylsl installed: `python -m pylsl.examples.SendData`
87-
1. Run the Unity game.
15+
Please see the separate [README-Unity](https://github.com/labstreaminglayer/liblsl-Csharp/README-Unity.md).

0 commit comments

Comments
 (0)