The series continues today to fill a small, but important gap in the C++ plugin’s build script. Unity helpfully provides symbols like UNITY_IOS for us to check with #if. This lets us add and remove blocks of code that should only be present in a certain build of the game. We’d like the same functionality in C++ that Unity provides to C#, so today we’ll upgrade the build script to allow that.

Table of Contents

Unity automatically specifies the correct symbols for the current build target. This lets us tell the compiler what code to compile on which platform:

// C#
void HandleInput()
{
	#if UNITY_EDITOR
		// Handle keyboard and mouse input
	#elif UNITY_IOS || UNITY_ANDROID
		// Handle touch screen input
	#endif
}

These symbols are only defined by Unity when it builds C# code that’s directly placed in the Assets directory. If we’re building a .NET DLL on our own, we’ll have to specify the symbols for ourselves. The same goes for building plugins like we’re doing in this series so that we can script in C++.

C++ has a similar system to C#’s #if preprocessor directives. It’s actually much more powerful as we can use it to define macros, get line numbers, and much more. For now, we’ll just use it to set boolean flags like in C#. The code is very similar in C++:

// C++
void HandleInput()
{
	#if defined(UNITY_EDITOR)
		// Handle keyboard and mouse input
	#elif defined(UNITY_IOS) || defined(UNITY_ANDROID)
		// Handle touch screen input
	#endif
}

Sometimes using #ifdef X is more convenient than #if defined(X). See here for the full list of what we can do.

Remember from part six of the series that we’re using CMake to build our C++ plugin. This actually makes it quite easy to detect which platform we’re building for and define the appropriate symbols.

For now, we’ll support five platforms:

  • Windows (editor and standalone)
  • macOS (editor and standalone)
  • Linux (editor and standalone)
  • iOS
  • Android

Unity supports a lot more platforms and we’ll see how easy they would be to add support for as we go. We’ll start with these five for now.

CMake has the familiar concepts of if (X), else(), elseif (X), and endif(). With these we can check variables that help us understand what environment we’re building project files for. For example, CMake defines a WIN32 boolean variable that we can use to detect Windows. It also defines a CMAKE_SYSTEM_NAME string variable that we can use to check for macOS (“Darwin”) or Linux.

That takes care of three platforms, so we just need to cover Android and iOS. Recall that building for Android requires us to call CMake with the NDK’s directory as a command-line parameter: -DANDROID_NDK=/path/to/android/ndk. That means we can use if (ANDROID_NDK) to check if we’re building for Android.

Previously, we hadn’t built a CMake project for iOS. This is because we can simply drop C++ files into our project’s Assets directory and Unity will automatically include them in the Xcode project it builds for iOS. That meant we’d use CMake to generate project files for another platform and use that instead. Now that we want to specify UNITY_IOS among other symbols, we need to allow CMake to build for iOS.

Just like with Android, we’ll need a CMake “toolchain” file for iOS. I’ve added one to the Assets directory so we just need to pass -DCMAKE_TOOLCHAIN_FILE=/path/to/Unity/CppSource/iOS.cmake on the command line to CMake. The toolchain file will set the IOS variable, so we just need to check for if (IOS) to determine we’re on that platform.

This change means that the location of the C++ files needs to change. They used to be in the Unity project’s Assets directory, but now that iOS plugins are built via CMake we don’t want Unity to include the C++ source in the Xcode project anymore because we have already built a bundle which Unity will automatically include in the Xcode project. The new location for C++ files is in the Unity project’s CppSource directory.

Finally, there’s the difference between building for the Unity editor and for a standalone build when on Windows, macOS, and Linux. We’ll also add that as an option to the CMake command line so we can simply pass -DEDITOR=TRUE if we want an editor build.

Now we’ve got everything we need to detect which platform we’re building for. The last step is to define the symbols for C++ to use with #if. CMake makes that really easy via a built-in add_definitions function. We simply call add_definitions(-DUNITY_IOS) when appropriate and the symbol will be defined in the generated project files.

With that, let’s go ahead and write a simple if-else block in our CMakeLists.txt script to detect the platform and define the appropriate symbols:

if (EDITOR)
	add_definitions(-DUNITY_EDITOR)
	if (WIN32)
		add_definitions(-DUNITY_EDITOR_WIN)
	elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
		add_definitions(-DUNITY_EDITOR_OSX)
	elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
		add_definitions(-DUNITY_EDITOR_LINUX)
	endif()
else()
	add_definitions(-DUNITY_STANDALONE)
	if (WIN32)
		add_definitions(-DUNITY_STANDALONE_WIN)
	elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
		add_definitions(-DUNITY_STANDALONE_OSX)
	elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
		add_definitions(-DUNITY_STANDALONE_LINUX)
	endif()
endif()
if (IOS)
	add_definitions(-DUNITY_IOS)
endif()
if (ANDROID_NDK)
	add_definitions(-DUNITY_ANDROID)
endif()

Finally for today, let’s discuss how to switch between build targets. Switching platform as usual will still work and switch to using the new platform’s C++ plugin. Unlike C# scripting, the C++ IDE won’t automatically have its build target changed because only the C# project files that Unity generates and we largely ignore are updated. So we’ll need to take a couple of extra steps. Here’s how we change platforms:

  1. In the Unity editor, go to File > Build Settings
  2. Select a platform from the Platforms list
  3. Click the Switch Platform button
  4. Open a command prompt and go to the CMake build directory
  5. Delete all build files: rm -fr * (macOS, Linux) or del *.* (Windows)
  6. Re-run CMake (e.g. cmake -G Xcode /path/to/Unity/CppSource)

That’s all there is to it. Switching platforms should now be almost as easy in C++ as it is in C#!