Plugins Guide
This guide explains how to build and develop the C++ plugins that provide core functionality for the micromissiles simulator and allow C++ code to be executed within Unity.
Prerequisites
- A C++ compiler (GCC, Clang, or MSVC)
- Git
- Python (for Bazel)
- Bazel 8 (or Bazelisk)
Installing Bazel
This project requires Bazel version 8.
For detailed installation instructions for your platform (Windows, macOS, Linux), please refer to the official Bazel documentation.
A few important notes:
- Bazelisk is recommended as it automatically manages Bazel versions based on the
.bazelversionfile. - On Windows, you will need additional components like MSYS2 and Visual Studio Build Tools.
- You can verify your installation with
bazel version.
Building the Plugins
Change into the
pluginsdirectory.bashcd pluginsBuild all plugins:
bashbazel build //...Build specific plugins:
bash# Build the assignment plugin. bazel build //:assignment # Build the example plugin. bazel build //:example # Build and package all plugins into a tarball. bazel build //:plugins # Build and package all plugins into a tarball with optimization enabled. bazel build -c opt //:pluginsAfter building, the compiled shared libraries can be found in the
bazel-bindirectory. The packaged plugins tarball are located atbazel-bin/plugins.tar.gz.Run tests:
bashbazel test //...
Integrating with Unity
To use these plugins with the Unity project:
- Build the plugins using the instructions above. Ensure that you have optimization enabled to reduce the shared library size.
- Extract the contents of
bazel-bin/plugins.tar.gz. - Copy the shared libraries (
.dll,.dylib, or.sofiles) to the appropriate plugins directory in the Unity project.
Ubuntu Compatibility
Ensure that the plugin is compiled on the same Ubuntu version (e.g., 2022.04) on which the Unity project will run to maintain compability with the glibc and libstdc++ standard libraries. Unity will fail load the plugin if the plugin was compiled against a newer version of glibc or libstdc++ that is not present on the current system.
Currently, the plugin is compatible with Ubuntu 22.04 or newer.
Development
Project Structure
All plugin-related code can be found under the plugins/ directory. Currently, there are two plugins:
assignment/: Implements different assignment algorithms using Google's OR-Tools library to assign interceptors to threats.protobuf/: Implements loading Protobuf messages from Protobuf text files.
Other useful code can be found in the other directories:
base/: Common base utilities.example/: Contains an example plugin implementation.experimental/: Experimental code, including toy examples to demonstrate various packages.
Status Codes
Plugins should never throw exceptions as these cannot be caught by Unity and will cause the simulation to crash. Instead, plugins should always return a status code that Unity can check and handle appropriately.
The complete list of status codes is defined in status.proto and is adapted from Google's Abseil status codes. Defining a Protobuf enumeration for status codes allows both the C++ plugins and the Unity C# code to reference the same set of values.
The most common status codes are given in the table below:
| Status Code | Description |
|---|---|
STATUS_OK | Returned on success and indicates no error. |
STATUS_INVALID_ARGUMENT | Returned when an invalid argument was specified. |
STATUS_NOT_FOUND | Returned when a resource was not found. |
STATUS_FAILED_PRECONDITION | Returned when the arguments are valid but an invariant has not been satisfied. |
STATUS_OUT_OF_RANGE | Returned when a resource was accessed past its valid range. |
STATUS_UNIMPLEMENTED | Returned when the operation has not been implemented. |
STATUS_INTERNAL | Returned when an internal error occurred during the operation. |
For more details on the meaning and usage of each status code, refer to the Abseil status documentation.
Example
Because plugins are designed to return a status code rather than throw exceptions, they rely on output arguments to pass data back to Unity. For example, the Assignment plugin exposes the following C API for performing an even assignment:
// Assign the agents to the tasks using an even assignment.
plugin::StatusCode Assignment_EvenAssignment_Assign(
const int num_agents, const int num_tasks, const float* costs,
int* assigned_agents, int* assigned_tasks, int* num_assignments)The corresponding Unity C# declaration is:
// Assign the agents to the tasks using an even assignment.
[DllImport("assignment")]
public static extern Plugin.StatusCode Assignment_EvenAssignment_Assign(
int numAgents, int numTasks, float[] costs, int[] assignedAgents, int[] assignedTasks,
out int numAssignments);In this example:
costsis a float array passed from Unity to the plugin.assigned_agents/assignedAgentsandassigned_tasks/assignedTasksare integer arrays used as output arguments.num_assignments/numAssignmentsis an integer output argument of the plugin.
// Solve the assignment problem.
int[] assignedInterceptorIndices = new int[assignableInterceptors.Count];
int[] assignedThreatIndices = new int[assignableInterceptors.Count];
int numAssignments = 0;
Plugin.StatusCode status = Assignment.Assignment_EvenAssignment_Assign(
assignableInterceptors.Count, activeThreats.Count, assignmentCosts,
assignedInterceptorIndices, assignedThreatIndices, out numAssignments);After the call, the returned status code should always be checked and logged if necessary.
if (status != Plugin.StatusCode.StatusOk) {
Debug.Log($"Failed to assign the interceptors to the threats with status code {status}.");
return assignments;
}