Unity Integration
Aside from basic RTI connectivity and publish/subscribe messaging, the Unity integration contains functionality for managing runtime control, entities, geometry, measures, injects, commands etc in a way that integrates nicely with the game engine architecture.
Getting started
See the Unity tutorial for setting up a new project and creating a basic simulator.
Adding the package
The Inhumate RTI integration for Unity is available in several locations depending on your preference:
- Unity Asset Store (note however that the Asset Store version may be outdated)
- OpenUPM
- Inhumate Downloads
- GitHub
Spawn co-simulated entities
Entities are objects/actors that exist in the co-simulation “world”, and their life cycle (create -> destroy) and state (more on that later) are synchronized via the RTI.
- Create a cube in your
Demoscene hierarchy. - Add an RTI Entity component, set its Type property to
cube. - Add an RTI Position component.
- Make it a prefab by dragging the cube from the Hierarchy view into your Project view.
- Delete the cube from the scene.
- Create an empty game object.
- Add an RTI Spawner component.
- For the Spawnable Entities property, add an element with type set to
cubeand drag your cube prefab into the prefab property.
Now, if you play the scene, you can spawn and destroy a cube using the RTI CLI:
# create a cube
rti entity create --id thecube cube 0 1 2.5
# look around and find it with the editor camera, then move it
rti entity position thecube 2 2 2.5
# then destroy it
rti entity destroy thecube
Spawn a player
Your scenario scene is loaded upon reception of a load scenario message. At that point, no entities should be created. You need to spawn the player when the simulation starts, not when the scene is loaded. You also need to either:
- create a “remote representation” prefab that the RTI Spawner can use for playback, or
- use the same prefab but adapt your scripts to differentiate between simulation and playback behavior. Use the
RTIEntity.publishingandRTIEntity.receivingproperties for that.
Custom (project-specific) messages
Now let’s start digging into adding more functionality than just runtime control, entities and their whereabouts.
Pubsub custom messages (string or JSON)
You can publish and subscribe to simple text or JSON messages.
RTIConnection.Instance.Publish("mychannel", "my message");
RTICOnnection.Instance.PublishJson("mychannel", mySerializableObject);
Pubsub protobuf messages representing entity state
A common pattern for Unity-based simulations is to exchange entity state beyond position, for example a vehicle that needs to publish its vehicle-specific state (e.g. steering angle and brake lights) so that it can be subscribed to and visualized by other simulation clients.
Let’s say you have a component Vehicle that manages your vehicle steering, lights etc.
Create a .proto with the protobuf definition of the state to be transferred:
syntax = "proto3";
message VehicleState {
string id = 1; // entity id
float steering_angle = 2; // degrees, positive right
bool headlight = 3;
bool brakelight = 4;
bool blinker_left = 5;
bool blinker_right = 6;
bool reverselight = 7;
// bool engine_running
// int gear
// int rpm
// etc...
}
Use the protobuf compiler to generate a C# representation, typically placed in a Generated/ subfolder of your scripts.
Then make a component deriving from RTIEntityStateBehaviour, something along the lines of:
public class RTIVehicleState : RTIEntityStateBehaviour<VehicleState> {
public string ChannelName => "vehiclestate";
public float updateInterval = 1f;
private Vehicle vehicle;
protected override void Start() {
base.Start();
vehicle = GetComponent<Vehicle>();
}
void Update() {
if (entity.published && publishing && Time.time - lastPublishTime > updateInterval) {
lastPublishTime = Time.time;
Publish(new VehicleState {
Id = entity.id,
SteeringAngle = vehicle.steeringAngle,
Headlight = vehicle.headlight,
// ...
});
}
}
protected override void OnMessage(VehicleState message) {
if (receiving && enabled) {
vehicle.steeringAngle = message.SteeringAngle;
vehicle.headlight = message.Headlight;
// ...
}
}
}
Scenario loading
Part of the runtime control standard message is a load scenario message, and querying for available scenarios. The default behavior of the Unity package is to load a home level on startup and stop message, and when a load scenario message is received, lookup the name in the scenarios list (which is populated with the scenes in build settings by default, or can be specified with Scenario objects in the editor) in the RTIConnection, and then load a scene with the same name as the scenario. This behavior can be overridden in a script that:
- fills the
RTIConnection.scenarioslist with names of scenarios that can be loaded - implements delegates for
RTIConnection.OnLoadScenarioandRTIConnection.OnStopevents
See RTITestCustomScenarioLoading.cs for an example.