Integrating Unity code into React Native

Ilya Mazhugin, mobile developer
Ilya Mazhugin, mobile developer
May 8, 2024
16 minutes
Contents
Hello everyone! It's the dev.family team again with a rather unusual topic. This time, let's talk about games. Specifically, how to integrate Unity into React Native.
It's obvious that you can't develop a game on React Native. And you don't need to. There are plenty of game engines that allow you to develop games for various platforms and operating systems, whether it's iOS or Android, macOS or Windows. Among them are Unity and Unreal Engine. Today, we'll take a look at how to use Unity in cross-platform mobile applications.
You might wonder, why bother with this strange and obscure integration if it's easier to make a full-fledged game on Unity?
Well, let's start with the fact that games can have extensive menus and various functionalities. For example, an embedded messenger, extensive settings, in-game guilds, and so on. We're not talking about AAA games like Genshin, but rather small indie games or similar ones, where the entire game can be made into a separate application, and the mechanics and scenes can be implemented in Unity. Additionally, Unity is great not only for game development but also for using technologies like VR or AR, where you can utilize scenes, various models, model collisions, and more. Here's a similar situation to when we have a separate application and transition to a screen with a Unity project.

Initialization of a React Native Project

To initialize a project in React Native, enter the following command in the directory where you want to create the project: npx react-native init unityapp (This project uses react-native version 0.73)
You will receive the standard initial React Native application.
After that, we'll add a couple of libraries for navigation. We only need them to navigate to another screen. In our case, it's the screen with Unity.

yarn add @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context
Then run command cd ios && pod install && cd.. for iOS. For android add this code in MainActivity.kt as done in react-navigation documentation.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(null)
  }
And at the top, add the import statement:

import android.os.Bundle;
After that, we'll add two screens to navigate between them, along with the navigator (this code is just an example of how the libraries work). Therefore, we'll have two screens: one for the application and the other for our Unity project. Here's the result:
Actually, we've finished with the preparation of our application. Let's proceed to the second part. We'll create a project in Unity.

Initialization of the Unity Project

To start, let's create the Unity project if you haven’t already had the one. Make sure you have Unity Hub installed on your computer. You can find the instructions here. There are installers available for all platforms. After all, select and install the LTS version, as it's the supported version. But you have probably already figured that out.
Now, let's create the Unity project:
In this article, we use a 2D game example, a clone of Flappy Bird. So let's create a 2D project on mobile platforms:
As a result, we get the project and immediately open it.
Here you see the ready-made project. But you didn't miss anything. We decided not to spend time on describing everything that was done in Unity, as it's not relevant to our topic.
You can view, download, or critique all the code by following the link (links are located at the bottom) and download the project. Or if you will decide to replicate the following steps by yourself.
Here's our mini-game in Unity. Now we need to move it into the mobile application, and at the end of the game, transfer our result, record it in the application, and display it in the list with results. Let's get started.

Unity integration into React Native

React Native part I

To begin, let's install the library that will allow our application to interact with the game:

yarn add @azesmway/react-native-unity
Then, run the command to install pods for iOS:

cd ios && pod install
IMPORTANT: Before proceeding to work with Android, execute yarn run android to generate the local.properties file (it contains the path to the Android SDK). This will be needed later to run the app once the Unity project is installed.

Unity Android

Let's start with the bad news: We won't be able to support Hot Reload with the Unity project. Here's the explanation: To integrate the game into the React Native project, we need to create a build of the game and then transfer it to our environment. As you can imagine, this process of building is what prevents us from having any Hot Reload capability. Let's move on to the build process.
We'll begin with the Android platform.
First, let's open the build settings (unfortunately, we can't do everything in one command like in traditional JS, but we have what we have).
Navigate to File > Build Settings
Next, according to the image, in step 4, navigate to Player Settings.
Expand the Other Settings section and uncheck the Auto Graphics API box. Then, under Graphics API, add OpenGLES3 & OpenGLES2 (ignore the "deprecated" warning), by clicking on the plus icon. You can adjust the Color Gamut settings according to your preferences (or leave them if you're feeling lazy ;-) ).
After that, scroll down further to the Configuration tab. Change the Scripting Backend field value from Mono to IL2CPP. This is necessary to reveal new options for Target Architectures below. Then, select all the values (why? Just to be safe! Just kidding). We need an arm for mobile platforms. And we'll leave x86_64, as some mobile phones used to have processors with this architecture.
With that, the Android build setup is complete. Now, let's move on to building and integration:
First, in the root of our project, create a directory named unity/builds/android. This folder will store our builds for iOS and Android applications.
Then, click the Export button and select the newly created directory. Export the build for Android.
Next, navigate to the Android folder of our application and do the following:

In android/settings.gradle add

include ':unityLibrary'
project(':unityLibrary').projectDir=new File('..\\unity\\builds\\android\\unityLibrary')
In android/build.gradle

allprojects {
  repositories {
    // this
    flatDir {
        dirs "${project(':unityLibrary').projectDir}/libs"
    }
In android/gradle.properties

unityStreamingAssets=.unity3d
In android/app/src/main/res/values/strings.xml

<string name="game_view_content_description">Game view</string>
Then move to the created folder unity/builds/android, then to unityLibrary/src/main/AndroidManifest.xml and in this file delete the tag <intent-filters> and what it contains. This can cause problems with the Android launch.
Therefore, the preparations are complete. We can now add UnityView to our application.
Navigate to App.tsx and replace View with UnityView.

const UnityScreen = () => {
  return ;
};
And don't forget about importing UnityView itself, just in case ;).
As a result, we get the following:

Challenges we faced with (Android)

During the integration of the Unity project into React Native, despite following the instructions, we faced the following issues/errors:
• We had the mobilenotifications.androidlib package installed, which caused an error during the launch: unityLibrary couldn't find it during the build. This can be fixed by commenting out the line implementation project('mobilenotifications.androidlib') in unityLibrary/build.gradle. In fact, it could be completely removed, but the decision was made: if it works, don't touch it. So, for now, we decided to leave it as is 🙂


dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// implementation project('mobilenotifications.androidlib')
}
Absence of compileSdkVersion in build.gradle:
We fixed this by replacing compileSdkVersion with the same version as in android/build.gradle in the project's root directory. Additionally, we added compileSdkVersion version in unityLibrary/build.gradle under defaultConfig


android {
ndkPath "/Applications/Unity/Hub/Editor/2022.3.16f1/PlaybackEngines/AndroidPlayer/NDK"

compileSdkVersion 34
buildToolsVersion '34.0.0'

compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}

defaultConfig {
compileSdkVersion 34 // here
minSdkVersion 22
targetSdkVersion 33
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
versionCode 1
versionName '1.0.0'
consumerProguardFiles 'proguard-unity.txt'
}
}
• And don't forget to ensure that targetSdkVersion matches with the one in android/build.gradle ;)
As a result, we have a working application written in React Native that includes a game written in Unity. But this is only for Android so far. Now let's move on to iOS.

Unity iOS


Honestly, for us, this was the most challenging part, but also the shortest. Let's explain why and get started.
Probably the most time-consuming part was dealing with Libraries/Plugins/iOS, as following the instructions of the library itself required changing the UnityFramework target. However, during the build process, when we navigated to the Libraries folder, we couldn't find any Plugins folder. We found an answer on GitHub issues that we needed to add the folder and place two files, NativeCallProxy.m & NativeCallProxy.h (which can be obtained from the Unity code repository), into it. Now, let's add to this folder two files to Assets > Plugins/iOS.
After that, we need to build the project. Again, go to Unity > File > Build Settings. Here, select iOS and click on the Switch Platform button ( the same as with Android).

Then, go to the Player Settings window. Here, specify the Bundle Identifier the same as in our application, then the Signing Team ID (if signing is required, otherwise it's optional). Also, ensure that Scripting Backend is set to IL2CPP.
After that, in Build Settings, click on the Build button and choose the location for our build. For convenience, we've gathered it in [root_project]/ios/unityBuild, so we don't lose it ;) However, it's not necessary as we won't need the entire build in our codebase.
Once we've built the project, open Unity-iPhone.xcodeproj in Xcode and perform the following actions:
• Move to Libraries/Plugins/iOS and selectNativeCallProxy.h. In Target Membership in UnityFramework change from Project to Public.
• Select the Data folder and change Target Membership to UnityFramework.
• Next, if necessary, select the development team and build the UnityFramework (building is mandatory!!!).
• After the build is completed, copy our UnityFramework to [root_project]/unity/builds/ios
• In conclusion, execute the command:
rm -rf ios/Pods && rm -f ios/Podfile.lock && npx pod-install
After all the steps mentioned above, we built our application on iOS. Please note that UnityView will work correctly on a real iOS device, unlike Android.

The final result

Summary

So, we've covered how to integrate a Unity project into a mobile app built with React Native. We understand that this task is quite complex, and not all projects may require it. However, as we mentioned in the introduction, such examples do exist.
In this article, we've walked through the integration step by step. If this topic will be popular and gain significant interest, we'll write the next part, in which we'll improve our integration to allow data transfer between React Native and Unity, and vice versa. Or you can try to do it by yourself by following the instructions in the library documentation
It was the dev.family team with you, and see you later ;)

Links

• The repository where you can view the code of the mobile application: here
• The repository where you can find the game code: here
• Link to the library: here