Mastering C# and Unity3D

C++ Scripting: Part 5 – Bindings Code Generator

Last week in the series we took a step back to verify that the C++ plugin’s performance was acceptable. With that confirmed, we’ll continue this week by making our programming lives easier. One pain point so far has been with exposing new Unity APIs to C++. It’s not that it’s difficult to do this, but there’s a lot of boilerplate required. That boilerplate takes time to write and it’s easy to make mistakes copying and pasting existing functions. So this week’s article introduces a code generator that will write the boilerplate for us! We’ll also reorganize the project a little so the code that supports C++ scripting is separated away from our game code. That’ll make it easy to add support for C++ scripting to any Unity project.

First, let’s add a code generator to generate all the boilerplate required to make C# functions callable by C++. The code generator is simply a Unity editor script that we can run with a menu item or via the command line. It loads a JSON file describing what should be exposed. For example, in previous articles we exposed the default GameObject constructor and the GameObject.transform and Transform.position properties. Do do that, we’d add them and their base classes to a JSON document and say that those types can be found in UnityEngine.dll:

This document allows us to specify exactly what we want to expose to C++. This keeps bloat down and allows the code generator to run really fast: under a second on my machine. We simply fill in the constructors, methods, properties, and fields we want exposed and run the code generator to fill in all the required C# and C++ code.

The code generator is currently 1700 lines, so I won’t go over all of how it works. At a high level though, it uses C# reflection to load a DLL, builds a lot of strings of source code, and injects them into existing source files between start and end markers. Currently, the code generator supports some basic functionality but lacks support for some C# features:

Supported

Not Supported

Constructors

Arrays (single- or multi-dimensional)

Properties (get and set)

out and ref parameters

Fields

Struct types

Methods

Generic functions and types

Class types (static and regular)

Delegates

With this tool available to us, we can quickly expose new Unity API functionality. For example, if we wanted to expose the Component.transform property we’d simply modify one line of the JSON and run the code generator:

"Properties":["transform"],

"Properties": [ "transform" ],

We can also expose functionality from non-Unity DLLs. For example, we can expose System.Diagnostics.Stopwatch from the .NET API by adding this JSON block:

Next up for today, let’s split the existing code up into two parts. First is the code that supports C++ scripting. This part includes all the native plugin loading and unloading, exposing C# functions to C++, and object-oriented type definitions. This is the “bindings” layer that games shouldn’t ever need to touch. It should be easy to copy/paste into a new project and start scripting that project in C++.

The second part is the part where all the game-specific code goes. It just focuses on getting the job done for the game, not the gory details of how to bind C# to C++. It looks like the Stopwatch code above.

So far everything has been in one C# file and one C++ file. Instead, we’ll split up the project this way:

This way the NativeScript directory can simply be dropped into a Unity project or updated. The game never modifies anything in there. It does, however, require the game to provide a few things. First, the game must define a NativeConstants class in the global namespace with a few required fields:

Second, NativeScriptConstants.ExposedTypesJsonPath needs to point to a JSON file describing all the .NET types to expose. This is the same kind of JSON file as we talked about above.

Third, the game’s C++ code must define two functions:

// Called when the plugin is initializedvoid PluginMain(){}// Called for MonoBehaviour.Updatevoid PluginUpdate(){}

// Called when the plugin is initialized
void PluginMain()
{
}
// Called for MonoBehaviour.Update
void PluginUpdate()
{
}

That’s it! With those simple requirements out of the way, any Unity project can start writing scripts in C++.

At this point there are multiple files, a directory structure, and a sizable code generator involved so there’s too much to post right here in the article. So I’ve created a GitHub project which you can check out if you want to see the details or try out some C++ scripting for yourself.

The last major missing piece of this project is a system to easily build the C++ plugin for a variety of platforms: Windows, Mac, iOS, Android, etc. That will be the subject of next week’s article.