Tích hợp Unity Library với React Native
1. Chuẩn bị app React Native
B1: Tạo một app react native demo
npx @react-native-community/cli init "<react-native-project>"
cd "<react-native-project>"B2: Cài đặt lib tích hợp 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 iOSB3: Cài đặt các lib để phục vụ demo
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-stackB4: Tạo các screens demo.
mkdir -p screens- Tạo thêm
HomeScreen.jsvàUnityScreen.jsvào thư mục<react-native-project>/screensvới nội dung sau:
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>
)
}- Sửa nội dung
App.tsxthành:
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. Tích hợp lib Unity với Project React Native Android
Lưu ý: Lib được sử dụng tích hợp không hoàn chỉnh, cần phải có một số sửa đổi trên lib gốc để tích hợp được. Các sửa đổi sẽ được note chi tiết theo tài liệu
B1: Copy file thư mục unityLibrary vào thư mục unity/build/android
Mở file node_modules/@azesmway/react-native-unity/android/build.gradle comment dòng 111 của file
Nguyên nhân là lib chưa support tích hợp Unity bằng file AAR, file trong dòng comment đã được build vào AAR ở phase build
// implementation files("${project(':unityLibrary').projectDir}/libs/unity-classes.jar")Mở file node_modules/@azesmway/react-native-unity/android/src/main/java/com/azesmwayreactnativeunity/ReactNativeUnityViewManager.java sửa lại function pauseUnity:
@Override
public void pauseUnity(ReactNativeUnityView view, boolean pause) {
if (isUnityReady()) {
assert getPlayer() != null;
if (pause) {
getPlayer().pause();
} else {
getPlayer().resume();
}
}
}B2: Sửa một số file trong thư mục <react-native-project/android>
---------------------------------------------------------------
---------------- "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>B3: Mở file node_modules/@azesmway/react-native-unity/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java
Sửa nội dung function requestFrame trong file thành:
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;
}
}
}B4: Build project android thành 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. Tích hợp lib Unity với Project React Native iOS
Lưu ý: Lib được sử dụng tích hợp không hoàn chỉnh, cần phải có một số sửa đổi trên lib gốc để tích hợp được. Các sửa đổi sẽ được note chi tiết theo tài liệu
B1: Copy UnityFramework.framework vào thư mục unity/builds/ios
Mở file node_modules/@azesmway/react-native-unity/react-native-unity.podspec, thêm các frameworks cần thiết vào project.
Nguyên nhân là một số third-party viết thành framework riêng, trên project sinh bởi Unity đã link sẵn, tuy nhiên trên project ReactNative chưa có sẵn
s.vendored_frameworks = [
"ios/UnityFramework.xcframework",
"ios/NSR.xcframework",
"ios/StcCorder.xcframework"
]Sau đó build lại podfile (bước này cần thiết do đã sửa podspec của lib trên)
cd "<react-native-project>"
rm -rf ios/Pods && rm -f ios/Podfile.lock && npx pod-installB2: Thêm các privacy cho app để sử dụng camera, location, microphone, … vào 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>B3: Mở file workspace trong folder ios bằng XCode
-
kết nối máy iPhone vật lý
-
chọn signing team (bước này dễ quên nên nhắc trong docs luôn)
-
chọn Build sang Release (
Product > Scheme > Edit Scheme > Build Configuration trong thẻ Info > chọn Release) -
Build app (đoạn này nên thành công và app được cài vào máy iPhone vật lý)