Unity

Overview

Simulate includes an integration with Unity via the "Unity Plugin", install the Unity Plugin from the SDK installer. The plugin lets you connect clients developed in Unity to your Simulate simulations.

Prerequisities

General architecture

To build a Unity client for the simulation you should be familiar with the general simulate-connect-client architecture.
This guide describes setting up the project step by step. In addition, a complete project which uses the Aether Plugin for Unity is available in the Physics demo which can be selected during installation. Once installed, the UnityClient project can be found in %USERPROFILE%/Hadean/Unity/aetherplugin.tgz.

Adding the Simulate Unity Plugin to a unity project.

  1. 1.
    Open or create a unity project.
  2. 2.
    Add the Aether plugin to the project using Window -> Package Manager menu. Select the + menu -> Add package from tarball and select the path where sdk installed the plugin, by default: %USERPROFILE%/Hadean/Unity/aetherplugin.tgz
Could not load image
Adding the Aether Unity Plugin to Unity
  1. 1.
    Once added the plugin will be visible in the package list and in the package menu, as in the screenshot below (the version of the plugin will match the version of the SDK).
The Aether Engine Unity Plugin available in Unity

Using the client API from Unity

The Unity Client API is very similar to the C++ API described in the repclient documentation, with small changes to make it integrate better with C#.
Here's an overview of an example class used to connect to the Simulate simulation.
using AetherPlugin;
public class Simulation : MonoBehaviour
{
private Repclient _repclient;
// Awake is called before the first frame update - we initialize here
void Awake()
{
// 1. create a connection
_repclient = Repclient.Connection("aether-sdk.mshome.net", "8881");
// 2. authenticate with a player id and a token
_repclient.AuthenticatePlayerIdWithToken(1, "00000000000000000000000000000000");
}
// Update is called once per frame
void Update()
{
// 3. read the received data in the update loop
while (_repclient.IsConnected()) {
byte[] repData = _repclient.ReceiveMessage();
if (repData == null || repData.Length == 0) return;
// 4. decode the data according to the used protocol (see "Using FlatBuffers for marshalling the data")
var message = DecodeMessage(repData);
// use the message
foreach (Entity e in message.Entities) {
// ...
}
}
}
// send a client event to the simulation
void SendEvent()
{
// 5. encode the event data according to the used protocol (see "Using FlatBuffers for marshalling the data")
byte[] eventData = EncodeEvent();
// 6. send the event
_repclient.SendEvent(eventData, false);
}
}

Defining a protocol using FlatBuffers

The protocol documentation contains an overview of different approaches to defining a client-server communication protocol. For a project with a Unity client, a protocol defined using FlatBuffers is a good starting point, as it makes it easy to define the data format in one place without having to worry about keeping the C++ and C# definitions synchronized (besides re-running the code generator). It's worth noting that Simulate and the Unity plugin are themselves independent of the used protocol, so FlatBuffers can be replaced with a different approach if needed.
Here's a minimal example of a FlatBuffers protocol file:
// define a structure used for storing coordinates
struct Vec3 {
x: float;
y: float;
z: float;
}
// define a very simple entity
struct Entity {
position: Vec3;
}
// define each message coming from the simulation to contain the current state of all entities
table SimulationMessage {
entities: [Entity];
}
// define a very simple event to be sent to simulation
table ClientEvent {
position: Vec3;
}
For this guide we'll assume the protocol is saved in a file named protocol.fbs.
Adding FlatBuffers to a Unity project
Once the protocol is defined, the Unity project needs to be configured to use the FlatBuffers protocol. This involves several steps:
  1. 1.
    Get FlatBuffers.dll and flatc
  2. 2.
    Add FlatBuffers.dll to the Unity project
  3. 3.
    Generate the the C# source code for protocol files
  4. 4.
    Add the generated files to the project
  5. 5.
    Automate the process (optional)
1. Get FlatBuffers.dll and flatc
Flatc is the FlatBuffers compiler which generates the code for encoding and decoding data using FlatBuffers protocol. FlatBuffers.dll contains classes necessary to use the generated C# code.
There are multiple ways of obtaining these:
  • Use the binaries shipped with the example Physics Demo project, by default installed in %USERPROFILE%\Hadean\Aether Engine Examples\PhysicsDemo\Simulation\Protocol.
  • Building the binaries from source: flatc and FlatBuffers.dll
  • Download binaries using NuGet: flatc and FlatBuffers.dll
2. Add FlatBuffers.dll to the Unity project
  1. 1.
    Create a FlatBuffers directory to hold FlatBuffers related files in Assets/ directory of the Unity project.
  2. 2.
    Drag and drop FlatBuffers.dll into Assets/FlatBuffers directory.
This will make the FlatBuffers API, used for encoding and decoding data, available in your Unity project.
3. Generate the the C# source code for protocol files
The protocol compiles converts the protocol definitions specified in protocol.fbs into C# classes that can then be used to encode and decode the data.
The following command will generate the files:
# -n generates C# classes
$ flatc.exe -n protocol.fbs
Here are the generated methods that we're going to use in the example:
SimulationMessage.cs (details omitted for brevity):
public struct SimulationMessage : IFlatbufferObject
{
public static SimulationMessage GetRootAsSimulationMessage(ByteBuffer _bb);
public Entity? Entities(int j);
public int EntitiesLength;
};
Vec3.cs (details omitted for brevity):
public struct Vec3 : IFlatbufferObject {
public static Offset<Vec3> CreateVec3(
FlatBufferBuilder builder,
float X, float Y, float Z
);
};
ClientEvent.cs (details omitted for brevity):
public struct ClientEvent : IFlatbufferObject
{
public static void StartClientEvent(FlatBufferBuilder builder);
public static void AddPosition(FlatBufferBuilder builder, Offset<Vec3> positionOffset);
public static Offset<ClientEvent> EndClientEvent(FlatBufferBuilder builder);
};
4. Add the generated files to the project
Drag and drop the files generated in the previous step into the Assets/FlatBuffers directory in the Unity project.
The result should look like the following:
Could not load image
hadean screen snippet
5. Automate the process
The steps 3. and 4. of the above process need to be performed every time the protocol definition in protocol.fbs is changed. Therfore it's a good idea to automate the process, which can be done using the script below:
# download flatc and FlatBuffers.dll
nuget restore -PackagesDirectory "${PSScriptRoot}\packages" ${PSScriptRoot}
# create FlatBuffers directory in the assets directory of the Unity project
$unity_assets_path = "${PSScriptRoot}\UnityProject\Assets\FlatBuffers"
New-Item -ItemType "directory" -Path $unity_assets_path -Force
# get flatc from the downloaded package
$flatc_bin = Get-ChildItem "${PSScriptRoot}\packages" -Filter flatc.exe -Recurse | % { $_.FullName }
# generate csharp files
$flatc_bin -n -o $unity_assets_path "${PSScriptRoot}\protocol.fbs"
# copy FlatBuffers.dll to the assets directory of the Unity project
Copy-Item "${PSScriptRoot}\packages\ek96.FlatBuffers.1.11.0\lib\netstandard2.0\FlatBuffers.dll" -Destination $unity_assets_path
Using FlatBuffers for marshalling the data
Once the protocol has been defined and all the classes and libraries are visible in Unity, the data marshalling can be implemented.
using FlatBuffers;
public class Simulation : MonoBehaviour
{
List<global::Entity> DecodeMessage(byte[] data)
{
ByteBuffer bb = new ByteBuffer(data);
var entities = new List<global::Entity>();
var message = SimulationMessage.GetRootAsSimulationMessage(bb);
for (int i = 0; i < message.EntitiesLength; i++)
{
entities.Add(message.Entities(i).Value);
}
return entities;
}
byte[] EncodeEvent()
{
FlatBufferBuilder fbb = new FlatBufferBuilder(1);
var position = Vec3.CreateVec3(fbb, 0, 0, 0);
ClientEvent.StartClientEvent(fbb);
ClientEvent.AddPosition(fbb, position);
fbb.Finish(ClientEvent.EndClientEvent(fbb).Value);
}
}
Steps to implement FlatBuffers marshalling on the simulation side are similar to the steps on the client side:
  1. 1.
    Add the FlatBuffers header-only-library to the CMake project
  2. 2.
    Generate C++ source code from the protocol definition and add them to the project
  3. 3.
    Call FlatBuffers marshalling methods in order to serialize and deserialize the data.
More details on the usage of FlatBuffers on the server side are available in the protocol documentation and in the PhysicsDemo installed with the SDK.