Интеграция Unity кода в React Native

Всем привет! На связи снова команда dev.family с весьма необычной темой. В этот раз поговорим об играх. А именно, как интегрировать Unity в React Native.

На самом деле, это очевидно, что на React Native игру не напишешь. Оно и не надо. Движков, позволяющих разрабатывать игры под разные платформы и операционных системы, будь то iOS или Android, macOS или Windows, — огромное множество. Есть среди Unity и Unreal Engine. Сегодня мы посмотрим, как использовать первый из них в кросс-платформенных мобильных приложениях.

Думаю, не стоит говорить, что сделать полноценно игру на Unity удобнее. Зачем тогда вообще нужна эта странная и непонятная интеграция?

Начнем с того, что игры могут иметь обширное меню и различный функционал. Допустим, встроенный мессенджер, большие настройки, — те же гильдии внутри игры и тд. Речь не идет об AAA играх по типу Genshin, а, скорее, о маленьких инди или подобных, где всю игру можно сделать отдельным приложением, а сами механики и сцены скинуть на юнити. Также юнити хорош не только в создании игр, но и использовании таких технологий как VR или AR, где можно использовать сцены, различные модели, коллизии этих моделей и другое. Тут схожая ситуация с той, где у нас есть отдельное приложение и мы делаем переход на экран с Unity проектом.

Инициализация проекта на React Native

Для инициализации проекта на React Native вводим команду в директории, где хотим создать проект: npx react-native init unityapp (Данный проект использует версию react-native 0.73)

Получаем стандартное начальное приложение на react-native

Интеграция Unity кода в React Native

После чего добавим пару библиотек для навигации, они нам нужны только чтобы переходить на другой экран. В нашем случае, — экран с юнити.

yarn add @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context

Затем cd ios && pod install && cd .. для iOS и Android, следуя документации react-navigation, добавляем этот код в MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(null) }

И сверху добавляем импорт:

После добавим два экрана, чтобы переходить с одного на другой, и сам навигатор (данный код лишь пример работы библиотек). Таким образом получаем два экрана: один — самого приложения и второй, — где будет наш юнити проект. Получается следующий результат:

Интеграция Unity кода в React Native
Интеграция Unity кода в React Native

Собственно, с подготовкой нашего приложения мы закончили. Приступим ко второй част. Создадим проект на юнити.

Инициализация Unity проекта

Для начала создадим сам проект на Unity, если у вас нет готового. Перед этим установите Unity Hub на компьютер. Инструкцию можно найти здесь. Там есть установщики для всех платформ. После этого выберите и установите LTS версию, так как это поддерживаемая версия. Но вы и так, скорее всего, это поняли.

Теперь создадим сам Unity проект:

Интеграция Unity кода в React Native

В данной статье мы будем рассматривать все на примере 2D игры, — клона Flappy Bird. То есть сейчас создадим 2D проект и сразу на мобильные платформы (по факту это ни на что не влияет):

Интеграция Unity кода в React Native

Таким образом, получаем проект и сразу открываем его.

Интеграция Unity кода в React Native

Здесь вы видите уже готовый проект. Но вы ничего не пропустили. Мы решили не тратить время на описание всего, что было сделано в самом юнити, т.к. это не относится к нашей теме.

Весь код вы можете сами посмотреть, скачать или раскритиковать, если перейдете по ссылке (ссылки находятся внизу) и скачаете проект себе. Или если захотите повторить следующие этапы самостоятельно.

Собственно вот наша мини игра на Unity. Сейчас необходимо переместить ее в мобильное приложение, а по окончании игры перенести наш результат, записать его в приложении и вывести в списке с результатами. Приступим

Интеграция Unity в React Native

React Native part I

Для начала давайте поставим библиотеку, которая позволит нашему приложению взаимодействовать с игрой:

yarn add @azesmway/react-native-unity

После выполняем команду для установки подов под iOS:

cd ios && pod install

ВАЖНО: перед тем, как дальше работать с андроидом, выполнить yarn run android, чтобы появился файл local.properties (он хранит в себе путь к android sdk). Это понадобится в дальнейшем для запуска прилаги, когда будет установлен проект на Unity.

Unity Android

Начну с плохой новости: Hot Reload с Unity проектом мы поддерживать не сможем. Объясню почему: чтобы интегрировать игру в React Native проект, нужно собрать билды самой игры и потом перенести к нам. Как вы поняли, билды и есть та причина, которая лишает нас всякой возможности Hot Reload’а. Перейдем к сборке.

Начнем с Android платформы.

Для начал откроем саму настройку сборки (жаль, что нельзя все сделать одной командой, как в привычном JS, но имеем что имеем).

Переходим в File > Build Settings

Интеграция Unity кода в React Native

Дальше, как видно на картинке, в пункте 4 переходим в Player Settings.

Раскрываем раздел Other Settings и убираем галочку с Auto Graphics API. После этого под Graphics API добавляем OpenGLES3 & OpenGLES2 (не смотрим на то, что оно пишет deprecated), нажимая на значок Плюса. Настройки Color Gamut можете сделать под себя (или оставить как есть, потому что лень разбираться ;-) ).

Интеграция Unity кода в React Native

После этого спускаемся еще ниже до таба Configuration. Там значение поля Scripting Backend меняем с Mono на IL2CPP. Это нужно, чтобы ниже появились новые варианты Target Architectures. Там выбираем все значения (для чего? Да на всякий случай! Шутка) arm нам нужны для мобильных платформ. А x86_64 оставим, поскольку раньше мобильные смартфоны был с процессорами этой архитектуры).

Интеграция Unity кода в React Native

На этом настройка билда для андроида закончена. Можем перейти к сборке и интеграции:

Для начале в корне нашего проекта создаем директориюunity/builds/android. В этой папке будут хранится наши сборки для iOS и Android приложений.

После нажимаем на кнопку Export и выбираем только что созданную папку, экспортируем билд для андроида.

Интеграция Unity кода в React Native

Затем переходим в папку Android нашего приложения и делаем следующее:

В android/settings.gradle добавляем

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

В android/build.gradle

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

В android/gradle.properties

unityStreamingAssets=.unity3d

В android/app/src/main/res/values/strings.xml

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

Дальше переходим в нашу созданную папку unity/builds/android, после в unityLibrary/src/main/AndroidManifest.xml и в этом файле удаляем тег <intent-filters> и то что в нем. Это может вызвать проблемы с запуском Android.

Таким образом приготовления закончены, можем добавить UnityView в наше приложение.

Заходим в App.tsx и заменяем View на UnityView

const UnityScreen = () => { return ; };

И не забываем про импорт самого UnityView, а то мало ли ;).

На выходе получаем следующее:

Проблемы, с которыми мы столкнулись (Android)

При интеграции Unity проекта в React Native, хоть все было по инструкции, мы столкнулись со следующими проблемами/ошибками:

  • У нас был установлен пакет mobilenotifications.androidlib и он вызывал ошибку при запуске: unityLibrary при сборке не мог его найти. Это можно исправить, закомментировав строку implementation project('mobilenotifications.androidlib') в unityLibrary/build.gradle. На самом деле, его можно вообще выпилить, но было принято решение : работает - не трогай. Поэтому его пока что оставили.
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // implementation project('mobilenotifications.androidlib') - вот эту }
  • Отсутствие compileSdkVersion в build.gradle. Это мы поправили так: заменили compileSdkVersion на ту же что и android/build.gradle в корне проекта и добавили compileSdkVersion version в unityLibrary/build.gradle в defaultConfig.
android { ndkPath "/Applications/Unity/Hub/Editor/2022.3.16f1/PlaybackEngines/AndroidPlayer/NDK" compileSdkVersion 34 buildToolsVersion '34.0.0' compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } defaultConfig { compileSdkVersion 34 // вот здесь minSdkVersion 22 targetSdkVersion 33 ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } versionCode 1 versionName '1.0.0' consumerProguardFiles 'proguard-unity.txt' } }
  • И не забудьте проверить, чтобы targetSdkVersion совпадал с тем что в android/build.gradle ;)

В итоге, у нас есть рабочее приложение, написанное на React Native, которое имеет в себе игру написанную на Unity. Но это пока что только на Android. Теперь давайте перейдем к iOS.

Unity iOS

Честно говоря, для нас это была самая сложная часть, хоть и самая короткая. Далее объясним почему. Итак, приступим.

Наверное, больше всего времени ушло на часть c Libraries/Plugins/iOS, так как, если следовать инструкции самой библиотеки, нужно поменять таргет UnityFramework. Но во время сборки при переходе в папку Libraries никакой папки Plugins найдено не было. На github issues мы нашли ответ, что папку нужно добавить самим и поместить туда два файла NativeCallProxy.m & NativeCallProxy.h (их можно взять из репозитория с кодом Unity). Теперь добавляем в папку Assets > Plugins/iOS с этими двумя файлами.

После этого нужно собрать билд. Переходим снова в Unity > File > Build Settings. Тут мы выбираем iOS и нажимаем на кнопку Switch Platform (по сути, как и в случае с Android).

Интеграция Unity кода в React Native

Далее переходим в окноPlayer Settings.Тут указываем Bundle Identifier как у нашего приложения, затем Signing Team ID (если делаем sign, это не обязательно). И посмотреть чтобы Scripting Backend стоялIL2CPP.

Далее вBuild Settings нажимаем на кнопку Build и выбираем местоположение нашего билда, для удобства мы собрали его в [root_project]/ios/unityBuild, чтобы его не потерять ;), но это не обязательно так как весь билд в кодовой базе нам не понадобится.

После того как мы собрали билд, открываем в Xcode Unity-iPhone.xcodeproj и выполняем следующие действия:

Интеграция Unity кода в React Native
  • Переходим в Libraries/Plugins/iOS и выбираем NativeCallProxy.h. В Target Membership у UnityFramework меняем с Project на Public.
Интеграция Unity кода в React Native
  • Выбираем папку Data и меняем Target Membership на UnityFramework.
Интеграция Unity кода в React Native
  • Далее, если требуется, выбираем команду разработки и собираем UnityFramework (сборка обязательна!!!).
Интеграция Unity кода в React Native
  • После того, как билд был собран, копируем наш UnityFramework в [root_project]/unity/builds/ios
Интеграция Unity кода в React Native
  • В заключении выполняем команду rm -rf ios/Pods && rm -f ios/Podfile.lock && npx pod-install

После всего вышеперечисленного собираем наше приложение на iOS. Обращаем внимание, что корректно работать UnityView будет именно на реальном iOS устройстве, в отличие от Android.

Конечный результат

iOS
Android

Заключение

Вот мы и разобрали, как устроена интеграция Unity-проекта в мобильное приложение на React Native. На самом деле, мы понимаем, что задача весьма нетривиальна, и далеко не все проекты могут в этом нуждаться. Однако, как мы и говорили в вводной части, такие примеры встречаются и имеют право на жизнь.

В статье мы пошагово рассмотрели, как устроена данная интеграция. Если тема окажется актуальной и наберет большой охват, напишем следующую, в которой мы улучшим нашу интеграцию, чтобы передавать данные с react-native в Unity и наоборот. Или вы можете попробовать сделать это самостоятельно, следуя инструкции из документации к библиотеке.

С вами была команда dev.family и до новых встреч ;)

Ссылки

Другие важные статьи о разработке:

11
1 комментарий

Вижу много потенциальных применений, особенно в интерактивных и визуально насыщенных проектах

1
Ответить