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
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:
- Comment the line
android.aapt2FromMavenOverride=
inunity/builds/android/gradle.properties
file. - In the
unity/builds/android/launcher/build.gradle
file and comment the linendkPath “/…
. - In the
unity/builds/android/unityLibrary/build.gradle
file and comment out it’sndkPath “/…
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:
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. Dopod 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 infobuilds/ios
folder clean and run the project again.