SDKSDKsXenseAR Native SDKReact NativeTích hợp Unity Library với React Native - 1.0.3+

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 iOS

B3: 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-stack

B4: Tạo các screens demo.

mkdir -p screens
  • Tạo thêm HomeScreen.jsUnityScreen.js vào thư mục <react-native-project>/screens vớ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.tsx thà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-install

B2: 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ý)