Embed Unity 3d games into your react-native mobile apps

React Native + Unity 3D Tutorial

Create a Unity 3D and react-native hybrid game-embedded mobile app

Chaudhry Talha 🇵🇸
11 min readNov 30, 2024

React Native + Unity 3D type of hybrid can be useful in many ways.

A use-case: — The e-commerce platforms have sales going on all the time. They can embed mini-games in the app that hook users up into shopping on their app. Lazada in Malaysia has done a similar thing named “Lazland” in which you grow crops and on a harvest, you get a real bag of rice.

Just like that, you can think of ways to engage users. This article is structured as:

Setup
— Exporting Unity Game for React-Native

— — Android
— — iOS
Unity exports configurations
— — Android
— — iOS
Running the react-native app with UnityView
— — Android
— — iOS
Sending a message from react-native to unity

Setup

My react-native version is v0.75.4. The React-Native library that we’ll need is azesmway/react-native-unity. Install the v1.0.7 of this library:

yarn add @azesmway/react-native-unity@1.0.7

We’ll do pod install later after adding the unity project in the react-native iOS folder.

Below is how your react-native project should be. Create unity/builds/android and ios folders in which you’ll keep the unity’s ios and Android builds.

.
├── android
├── ios
├── unity
│ └── builds
└── android
└── ios
├── node_modules
├── package.json
└── README.md

Exporting Unity Game for React-Native:

Assuming you have a unity project ready. This will show you how to export it to be used in react-native.

If you want to see a sample Unity code example, please scroll to the end of this article and under Side Notes you can find Sample Unity Code Example you can refer to that.

Android

In Unity navigate to File > Build Settings

As per step 4, navigate to Player Settings…

Expand the Other Settings section and uncheck the Auto Graphics API box. Then, under Graphics APIs, add OpenGLES3 and OpenGLES2 (ignore the “deprecated” warning), by clicking on the plus (+) icon.

You can also adjust the Color Gamut settings according to your preferences (optional).

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 under it; Your android project should look similar to:

That’s it for the export part. We’ll see later what code change we need to do.

iOS:

Go to Unity > File > Build Settings 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 and the Signing Team ID (if signing is required; otherwise, it’s optional). Also, ensure that the Scripting Backend is set to IL2CPP.

After that, in Build Settings, click on the Build button and choose the location for our build.

Once we’ve built the project, open Unity-iPhone.xcodeproj in Xcode and perform the following actions:

Let’s do the XCode-related setting. We’ll need to export the UnityFramework for iOS in our react-native project.

Below is how your iOS export should be at this moment:

That’s all for the export part of the iOS project from Unity 3D.

Unity exports configurations

We’ll make all the configurations needed for iOS and Android to make it work with react-native.

Android

Copy all the files in the Android export of Unity to /unity/builds/android

Next open, /unity/builds/android/unityLibrary/src/main/AndroidManifest.xml and comment on the entire <intent-filter>…</intent-filter>

If you’re using expo, you’re done. The built-in expo plugin will handle the rest. If you’re not using expo, you’ll need to follow the steps below.

Add the following lines to /android/settings.gradle:

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

Next in android/build.gradle add:

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

Next in android/gradle.properties add:

unityStreamingAssets=.unity3d

Finally, add the line of code below to android/app/src/main/res/values/strings.xml

<string name="game_view_content_description">Game view</string>

That’s all the changes we need to make for Android. At this stage, you should be able to Clean your react-native Android project or build Gradle again successfully, but the building will have some errors. We’ll resolve these errors in the next section when we run the app.

iOS

We don’t need to copy the entire Unity iOS project into our react-native side but first, we’ll need to do some configuration to Unity’s iOS export. Open the Unity iOS project’s Unity-iPhone.xcodeproj file in XCode:

First, select UnityFramework as the default scheme:

Select Data folder and set a checkbox in the Target Membership section to UnityFramework

Click NativeCallProxy.h inside the Unity-iPhone/Libraries/Plugins/iOS folder of the Unity-iPhone project and change UnityFramework in it’s Target Membership from Project to Public.

If you’re running on an iOS physical device you’ll need a paid Apple developer account. Remember that the unity framework will not work on the iOS simulator. So, the step below is completely optional

Press cmd + B or from the XCode menu select Product > Build

This build should succeed. One change you’ll notice in the XCode project navigator pane the UnityFramework under Products is no longer in red, right-click on it and select Show in Finder

This UnityFramework.framework folder is what we need to copy in /unity/builds/ios folder:

No need to build right now. We’ll cover that part in the Running section for iOS.

Running the react-native app with UnityView

Now, we’ll build and run our Android and iOS builds that’ll have the unity embedded. Let’s first create a screen where we’re going to show unity inside the react-native project. So, create a new file in your react-native project named UnityScreen.tsx or anything you want to name on the screen.

import React, {useRef} from 'react';

import UnityView from '@azesmway/react-native-unity';
import {View} from 'react-native';

const UnityScreen = () => {
const unityRef = useRef<UnityView>(null);

return (
<View style={{flex: 1}}>
<UnityView
ref={unityRef}
style={{flex: 1}}
onUnityMessage={result => {
console.log('onUnityMessage', result.nativeEvent.message);
}}
/>
</View>
);
};

export default UnityScreen;

If the styles has an error, leave it as it is.

iOS

We’ll need to purge the pods when we add a Unity build. What it means is that you’ll need to run the command below from your react-native project root.

rm -rf ios/Pods && rm -f ios/Podfile.lock && npx pod-install

It will delete the currently installed pods and then install them again. Depending on what you have in your project the pods will be reinstalled, but for this tutorial, we’re interested in seeing Installing react-native-unity (1.0.7) in green when we run the above command.

Now open your <YOUR_RN_APP>.xcworkspace to clean and cmd + b the project. The build should be successful, now you can press the Play button (Only if you can test on a physical device) and the unity view should run on iOS. For me, it looks like this below:

Android

For Android, you need to ensure two things. In my case, a friend of mine who is a Unity developer was giving me the exports. So the Android SDK paths were set as per his system. So, when I got the build that’s the first thing that I cleaned. If you don’t then you might face errors like:

> Task :unityLibrary:BuildIl2CppTask FAILED
[CXX1101] Location specified by android.ndkPath (/Applications/Unity/Hub/Editor/2022.3.52f1/PlaybackEngines/AndroidPlayer/NDK) did not contain a valid NDK and couldn't be used

Build file '/MyApp/unity/builds/android/unityLibrary/build.gradle' line: 73
Execution failed for task ':unityLibrary:BuildIl2CppTask'.
> NDK is not installed

To solve this search for /Applications/Unity/Hub/Editor or whichever NDK path is mentioned in your project Location specified by android.ndkPath. you’ll find all the references to it in the unity/builds/android folder:

Below is what you need to replace in each of these three files:

  1. Comment the line android.aapt2FromMavenOverride= in unity/builds/android/gradle.properties file.
  2. In the unity/builds/android/launcher/build.gradle file and comment the line ndkPath “/….
  3. In the unity/builds/android/unityLibrary/build.gradle file and comment out it’s ndkPath “/… line.

Here are the screenshots of the code and how it looks now.

The second and last thing that we need to do to run the app for Android requires a one-time wait of about 20 minutes for Android to build everything. For some reason on Mac, there is a security setting that even if it is set to App Store and identified developers we’ll need to manually allow files every time it asks.

At this point, I recorded instead of taking screenshots because this step confused me a lot when I was doing it. So, everything I did in this video, do those and then continue.

Here is how it looks on my Android physical device:

Sending a message from react-native to unity

You can send messages from react-native to unity and vice versa. One thing to consider in that is:

You can only pass String.

This means if you want to pass some JSON data, you need to stringify and parse it at Unity 3d’s end.

On the react-native side, you can add buttons and views on top of the UnityView at absolute positions. For example, my UnityScreen.tsx right now is:

import React, {useRef} from 'react';

import UnityView from '@azesmway/react-native-unity';
import {Button, View} from 'react-native';

const UnityScreen = () => {
const unityRef = useRef<UnityView>(null);

return (
<View style={{flex: 1}}>
<UnityView
ref={unityRef}
style={{flex: 1}}
onUnityMessage={result => {
console.log('onUnityMessage', result.nativeEvent.message);
}}
/>
<View style={{position: 'absolute', top: 50}}>
<Button
title="My Unity Action"
onPress={() => {
unityRef.current?.postMessage(
'GAME_OBJECT_NAME',
'FUNCTION_NAME',
'STRINGIFY_ARGUMENTS',
);
}}
/>
</View>
</View>
);
};

export default UnityScreen;

You need to ask the unity developer what is your game object name, the function you need to call, and the arguments either string or stringify JSON is sent and this data will execute some action on the Unity side it can be used to play a specific animation, to jump, or anything that can be done in Unity 3D.

If you take the Sample Unity Code Example shared below as a reference the unityRef.current?.postMessage would be:


const unityData = {
name: "I'm Stepa",
age: 25,
};

const jsonedData = JSON.stringify(unityData);

unityRef.current?.postMessage(
'ReactToUnity',
'GetDatas',
jsonedData,
);

This wraps up the complete integration of Unity 3d + React-Native. Excluding the unity project I have the Github project up: https://github.com/thechaudharysab/ReactUnityPOC

Here is a real application that is developed using the same approach that you have learned in this article:

https://play.google.com/store/apps/details?id=com.ibjects.pailo

If you have any questions or are stuck on a step feel free to connect on LinkedIn and ask.

If you find this article useful do press 👏👏👏 so that others can also find this article.

Like to support? Scan the QR to go to my BuyMeACoffee profile:

https://www.buymeacoffee.com/chaudhrytalha

Side Notes

Here are some side notes,

Sample Unity Code Example

Create an empty GameObject and name it ReactToUnity.

Here is the code for a Sample.cs file:

using UnityEngine;
goo
public class DataFromReact : MonoBehaviour
{
public string name;
public string age

private void Awake()
{
if (instance == null)
{
instance = this;
}
}

public class JsonObject
{
public string name;
public string age;
}
// As you can see here is the name of the function that we get the data.
// it should have the same name in RN function postMessage.
public void GetDatas(string json)
{
JsonObject obj = JsonUtility.FromJson<JsonObject>(json);
name = obj.name;
age = obj.age;
}
}

Possible Errors

Problem #1:

If during the release build you face app:checkReleaseAarMetadata ERROR, make sure in /unity/builds/android/unityLibrary/build.gradle and /unity/builds/android/launcher/build.gradle both’s compileSdkVersion is the same as your react-native project android/build.gradle's compileSdkVersion

Problem #2:

On iOS if you see something like:

The sendMessageToMobileApp is my custom function name that I use to communicate between my react-native and unity projects.

I assumed you already have made UnityFrameworrk public and the other steps defined above in your.xcodeproj.

I had to add sendMessageToMobileApp in NativeCallProxy.h & NativeCallProxy.mm as the highlighted code shows in the below screenshots:

Problem#3 UNTESTED SOLUTION

'UnityFramework/UnityFramework.h' file not found

First, ensure you’re running it on a physical device with no iOS simulator.

ONLY If the error still persists:

  • Method 1: Remove ios/Pods folder and the pods lock file. Do pod install again clean and run the project again.
  • Method 2: If method 1 doesn’t work → Open the Unity-iPhone.xcodeproj from the Unity iOS build and build the unity framework again. Copy the latest info builds/ios folder clean and run the project again.

--

--

Responses (1)