Integrate Unity Library with React Native
1. Prepare the React Native Application
1. Create a React Native Demo Project
npx @react-native-community/cli init "<react-native-project>"
cd "<react-native-project>"2. Install react-native-unity
npm install @azesmway/react-native-unity@latest
mkdir -p unity/builds/android # Tạo thư mục Android
mkdir -p unity/builds/ios # Tạo thư mục iOS3. Dependencies for Demo Navigation
npm install @react-navigation/native
npm install react-native-screens react-native-safe-area-context react-native-gesture-handler react-native-reanimated react-native-vector-icons
npm install @react-navigation/native-stack4. Create Demo Screens
mkdir -p screens- Create
HomeScreen.jsandUnityScreen.jsinside<react-native-project>/screens:
HomeScreen.js
import React from 'react';
import { SafeAreaView, View, Text, Button, StyleSheet } from 'react-native';
export default function HomeScreen({ navigation }) {
return (
<SafeAreaView style={styles.container}>
<View style={styles.buttonContainer}>
<Button
title='Go to AR'
onPress={() => navigation.navigate('Unity', {
link: "https://xensear.vn/sunland/homescreen"
})}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
buttonContainer: {
marginTop: 20,
alignItems: 'center',
},
});UnityScreen.js
import UnityView from '@azesmway/react-native-unity'
import { React, useRef, useCallback, useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useFocusEffect } from '@react-navigation/native';
export default function UnityScreen({ navigation, route }) {
const unityRef = useRef();
const { link } = route.params;
useEffect(() => {
if (unityRef.current) {
let message = JSON.stringify({
type: "setPlatformType",
message: "reactNative"
})
// @ts-ignore
unityRef.current.postMessage("ReactNativeCommandHandler", "Handle", message)
}
}, []);
function HandleUnityMessage(message) {
console.log("Handle message", message);
if(message.includes("unityReady") || message.includes("backToHomeScreen")) {
if (unityRef.current) {
//message set scene
let message = JSON.stringify({
type: "updateHomeScreenUrl",
message: link
})
// @ts-ignore
unityRef.current.postMessage("ReactNativeCommandHandler", "Handle", message)
}
} else if (message.includes("getNativeParameters")) {
} else if (message.includes("backToNativeView")) {
if (unityRef.current) {
// @ts-ignore
unityRef.current.unloadUnity()
navigation.goBack()
}
}
}
return (
<View style={{ flex: 1}}>
<UnityView
// @ts-ignore
ref={unityRef}
style={{ flex: 1 }}
onUnityMessage={(result) => HandleUnityMessage(result.nativeEvent.message)}
fullScreen={false}
androidKeepPlayerMounted={false}
/>
</View>
)
}- Modify
App.tsx:
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomeScreen from './screens/HomeScreen';
import UnityScreen from './screens/UnityScreen';
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName='Home' screenOptions={{ headerShown: false }}>
<Stack.Screen name='Home' component={HomeScreen}/>
<Stack.Screen name='Unity' component={UnityScreen}/>
</Stack.Navigator>
</NavigationContainer>
)
}2. Integrate Unity Library into React Native Android
⚠️ The @azesmway/react-native-unity library does not fully support Unity AAR-based integration. Manual modifications to the original library are required. All required changes are documented below.
1. Copy unityLibrary
Copy the unityLibrary folder into: unity/build/android
Open file node_modules/@azesmway/react-native-unity/android/build.gradle comment line number 111
ℹ️ The library does not properly support Unity integration via AAR. The unity-classes.jar referenced in this line is already bundled inside the generated AAR during the Unity build phase. Keeping this line may cause duplicate class conflicts during Gradle build.
// implementation files("${project(':unityLibrary').projectDir}/libs/unity-classes.jar")Open file node_modules/@azesmway/react-native-unity/android/src/main/java/com/azesmwayreactnativeunity/ReactNativeUnityViewManager.java and edit function pauseUnity:
@Override
public void pauseUnity(ReactNativeUnityView view, boolean pause) {
if (isUnityReady()) {
assert getPlayer() != null;
if (pause) {
getPlayer().pause();
} else {
getPlayer().resume();
}
}
}2. Modify the following files inside the <react-native-project/android> directory
---------------------------------------------------------------
---------------- "android/gradle.properties" ------------------
---------------------------------------------------------------
# add this line
android.minSdkVersion=26
---------------------------------------------------------------
----------------- "android/settings.gradle" -------------------
---------------------------------------------------------------
# add this line
include ':unityLibrary'
project(':unityLibrary').projectDir=new File('..\\unity\\builds\\android\\unityLibrary')
---------------------------------------------------------------
-------------- "android/android/app/build.gradle" -------------
---------------------------------------------------------------
dependencies {
// add this line
implementation fileTree(dir: "${project(':unityLibrary').projectDir}/libs", include: '*.aar')
}
---------------------------------------------------------------
-------- "android/app/src/main/res/values/strings.xml" --------
---------------------------------------------------------------
// add this line inside resource block
<string name="game_view_content_description">GameView</string>3. Open file node_modules/@azesmway/react-native-unity/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java
Edit function requestFrame:
public FrameLayout requestFrame() {
try {
//Attempt to invoke getFrameLayout() for the newer UnityPlayer class
Method getFrameLayout = unityPlayer.getClass().getMethod("getFrameLayout");
return (FrameLayout) getFrameLayout.invoke(unityPlayer);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
// If it is old UnityPlayer, use isInstance() and cast() to bypass incompatible type checks when compiling using newer versions of UnityPlayer
if (FrameLayout.class.isInstance(unityPlayer)) {
return FrameLayout.class.cast(unityPlayer);
} else {
return null;
}
}
}4. Build Android APK
# JAVA_HOME in Android Studio location is <android-studio-folder>/jbr
export JAVA_HOME="<java-home-folder>"
# ANDROID_HOME in Android Studio location can be copied from SDK Manager
export ANDROID_HOME="<android-sdk-folder>"
cd "<react-native-project>/android"
./gradlew assembleRelease
# connect to device
adb reconnect
# install to device
adb install "<react-native-project>/android/app/build/outputs/apk/release/app-release.apk"3. Integrate Unity Library into React Native iOS
⚠️ The @azesmway/react-native-unity library does not fully support Unity Framework integration by default. Manual modifications to the original library configuration are required for proper integration. All necessary changes are documented below.
1. Copy UnityFramework.framework to unity/builds/ios
Open file node_modules/@azesmway/react-native-unity/react-native-unity.podspec, add frameworks into project.
ℹ️ Some third-party dependencies are distributed as standalone frameworks.
In Unity-generated Xcode projects, these frameworks are automatically linked during export.
However, in a React Native iOS project, these frameworks are not linked by default and must be explicitly declared (e.g., in the Podspec) to ensure successful compilation and runtime execution.
s.vendored_frameworks = [
"ios/UnityFramework.xcframework",
"ios/NSR.xcframework",
"ios/StcCorder.xcframework"
]Build podfile
⚠️ This step is mandatory because the Podspec inside node_modules was modified. Without reinstalling Pods, the new frameworks will not be linked correctly.
cd "<react-native-project>"
rm -rf ios/Pods && rm -f ios/Podfile.lock && npx pod-install2. Add privacy configs in order to use camera, location, microphone, … to file <react-native-project>/ios/<app-name>/info.plist
<key>NSCameraUsageDescription</key>
<string>Allow Expo experiences to use your camera</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Allow Expo experiences to use your location</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allow Expo experiences to access your microphone</string>
<key>NSMotionUsageDescription</key>
<string>Allow Expo experiences to access your device's accelerometer</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Give Expo experiences permission to save photos</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Give Expo experiences permission to access your photos</string>3. Open workspace file inside ios folder using XCode
-
Connect Physical Device
-
Choose signing team
-
Set Build Configuration to
Release(Product > Scheme > Edit Scheme > Build Configuration in Info > chọn Release) -
Build app (Select the connected physical device as the run target)