A library to allow writing Unity scripts in native code: C, C++, assembly.
By using C++ directly, you gain complete control over the code the CPU will execute. It's much easier to generate optimal code with a C++ compiler than with a C# compiler, IL2CPP, and finally a C++ compiler. You can even code with compiler intrinsics or assembly to directly write machine code and take advantage of CPU features like SIMD and hardware AES encryption for massive performance gains.
C++ is also a much larger language than C# and some developers will prefer having more tools at their disposal. Here are a few differences:
- Its template system is much more powerful than C# generics
- There are macros for extreme flexibility by generating code
- Function pointers instead of just delegates
- No-overhead algorithms instead of LINQ
- Bit fields for easy memory savings
- Pointers and never-null references instead of just managed referneces
- Much more. C++ is huge.
There are also some problems with C# code running under Unity that you'll automatically avoid. For one, Unity's garbage collector is very slow. It runs on the main thread, which blocks rendering and input handling. It collects all objects at once, which causes frame hitches. And it fragments memory, so eventually you may run out and crash.
A significant amount of effort is required to work around the GC and the resulting code is difficult to maintain and slow. This includes techniques like object pools, which essentially make memory management manual. You've also got to avoid boxing value types to managed types, foreach loops in some situations, and various other gotchas.
C++ has no required garbage collector and features optional automatic memory management via "smart pointer" types like shared_ptr. It offers an excellent alternative to Unity's primitive garbage collector.
While IL2CPP transforms C# into C++ already, it generates a lot of overhead. There are many surprises if you read through the generated C++. For example, there's overhead for any function using a static variable and an extra two pointers are stored at the beginning of every class. The same goes for all sorts of features such as sizeof(), mandatory null checks, and so forth. Instead, you could write C++ directly and not need to work around IL2CPP.
This project aims to give you a viable alternative to C#. Scripting in C++ isn't right for every project, but now it's an option.
- Supports Windows, macOS, iOS, and Android (editor and standalone)
- Object-oriented API just like in C#
GameObject go;
Transform transform = go.GetTransform();
Vector3 position(1.0f, 2.0f, 3.0f);
transform.SetPosition(position);
- No need to reload the Unity editor when changing C++
- Code generator exposes any C# API (Unity, .NET, custom DLLs) with a simple JSON config file
- Handle
MonoBehaviourmessages in C++
void MyScript::Start()
{
Debug::Log(String("MyScript has started"));
}
- Platform-dependent compilation via the usual flags (e.g.
#if UNITY_EDITOR) - CMake build system sets up any IDE project or command-line build
tl;dr - Most projects will not be noticeably impacted by C++ overhead and many projects will benefit from reducing garbage collection and IL2CPP overhead.
When scripting in C++, C# is used only as a "binding" layer so Unity can call C++ functions and C++ functions can call the Unity API. A code generator is used to generate most of these bindings according to the needs of your project.
All of your code, plus a few bindings, will exist in a single "native" C++ plugin. When you change your C++ code, you'll build this plugin and then play the game in the editor or in a deployed build (e.g. to an Android device). There won't be any C# code for Unity to compile unless you run the code generator, which is infrequent.
The standard C# workflow looks like this:
- Edit C# code in a C# IDE like MonoDevelop
- Switch to the Unity editor window
- Wait for the compile to finish (slow, "real" games take 5+ seconds)
- Run the game
With C++, the workflow looks like this:
- Edit C++ code in a C++ IDE like Xcode or Visual Studio
- Build the C++ plugin (extremely fast, often under 1 second)
- Switch to the Unity editor window. Nothing to compile.
- Run the game
One of the project's goals is to make it just as easy to work with C++ as it is to work with C#, if not easier.
- Download or clone this repo
- Copy everything in
Unity/Assetsdirectory to your Unity project'sAssetsdirectory - Copy the
Unity/CppSourcedirectory to your Unity project directory - Edit
NativeScriptTypes.jsonand specify what parts of the Unity API you want access to from C++. Some examples are provided, but feel free to delete them if you're not using those features. - Edit
Unity/CppSource/Game/Game.cppto create your game. Some example code is provided, but feel free to delete it. You can add more C++ source (.cpp) and header (.h) files here as your game grows.
- Install CMake version 3.6 or greater
- Create a directory for build files. Anywhere is fine.
- Open the Terminal app in
/Applications/Utilities - Execute
cd /path/to/your/build/directory - Execute
cmake -G MyGenerator -DCMAKE_TOOLCHAIN_FILE=/path/to/your/project/CppSource/iOS.cmake /path/to/your/project/CppSource. ReplaceMyGeneratorwith the generator of your choice. To see the options, executecmake --helpand look at the list at the bottom. Common choices include "Unix Makefiles" to build from command line or "Xcode" to use Apple's IDE. - The build scripts or IDE project files are now generated in your build directory
- Build as appropriate for your generator. For example, execute
makeif you choseUnix Makefilesas your generator or openNativeScript.xcodeprojand clickProduct > Buildif you chose Xcode.
- Install CMake version 3.6 or greater
- Create a directory for build files. Anywhere is fine.
- Open the Terminal app in
/Applications/Utilities - Execute
cd /path/to/your/build/directory - Execute
cmake -G "MyGenerator" -DEDITOR=TRUE /path/to/your/project/CppSource. ReplaceMyGeneratorwith the generator of your choice. To see the options, executecmake --helpand look at the list at the bottom. Common choices include "Unix Makefiles" to build from command line or "Xcode" to use Apple's IDE. Remove-DEDITOR=TRUEfor standalone builds. - The build scripts or IDE project files are now generated in your build directory
- Build as appropriate for your generator. For example, execute
makeif you choseUnix Makefilesas your generator or openNativeScript.xcodeprojand clickProduct > Buildif you chose Xcode.
- Install CMake version 3.6 or greater
- Create a directory for build files. Anywhere is fine.
- Open a Command Prompt by clicking the Start button, typing "Command Prompt", then clicking the app
- Execute
cd /path/to/your/build/directory - Execute
cmake -G "Visual Studio VERSION YEAR Win64" -DEDITOR=TRUE /path/to/your/project/CppSource. ReplaceVERSIONandYEARwith the version of Visual Studio you want to use. To see the options, executecmake --helpand look at the list at the bottom. For example, use"Visual Studio 15 2017 Win64for Visual Studio 2017. Any version, including Community, works just fine. Remove-DEDITOR=TRUE` for standalone builds. - The project files are now generated in your build directory
- Open
NativeScript.slnand clickBuild > Build Solution.
- Install CMake version 3.6 or greater
- Create a directory for build files. Anywhere is fine.
- Open a terminal as appropriate for your Linux distribution
- Execute
cd /path/to/your/build/directory - Execute
cmake -G "MyGenerator" -DEDITOR=TRUE /path/to/your/project/CppSource. ReplaceMyGeneratorwith the generator of your choice. To see the options, executecmake --helpand look at the list at the bottom. The most common choice is "Unix Makefiles" to build from command line, but there are IDE options too. Remove-DEDITOR=TRUEfor standalone builds. - The build scripts or IDE project files are now generated in your build directory
- Build as appropriate for your generator. For example, execute
makeif you choseUnix Makefilesas your generator.
- Install CMake version 3.6 or greater
- Create a directory for build files. Anywhere is fine.
- Open a terminal (macOS, Linux) or Command Prompt (Windows)
- Execute
cd /path/to/your/build/directory - Execute
cmake -G MyGenerator -DANDROID_NDK=/path/to/android/ndk /path/to/your/project/CppSource. ReplaceMyGeneratorwith the generator of your choice. To see the options, executecmake --helpand look at the list at the bottom. To make a build for any platform other than Android, omit the-DANDROID_NDK=/path/to/android/ndkpart. - The build scripts or IDE project files are now generated in your build directory
- Build as appropriate for your generator. For example, execute
makeif you choseUnix Makefilesas your generator.
To run the code generator, choose NativeScript > Generate Bindings from the Unity editor.
To configure the code generator, open NativeScriptTypes.json and notice the existing examples. Add on to this file to expose more C# APIs from Unity, .NET, or custom DLLs to your C++ code.
The code generator supports:
- Class types (i.e. Classes with methods, etc. Parameters, etc. are fine.)
- Constructors
- Methods
- Fields
- Properties (getters and setters)
- Generic return types
MonoBehaviourclasses with "message" functions (exceptOnAudioFilterRead)
The code generator does not support (yet):
- Struct types
- Arrays (single- or multi-dimensional)
- Generic functions and types
outandrefparameters- Delegates
MonoBehaviourcontents (e.g. fields) except for "message" functions
The JSON file is laid out as follows:
- Path - Absolute path to the DLL
- Types - Array of types in the DLL to generate
- Name - Name of the type including namespace (e.g.
UnityEngine.GameObject) - Constructors - Array of constructors to generate
- Types - Parameter types of the constructor including namespace
- Methods - Array of methods to generate
- Name - Name of the method
- ParamTypes - Parameter types to the method including namespace
- GenericTypes - Sets of type parameters to generate
- Name - Name of the type parameter (e.g.
T) - Type - Type to generate for the type parameter including namespace
- Name - Name of the type parameter (e.g.
- Properties - Array of property names to generate
- Fields - Array of field names to generate
- Name - Name of the type including namespace (e.g.
- MonoBehaviours
- Name - Name of the
MonoBehaviourclass to generate - Namespace - Namespace to put the
MonoBehaviourclass in - Messages - Array of message names to generate (e.g.
Update)
- Name - Name of the
To update to a new version of this project, overwrite your Unity project's Assets/NativeScript directory with this project's Unity/Assets/NativeScript directory and re-run the code generator.
Articles by the author describing the development of this project.
Please feel free to fork and send pull requests or simply submit an issue for features or bug fixes.
All code is licensed MIT, which means it can usually be easily used in commercial and non-commercial applications.
All writing is licensed CC BY 4.0, which means it can be used as long as attribution is given.