Communicate with Vortex using VHL interfaces

This page describes how to use data from a Vortex VHL interface in a custom Unity script. This is a simple way to implement custom behavior or logic in Unity using data in a distributed Vortex simulation.

See Working With VHL interfaces on mechanisms for more information about VHL interfaces.

Overview

When associating a Vortex Scene to a Unity Scene, VortexVHL components are created to allow communication between Unity and Vortex.

For example, if the Vortex Scene contains several VHL extensions:

The Unity Scene will have the same objects, ready to be used.

Using VortexVHL component

Any script can be added to a Game Object with a VortexVHL component and will be able to communicate with Vortex.

Vortex VHL component

 

To use the data in a script, the prefab must contain a script component:  it is in such a script that the VHL interface's data can be accessed. To do this, you first need to get a reference to the VortexVHL component. It is recommended to do this in the Start or Awake method, then re-use it as needed. The reference can also be set directly from the Inspector.

Accessing VHL interface

public class DisplayVHLData : MonoBehaviour { private Vortex.VortexVHL vhl = null; public UnityEngine.UI.Text speedText = null; public bool isVisible = false; void Awake() { vhl = GetComponent<Vortex.VortexVHL>(); } }

A series of getters VortexVHL.GetXXXX() and setters VortexVHL.SetXXXX() allows to set and get values for several types.

OnEnable() on a MonoBehaviour is too early to get or set values to VortexVHL. Initialization should be done in the Start() function.

Get a value from a VHL field

The following methods can be used to access the value of the data from a VHL interface's field. All these methods take the same argument: the string corresponding to the field path. When using these methods, if a field is not found, an error will appear in the log and the value returned will be a default value.

A field path name is simply the path to a Field in a VHL extension. A field path is built by concatenating the names of the field in the Field Container hierarchy until you get to the target field.

All field paths begin with either Inputs, Outputs, or Parameters, depending on what type of field they are. Examples of field paths are

  • Inputs.Vehicle.Speed

  • Inputs.Active

  • Outputs.Engine.RPM

  • Parameters.Vehicle.Engine.MaxRPM

Return Data Type

Get Value from Field Method

Additional Notes

Return Data Type

Get Value from Field Method

Additional Notes

bool

VortexVHL.GetBool(path)



int

VortexVHL.GetInt(path)



double

VortexVHL.GetReal(path)



string

VortexVHL.GetString(path)



UnityEngine.Vector2

VortexVHL.GetVector2(path)

Vortex Vector 2 of double values.

UnityEngine.Vector3

VortexVHL.GetVector3(path)

Vortex Vector 3 of double values.

UnityEngine.Vector3

VortexVHL.GetVector3AsPosition(path)

Vector 3, assumed as position information. It is converted from Vortex to Unity coordinate system.

UnityEngine.Vector4

VortexVHL.GetVector4(path)

Vortex Vector 4 of double values.

UnityEngine.Color

VortexVHL.GetColor(path)

Vortex Color of float values

UnityEngine.Matrix4x4

VortexVHL.GetMatrix(path)

Vortex Matrix44 represented by Unity's Matrix4x4 type. No conversion is done whatsoever.

Vortex.Types.PositionRotation

VortexVHL.GetMatrixAsPositionRotation(path)

Matrix4x4, interpreted as transform information into the PositionRotation class, which contains a position vector, rotation quaternion, and scale vector adapted from Vortex to Unity coordinate system. These can be used directly to assign to Unity Transform component's position, rotation and scale properties.

string

VortexVHL.GetExtensionName(path)




Set a value to a VHL field

In this integration, it is also possible to set values of a VHL extension through Unity. However, this feature is only available on standalone simulators (single node) or on the master node of a multi-node simulator.  From a script, it is possible to know if the application is on the master node using the IsMaster method. See this page: How to get information about the Vortex simulation.

For more information about nodes, please refer to Vortex Integration SDK.

The following methods can be used to set the value of the data from a VHL interface's field. All these methods take two arguments: the string corresponding to the field path and the value to be set in the VHL. When using these methods, if a field is not found, an error will appear in the log and the value returned will be a default value. These methods will also fail and return an error if the code is not running on the Master node. 

Data Type

Set value on VHL Field Method

Additional Notes

Data Type

Set value on VHL Field Method

Additional Notes

bool

VortexVHL.SetBool(path, value)



int

VortexVHL.SetInt(path, value)



double

VortexVHL.SetReal(path, value)



string

VortexVHL.SetString(path, value)



UnityEngine.Vector2

VortexVHL.SetVector2(path, value)

Vortex Vector 2 of double values.

UnityEngine.Vector3

VortexVHL.SetVector3(path, value)

Vortex Vector 3 of double values.

UnityEngine.Vector3

VortexVHL.SetVector3AsPosition(path, value)

Vector 3, assumed as position information. It is converted from Unity to Vortex coordinate system.

UnityEngine.Vector4

VortexVHL.SetVector4(path, value)

Vortex Vector 4 of double values.

UnityEngine.Color

VortexVHL.SetColor(path, value)

Vortex Color of float values

UnityEngine.Matrix4x4

VortexVHL.SetMatrix(path, value)

Unity's Matrix44 represented by Vortex Matrix4x4 type. No conversion is done whatsoever.

UnityEngine.Transform

VortexVHL.SetMatrixAsPositionRotation(path, value)

Unity's Transform converted to Vortex coordinate system as a Vortex Maxtrix4x4.

VHL Interface Script Example

Here is an example of a script that uses a VHL interface to display some Vortex data in Unity. 

Sample Script
using UnityEngine; using Vortex; public class DisplayVHLData : MonoBehaviour { private Vortex.VortexVHL vhl = null; public UnityEngine.UI.Text speedText = null; public UnityEngine.UI.Text distanceText = null; public UnityEngine.UI.Image engineImage = null; public bool isVisible = false; void Awake() { vhl = GetComponent<Vortex.VortexVHL>(); } void FixedUpdate() { if (speedText != null) { double speed = vhl.GetReal("Inputs.Speed"); // Displays speed with 2 decimals format speedText.text = $"Current Speed: { speed.ToString("F2") }"; } if (engineImage != null) { bool engineOn = vhl.GetBool("Inputs.Engine Active"); engineImage.enabled = engineOn; } if (distanceText != null) { double distance = vhl.GetReal("Outputs.Distance"); // Displays distance with 2 decimals format distanceText.text = $"Distance Moved: { distance.ToString("F2")}"; } vhl.SetBool("Parameters.Visibility", isVisible); } }

Here is how this script is attached to a prefab with the proper components and references to them:

And here is the VHL interface as defined in a mechanism in the Vortex Editor:

Using VortexVHL dynamic fields

For ease of use, the VortexVHL component exposes dynamic fields. The member Inputs, Outputs and Parameters are object extending C# System.Dynamic.DynamicObject. For example, a VHL input property named "Velocity" can be access by writing vhl.Inputs.Velocity. There is a few things to keep in mind while using this feature.

  • The dynamic fields are subject to the same limitations as C# DynamicObject 
    This means the properties can't be reserved words such as double, return, bool, etc. This also means you can't use extension methods directly.

    // double myDouble = vhl.Inputs.double; // ERROR: double is a C# reserved keyword // Vector3 myPosition = vhl.Outputs.Position.MyExtensionMethod(); // ERROR: CS1973: Vector3<dynamic> has no applicable method named MyExtensionMethod() Vector3 myPosition = vhl.Outputs.Position; myPosition = myPosition.MyExtensionMethod(); // OK



  • Property's names are case sensitive


  • Non-alphanumeric characters are replaced with underscores
    Vortex allows field's properties to contain character that can't be used to form valid C# identifiers such as space, parenthesis, punctuation to name a few. These properties can be access by replacing the non-alphanumeric values with underscores. For example, the VHL input property "Desired Velocity" can be access with vhl.Inputs.Desired_Velocity, and the VHL output property "My output (5)" can be access with vhl.Outpus.My_output__5_. Notice the double underscore between output and 5: this is because the space and the opening parenthesis both account for one underscore.


  • Values are returned as raw values
    Objects like Vector3 and Matrix4x4 are returned as raw values. A Vector3 representing a position in Vortex frame of reference would need to be converted to Unity referential with ConversionUtilities.VortexToUnityVector. Similarly, a Matrix4x4 transformation matrix in the Vortex frame of reference also needs to be converted with ConversionUtilities.VortexToUnityPositionRotation.

Dynamic Field Usage Example

The following figures shows a VHL interface configuration and a script sample demonstrating how to use the dynamic fields.

Dynamic Fields
using UnityEngine; using Vortex; public class DynamicVHLData : MonoBehaviour { private Vortex.VortexVHL vhl = null; public bool myBool = false; public Color myColor = null; public string myString = null; void Awake() { vhl = GetComponent<Vortex.VortexVHL>(); } void FixedUpdate() { myBool = vhl.Inputs.isActive; // yields true myColor = vhl.Outputs.My_color; // yields a new UnityEngine.Color(1,1,1,1) myString = vhl.Parameters.Texture; // yields "default texture" } }

Using an observer on a VHL field

The observer pattern is a very useful way to simplify logic in the code. A regular check for value change in a VHL field, usually implement by polling the value at every Update(), becomes can quickly become a processing bottleneck in a complex system.

When using the Observer on VHL field, the observer callback function will be notified at each value change on the VHL field. This should be used on VHL fields that are not modified very often, e.g. turning on or off of a light. It should not be used for fields that change value continually, e.g. the position of an object.

Adding and removing an observer

First, to create an observer, you need to define a callback function. The delegate definition in this case is

public delegate void Vortex.VortexVHL.ObserverDelegate(string fieldPath);

The callback function will receive the field name that was changed. This way, the same observer callback can be used for several fields if needed.

The API to add or remove an observer is simple:

namespace Vortex { public class VortexVHL { // Add an observer to a VHL field // Arguments: // fieldPath: the path to any field in the VHL // callback: the delegate to call when the field has changed value // Return value // The Observer object that can be used to destroy the observer, null on failure to add the observer public Observer AddObserver(string fieldPath, ObserverDelegate callback); // Remove an observer from a VHL field // Arguments: // observer: the Observer object to remove (was obtained when adding the observer) public void RemoveObserver(Observer obsever); // Object representing an observer public class Observer { // Destroy this observer from the VHL public void Destroy(); } } }

An observer is added with the VortexVHL.AddObserver on a VortexVHL object. The argument fieldPath is a generic way to point to any field in the VHL. It supports any inputs, outputs or parameters, and any level of data Container in the VHL.

The syntax of a Field Path is simple. It is just the name of the fields in the hierarchy of Containers. It always starts with either Inputs, Outputs, or Parameters, followed by the name of the fields in the hierarchy

// add an observer to the Input "LightOn" in the vhl observer = vhl.AddObserver("Inputs.LightOn", onLightSwitchChanged); // add an observer to the field "LightOn" that is in the Container "LightControl", that is in the Container "ControlBoard", that is in the Ouputs of the vhl obsever = vhl.AddObserver("Outputs.ControlBoard.LightControl.LightOn", onLightSwitchChanged);

The AddObserver method returns an Observer object that needs to be kept. It is used to remove the Observer.

There are two ways of destroying an observer, both need the object returned from AddObserver, both are equivalent.

Observer myObserver = vhl.AddObserver("Inputs.LightOn", onLightSwitchChanged); // if you have access to the VHL, you can do vhl.RemoveObserver(myObserver); // or you can just do it on the observer itself myObserver.Destroy();

If an observer is not destroyed when the callback is no longer available, the application is likely to crash.

The best method to avoid any issues is to use AddObserver in OnEnable() of a MonoBehavior, and DeleteObserver in OnInactive()

Â