Integrating Unity code into React Native

Hi all! The dev.family team is in touch again with a very unusual topic. This time we'll talk about games. Namely, how to integrate Unity into React Native.

In fact, it is obvious that you cannot write a game in React Native. It's not necessary. There are a huge variety of engines that allow you to develop games for different platforms and operating systems, be it iOS or Android, macOS or Windows. Among them are Unity and Unreal Engine. Today we'll look at how to use the first one in cross-platform mobile applications.

I don’t think it’s worth saying that making a full-fledged game in Unity is more convenient. Why then is this strange and incomprehensible integration needed at all?

Let's start with the fact that games can have an extensive menu and various functionality. Let's say a built-in messenger, large settings, the same guilds within the game, etc. We are not talking about AAA games like Genshin, but rather about small indies or similar ones, where the entire game can be made into a separate application, and the mechanics and scenes themselves can be transferred to the unit. Unity is also good not only in creating games, but also in using technologies such as VR or AR, where you can use scenes, various models, collisions of these models, and more. Here the situation is similar to the one where we have a separate application and we make the transition to the screen with a Unity project.

Initializing a React Native Project

To initialize a project in React Native, enter the command in the directory where we want to create the project: npx react-native init unityapp (This project uses react-native version 0.73)

We get a standard initial application on react-native

Then we’ll add a couple of libraries for navigation; we only need them to move to another screen. In our case, it’s a screen from Unity.

yarn add @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context

Then cd ios && pod install && cd .. for iOS and Android, following the react-navigation documentation, add this code to MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(null)
  }

And add import on top:

import android.os.Bundle;

Then we will add two screens to move from one to another, and the navigator itself (this code is just an example of how the libraries work). Thus, we get two screens: one – the application itself and the second – where our unit project will be. The following result is obtained:

Actually, we are done with preparing our application. Let's move on to the second part. Let's create a project in Unity.

Initializing a Unity project

First, let’s create the project itself in Unity, if you don’t have a ready-made one. Before doing this, install Unity Hub on your computer. Instructions can be found Here. There are installers for all platforms. After that, select and install the LTS version as it is the supported version. But you most likely understood this anyway.

Now let's create the Unity project itself:

In this article we will look at everything using the example of a 2D game – a clone of Flappy Bird. That is, now we will create a 2D project and directly on mobile platforms (in fact, this does not affect anything):

Thus, we get the project and immediately open it.

Here you see a completed project. But you didn't miss anything. We decided not to waste time describing everything that was done in the unit itself, because… this is not relevant to our topic.

You can see, download or criticize all the code yourself if you follow the link (links are below) and download the project for yourself. Or if you want to repeat the following steps yourself.

Actually, here is our mini-game in Unity. Now we need to move it to the mobile application, and at the end of the game, transfer our result, write it down in the application and display it in the list with the results. Let's get started

Integrating Unity into React Native

React Native part I

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

yarn add @azesmway/react-native-unity

Then we run the command to install pods for iOS:

cd ios && pod install

IMPORTANT: before working further with Android, run yarn run android so that the local.properties file appears (it stores the path to the android sdk). This will be needed later to run the application when the project is installed on Unity.

Unity Android

I'll start with the bad news: we won't be able to support Hot Reload with the Unity project. I’ll explain why: in order to integrate a game into a React Native project, you need to collect builds of the game itself and then transfer it to us. As you understand, builds are the reason that deprives us of any possibility of Hot Reload. Let's move on to the assembly.

Let's start with the Android platform.

First, let’s open the assembly settings itself (it’s a pity that you can’t do everything with one command, as in the usual JS, but we have what we have).

Let's go to File > Build Settings

Next, as you can see in the picture, in point 4 we go to Player Settings.Expanding the section Other Settings and uncheck the Auto Graphics API. After this, under Graphics API add OpenGLES3 & OpenGLES2 (don’t look at the fact that it says deprecated), clicking on the icon Plus. Settings Color Gamut you can make it your own (or leave it as is, because you’re too lazy to figure it out 😉 ).

After this we go even lower to the taba Configuration. There is a field value Scripting Backend change from Mono on IL2CPP. This is necessary so that new options appear below Target Architectures. There we select all the values ​​(for what? Just in case! Just kidding) we need arm for mobile platforms. Let’s leave x86_64, since previously mobile smartphones had processors of this architecture).

This completes the configuration of the build for Android. We can move on to assembly and integration:

To begin, create a directory in the root of our project unity/builds/android . Our assemblies for iOS and Android applications will be stored in this folder.

Then click on the Export button and select the newly created folder, export the build for Android.

Then go to the Android folder of our application and do the following: 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>

Next, go to our created folder unity/builds/android, then to unityLibrary/src/main/AndroidManifest.xml and in this file we delete the tag and what is in it. This may cause problems starting Android.

Thus, the preparations are completed, we can add UnityView to our application.

Go to App.tsx and replace View with UnityView

const UnityScreen = () => {
  return ;
};

And don’t forget about importing UnityView itself, otherwise you never know ;).

The output is the following:

Problems we encountered (Android)

When integrating a Unity project into React Native, even though everything was according to the instructions, we encountered the following problems/errors:

• We had the mobilenotifications.androidlib package installed and it caused an error when launched: unityLibrary could not find it during assembly. This can be fixed by commenting out the implementation project('mobilenotifications.androidlib') line in unityLibrary/build.gradle. In fact, it could be cut out altogether, but the decision was made: if it works, don’t touch it. That's why they left him for now.

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// implementation project('mobilenotifications.androidlib') - вот эту
}

• Missing compileSdkVersion in build.gradle. We corrected this this way: we replaced compileSdkVersion with the same one as android/build.gradle in the project root and added compileSdkVersion version to unityLibrary/build.gradle in 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 // вот здесь
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 check that targetSdkVersion matches what is in android/build.gradle 😉

As a result, we have a working application written in React Native, which contains a game written in Unity. But this is only on Android for now. Now let's move on to iOS.

Unity iOS

Honestly, for us this was the most difficult part, although it was the shortest. Next we will explain why. So let's get started.

Probably, the part with Libraries/Plugins/iOS took the most time, since if you follow the instructions of the library itself, you need to change the UnityFramework target. But during the build, when going to the Libraries folder, no Plugins folder was found. On github issues we found the answer that you need to add the folder yourself and place two files NativeCallProxy.m & NativeCallProxy.h there (they can be taken from the repository with the Unity code). Now add these two files to the Assets > Plugins/iOS folder

After this you need to assemble the build. Let's go again to Unity > File > Build Settings. Here we choose iOS and press the button Switch Platform (essentially the same as in the case of Android).

Next we go to the window Player Settings. Here we indicate the Bundle Identifier as for our application, then the Signing Team ID (if we are doing a sign, this is not necessary). And see that the Scripting Backend is installed IL2CPP.

Next in Build Settings press the button Build and select the location of our build; for convenience, we have collected it in [root_project]/ios/unityBuildso as not to lose it ;), but this is not necessary since we do not need the entire build in the code base.

After we have assembled the build, we open in Xcode Unity-iPhone.xcodeproj and do the following:

• Go to Libraries/Plugins/iOS and select NativeCallProxy.h. In UnityFramework's Target Membership we change from Project to Public.

• Select the Data folder and change Target Membership to UnityFramework.

• Next, if required, select the development team and assemble UnityFramework (assembly is required!!!).

• After the build has been assembled, copy our UnityFramework to [root_project]/unity/builds/ios

• Finally we execute the commandrm -rf ios/Pods && rm -f ios/Podfile.lock && npx pod-install

After all of the above, we assemble our application on iOS. Please note that UnityView will work correctly on a real iOS device, unlike Android.

End result

Conclusion

So we looked at how the integration of a Unity project into a mobile application on React Native works. In fact, we understand that the task is very non-trivial, and not all projects may need this. However, as we said in the introductory part, such examples do occur and have a right to life.

In the article, we looked step by step at how this integration works. If the topic turns out to be relevant and gains a lot of coverage, we will write the next one, in which we will improve our integration to transfer data from react-native to Unity and vice versa. Or you can try to do it yourself by following instructions from the library documentation.

The dev.family team was with you and see you again 😉

Links

Repositorywhere you can view the mobile application code

Repositorywhere can I get the game code

Library

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *