Интеграция Unity кода в React Native. Часть 2
Всем привет! На связи снова команда dev.family. Мы продолжаем разбирать тему интеграции Unity-проекта в приложение, написанное на React Native.
В предыдущей серии…
Мы начали с того, что поместили игру на Unity в наше приложение. Как это было, можно почитать в предыдущей статье. Но пока части кода не взаимодействуют друг с другом, значит, работа не закончена. У нас есть кнопка «Save Result». Под нее было бы неплохо написать логику, чтобы показать, что у нас получилось. Спойлер: этим и не только займемся прямо сейчас.
Что будет дальше
Во второй части статьи мы возьмем текущую связку React Native + Unity и сделаем так, чтобы одна часть кода могла получать и обрабатывать сообщения с другой. И наоборот.
Продолжаем наше Unity-journey!
Предупреждение
В материалах статьи мы разобрали процессы работы над тестовым приложением. Это не панацея и не четкое указание, как именно нужно писать код. Но оно классно сработало для нас, и мы будем рады, если вдохновим вас на решение схожих задач в разработке.
Подготовка приложения
Для начала поработаем с кодом на React Native.
Давайте разберем, что именно мы добавили:
- unityRef – ссылка на наш UnityView, чтобы взаимодействовать с ним;
- messageToUnity – сообщение при навигации с нашего начального экрана. Именно его мы будем передавать в метод postMessage в Unity;
- useEffect – проверка наличия messageToUnity или изменений в нем с его дальнейшей передачей в Unity;
- postMessage – передача сообщения в Unity по gameObject, в methodName наше сообщение;
- handleUnityMessage – наш метод для обработки сообщения с Unity.
Далее передаем наш unityRef и вызываем handleUnityMessage в UnityView.
Чтобы избежать лишние подсвечивания, добавим:
Теперь на начальный экран (HomeScreen) добавим список с нашими результатами, где будем хранить 10 лучших попыток в игре.
Теперь перейдем в Unity Editor.
Подготовка игры
Перед тем, как настроить получение сообщений, давайте добавим новое поле, которое будет отображать наш лучший результат.
Тут вставим два текстовых поля:
- Best – просто текст;
- Best Score Text в Canvas – для хранения значения нашего лучшего результата.
Вот, как это выглядит на экране:
Теперь давайте обсудим, что мы хотим получить от отправки сообщений в/из Unity:
- Нажатие на кнопку «Save result» должно перенести нас на главный экран, но предварительно передать наши очки с Unity (onUnityMessage), после чего обработать и записать их в нашу статистику.
- Запись лучшего результата в поле bestScore (как раз по вызову нашего метода postMessage) при следующем попадании на экран с игрой, и так по кругу.
Но для начала необходимо разобраться, как отправлять и обрабатывать сообщения с Unity.
Отправка сообщений с Unity
Первое, что потребуется, – добавить скрипт. Назовем его MessageToReactScript. В него мы вставим следующий код, он хранится на странице GitHub самой библиотеки:
Это скрипт понадобится для отправки сообщения с Unity.
Теперь немного модифицируем его, чтобы получить нужный результат:
Обратите внимание: в примере представлена возможность отправлять только строку. Поэтому будем передавать все в JSON формате, чтобы не усложнять себе жизнь.
Далее добавляем новый класс, описывающий наш рекорд. У него есть два поля – date и score. Текущие счет и дата записываются непосредственно в score. Все, что нужно сделать дальше, – обернуть Score instance в JSON и отправить в мобильное приложение.
Скрипт готов. Теперь создаем новый GameObject ReactBridge:
Помещаем в него наш скрипт:
Переходим в Canvas и находим кнопку «Save Result», которую создали еще в прошлый раз. Помещаем в нее наш метод ButtonPressed из сниппета описанного выше: вначале добавляем GameObject ReactBridge, выбираем script MessageToReactScript и далее сам метод ButtonPressed():
Теперь при нажатии на кнопку «Save Result» мы будем отправлять JSON c нашим результатом в мобильное приложение.
На этом основная подготовка к отправке сообщения с Unity закончена. Теперь нужно собрать билд и переустановить Unity билд в Android и iOS.
ВАЖНО!
В прошлой части мы добавили в Plugins папку iOS, куда поместили файлы NativeCallProxy.mm и NativeCallProxy.h. с описанием функций, которые будем вызывать. Если они будут отличаться от тех, что мы вызываем в MessageToReactScript, или вовсе отсутствовать, то при сборке UnityFramework в Xcode получим ошибку, что такого метода не существует. Поэтому лучше перепроверить все соответствия заранее.
Для работы можно использовать файлы из репозитория библиотеки, которые лежат здесь.
Обработка сообщений с Unity в мобильной части
Давайте синхронизируемся. На данный момент у нас есть:
- пример Unity-проекта, которое отправляет сообщение с результатом в наше мобильное приложение.
- само мобильное приложение, которое, в свою очередь, обрабатывает сообщение с Unity.
Как пересобрать билд и поставить его в мобильное приложение, мы уже рассказали в первой части статьи.
Далее приступим к обработке сообщения. Для начала поставим alert, чтобы убедиться, что при нажатии на кнопку наше сообщение действительно отправляется.
Если делать все, как было описано выше, получим следующий результат:
Что мы видим? Сообщение отправляется, в нем отображаются данные о счете и времени, когда он был получен. Теперь давайте запишем их в наш стейт scores. Но для того, чтобы данные не были потеряны при перезагрузке приложения, мы запишем их в async-storage, перед этим установив саму библиотеку.
Вводим следующие команды:
Возможно, кто-то предпочитает использовать для хранилища более быстрые варианты по типу mmkv, но здесь нам это не критично. Поэтому нам вполне хватит async-storage.
Теперь снова поработаем с кодом на React Native – перейдем на главный экран и запишем в список результатов новое значение.
Для этого перепишем обработку сообщения Unity следующим образом:
Тут мы парсим наш JSON с результатом и после переходим на HomeScreen, передавая в параметрах сам результат.
В наши типы навигации мы также можем добавить данное изменение (оно поможет избежать ошибок от typescript):
Далее мы модифицируем HomeScreen, чтобы при получении результата через навигацию, он записывал нам в storage и state новые данные. Важный момент: в нашем случае мы храним только 10 лучших результатов в порядке убывания. Вы же можете отображать сколько угодно в любой удобной последовательности.
Вот, что у нас вышло:
В данном блоке кода мы делаем следующее:
- Достаем наши прошлые результаты и записываем их, если они имеются.
- В useEffect смотрим, что у нас нет данных (в scores). Если это так, то записываем предыдущие сохраненные в кэше данные.
- Записываем новые результаты, учитывая полученные через навигацию, и чистим параметры.
- Смотрим в useEffect, появился ли в параметрах новый результат, и если так, то переписываем результаты.
Также мы немного работали с UI: добавили обработку даты, чтобы не выводить ее в формате ISO (он смотрится не очень, согласитесь?) и чуть изменили стили. Вы можете сделать то же самое, или поменять интерфейс, как вам угодно.
На выходе мы получили такой результат:
Теперь пересобираем все для Android и проверяем, что все работает, как и должно:
Большая половина нашего функционала готова, и мы смело можем передавать данные с Unity в React Native. Теперь время поработать над другой, не менее важной задачей, – передачей данных из React Native в Unity.
Обработка сообщений с React Native в Unity части
Surprise-surprise! Оказывается, для отправки сообщений с React Native у нас уже была заготовка готового блока кода:
Здесь нужно посмотреть, есть ли в параметрах навигации messageToUnity. Если такой имеется, то вызвать функцию postMessage. Она, как видно из описания, принимает в себя следующие аргументы:
Следовательно, необходимо передать сюда наш игровой объект, указать его функцию и message.
Для этого воспользуемся объектом LogicManager и создадим ему новую функцию для обработки данного сообщения.
Далее переместимся в Unity и внесем последние изменения: в LogicManagerScript добавим новое поле, куда поместим BestScore, а затем создадим непосредственно саму функцию, которая будет изменять его значение:
Тут мы добавили:
- bestScoreText – текст, который достаем с UI;
- public void SetBestScore – функцию, которая принимает и помещает в текстовое значение bestScoreText message, передаваемый из React Native посредством функции postMessage.
Далее в Unity Editor поместим в наш LogicManagerScript (который находится внутри GameObject LogicManager ) текстовый элемент, чтобы значение нашего BestScoreText менялось.
Собственно на этом вся наша работа в Unity закончена. Но вы всегда можете добавить что-то еще или улучшить написанный код.
Осталось только в очередной раз пересобрать билд 🙂
Возвращаемся в React Native. Сначала изменим нашу функцию для перехода на страницу с Unity так, чтобы при наличии результатов мы доставали наивысшие из них и передавали в нашу игру.
Добавляем следующую функцию в HomeScreen:
После этого наш экран с Unity всегда будет ждать message. Далее передаем эту функцию в onPress нашей кнопки «Go Unity»:
Добавляем значения в функцию для отправки сообщения в Unity. В UnityScreen изменяем/добавляем следующее:
В данном примере мы указываем, что gameObject и methodName, которым мы передаем наш message – это LogicManager и SetBestScore, соответственно. В то же время наш message хранит в себе наилучший результат, передаваемый с экрана HomeScreen (наш message – это строка, которая может хранить в себе не только текст, но и JSON-объекты).
А теперь давайте запустим приложения на iOS и Android и проверим, что все работает как мы и ожидали:
iOS:
Android:
Всё работает 🙌
ВАЖНО!
При передаче сообщения в Unity мы столкнулись с небольшой проблемой: после отправки сообщения ничего не происходило. Оказалось, что дело в нашем начальном экране в Unity. Так как это была отдельная сцена, там не было LogicManager, и наш текст не отображался. Поэтому мы немного изменили код и поместили начальный экран в Canvas сцены самой игры. Это не самое красивое решение, но нам оно подошло. Поэтому, когда будете вызывать функцию postMessage, убедитесь, что открыта нужная сцена.
Заключение
Нам понадобилось всего две подробных статьи, чтобы разобрать вариант интеграции Unity-проекта в приложение, написанное на React Native. Мы пошагово расписали алгоритм, по которому работали сами. Надеемся, наш опыт облегчит решение похожих задач или вдохновит на новые проекты. Все примеры кода вы найдете в блоке с ссылками ниже. Вы можете узнать о процессе чуть больше, изучив материалы про плагины в Unity.
А здесь представляем вам конечный код React Native (да, мы что-то меняли на ходу):
На связи была команда dev.family, еще спишемся ;)
Ссылки
Другие важные статьи о разработке: