Всем привет. Меня зовут Григорий Дядиченко, и я технический продюсер. Сегодня хотелось поговорить про Unity, веб, как его дружить с мобильными телефонами, какие есть удобные трюки и приколы, и причём тут React. Если вам интересна тема веб проектов на Unity, добро пожаловать под кат!
Я очень сильно верю в то что пластмассовый мир веб победит. Ничего не надо устанавливать, идеальная модульность, даже в интеграции в приложения различных технологий есть очень много плюсов. Скажем вам нужны зачем-то AR маски в приложении. И зачем вам класть в приложение какие-то SDK, увеличивать вес приложения, если можно вставить подобную ссылку в вебвью. Да, пользователю потребуется интернет для работы функции. Но если данный функционал в приложении не является ключевым, то в таком формате плюсов больше. Так как от размера приложения в сторе очень сильно зависит конверсия в установки от той же рекламы. И когда скажем маски используются в рамках ограниченной рекламной активации, то даже для интеграции в мобильное приложение веб решение смотрится лучше, чем внедрение сложного сдк и функционала прям в приложение.
Веб обладает множеством недостатков. В большинстве вебвью не работает localstorage (в том же webview телеграма нужно каждый раз запрашивать разрешения). Помимо проблем поддержки разных браузеров, ограниченности возможностей рендера и нюансов какие технологии там доступны. Но тем не менее к 2022 году можно сказать, что веб стал в разы приятнее чем раньше. Сайт https://caniuse.com/ в целом прекрасен.
Я долго считал, что Unity и веб не совместимы смотря на какое-то невозможное время загрузки проекта и на другие проблемы, которые я видел. Но так как с 2020 года я решил больше заниматься проектами я разобрал много вариантов и вернулся к Unity. Pixi, Three, Babylon, Phaser, PlayCanvas прекрасные технологии со всего лишь одним недостатком. На них очень трудно найти разработчиков. В отличии от Unity скажем сами движки там легче, и грузятся они быстрее (меньше чем несколько секунд Unity проект грузить невозможно, но это можно обойти через UX и мы об этом поговорим).
Из-за невозможности выстроить некоторый процесс продакшена веб проекта, я решил таки покопаться "что не так с Unity" и обнаружил забавное. На самом деле в большинстве случаев проекты которые грузятся по несколько минут — проекты неправильно организованы и просто очень много весят. Само Unity грузится не так долго, и да билд меньше 5мб веса получить сложно. Но в современном мире 5мб - это не так критично. По минуте грузилось то, где использовалось множество ассетов, где при большом проекте не был реализован механизм OnDemand закачки и т.п. Разбирать все нюансы реализации веб проектов — это не на одну статью. Но сегодня хочется пройтись по некоторым рекомендациям и интересным подходам. Плюс разберём реализацию этого проекта, как некоего примера.
Зачем тут React?
Правильнее наверное ставить вопрос даже не про React, а про натив. Реакт — это просто один из удобных и популярных фреймворков + для него есть плагин, который мы разберём. Но все пункты ниже подходят для любой технологии нативного веба.
Шрифты и их отрисовка
Когда мы говорим про WebGL речь идёт про растровый рендер шрифтов. Что я заметил во всех технологиях, да и Unity не исключение. Для рендера шрифтов TMP формирует текстуру атлас с символами, которые потом натягиваются на Quad меши. Проблема данного подхода вес, и то как шрифт выглядит. Кривые браузер рисует симпатичнее.
События интерфейсов
Официально Unity не поддерживает мобильные телефоны. Но на самом деле на мобилках всё неплохо работает, кроме того что касается интерфейсов. Инпут филды и т.п. работают с багами, и есть очень много косяков. Делая же всё на нативном вебе всё работает идеально.
Можно скрыть загрузку
Любой игровой движок не особо нужен (да и удобен) для реализации меню, интерфейсов и т.п. Unity и адаптив вебовский — это вообще что-то на грани фентези. И в этом и заключается основной плюс натива. Грузится Unity несколько секунд (если правильно настроить проект, пожать всё что надо пожать и т.п.) И пока Unity грузится пользователь может: авторизоваться в свой аккаунт, выбрать уровень, выбрать скин для персонажа и заняться другими делами, которые потом просто будут переданы в рантайм Unity. Да даже пройти обучение. Поэтому с точки зрения пользователя — он вообще не увидит загрузки.
Вёрстка интерфейсов
Как я уже говорил. Все инструменты Unity и любого другого движка и близко не стоят с возможностями html5 + css3. Можно подумать "но есть же внутриигровые элементы и интерфейс должен с ними взаимодействовать". Сегодня мы разберём и это.
Сделаем небольшую игру
Разбирать реализацию чего-либо интересно на примерах + когда есть пример полезно иметь его под рукой, чтобы вспоминать "а как это вообще делается". Я не буду разбирать нюансы работы с React, что такое UseState и т.п. Так же как и глубоко углубляться в Unity. Для этого есть целые курсы. Мы же разберём примеры их взаимодействия и решения некоторых типовых задач + набор советов для веба. Для этого я подготовил два проекта примера: React контейнер и Unity игра Runner. Результат того, что получилось можно посмотреть тут. Всё собиралось на бесплатных ассетах и коленке, так что дизайн не судите строго, его можно привести в порядок.
Настройка Unity проекта
Больше всего на время загрузки проекта влияет итоговый размер билда, который складывается из двух частей. Размер ассетов и размер кода сборки. Поэтому для быстрой загрузке в вебе необходимо отрезать всё ненужное и пожать всё.
Убрать всё ненужное из движка
В Unity есть возможность отключать модули и пакеты, которые вам не нужны.
Если вам не нужен пространственный интерфейс, то не импортируйте TextMeshPro, он занимает много места. Так же отключайте всё неиспользуемое и не нужное. Скажем в проекте раннере PackageManager выглядит вот так.
Для уменьшения билда при отключенных пакетах важно, чтобы в Player Settings->Other Settings у вас было установлено Strip Engine Code и High.
Включаем правильное сжатие
Я предпочитаю Gzip так как его поддерживает большинство веб серверов, хотя говорят что Brotli оптимальнее.
Оптимизируем ассеты
Тут конкретных советов нет, но базово проверяйте зрительно, что у вас оптимальное разрешение текстур (жмите, включайте Crunch до тех пор, пока визуал особо не страдает). То что у вас нет Non Power Of Two текстур в проекте, а если и есть, то они упакованы в атласы. В общем тут нет никаких отличий от советов по оптимизации и в мобильном проекте.
Фикс краша на IOS
На некоторых версиях Unity и Safari на IOS у вас будет крашится Unity проект на загрузке. Это разбиралось в этой ветке форума, как и воркэраунд вокруг, лучше просто иметь ввиду, а не сразу считать, что Unity на сафари не работает.
Интеграция с React
Выбор минимального шаблона Unity
Нам в целом не понадобится html, который генерирует Unity. Но так как по продакшену проще финальный билд собирать из React проекта, то лучше сразу собирать минимальный. А не интегрировать сделанный на реакте контейнер в качестве Unity WebGL тимплейта. Я пробовал и так, и так, и замена файлов в React проекте в разы удобнее.
Плагин Unity Webgl React
Основная интеграция будет происходить через плагин https://react-unity-webgl.dev/. Очень удобно сделанный для связки Unity и React. Мы разберём что и как интегрируется.
Интеграция в шаблон
Сделав пустой React проект, можно добавить туда плагин. И создать компоненту UnityReactContaner который в первом приближении и нашими настройками проекта будет выглядеть вот так:
Unity React Container
import React from "react";
import { Unity, useUnityContext } from "react-unity-webgl";
function UnityContainer() {
const { unityProvider } = useUnityContext({
loaderUrl: "./build/UnityBuild.loader.js",
dataUrl: "./build/UnityBuild.data.unityweb",
frameworkUrl: "./build/UnityBuild.framework.js.unityweb",
codeUrl: "./build/UnityBuild.wasm.unityweb",
});
return <Unity unityProvider={unityProvider} />;
}
UnityBuild как название формируется в зависимости от папки сборки. Если ваша папка называется по-другому или файлы, то просто переименуйте их в скрипте. В отличии от официального примера отличаются две вещи. Приписка расширения (из-за наших настроек сжатия, и если вы их будете менять они могут измениться, на что стоит обращать внимание) и то, что путь относительный (так удобнее если игру вы кладёте не в корень вашего сайта). В проект нужно закинуть необходимые Unity файлы полученные в ходе сборки, чтобы иерархия папки public в проекте выглядела как-то так:
Пишем из Unity в React App
Разберём как использовать функционал коллбеков, чтобы передавать значения из Unity в React приложение. Для примера разберём WebUtils.jslib. Пока заведём в нём метод для передачи счёта:
WebUtils.jslib
var LibraryGLClear = {
OnScoreUpdate: function(scoreValue){
window.dispatchReactUnityEvent(
"OnScoreUpdate",
scoreValue
);
}
};
mergeInto(LibraryManager.library, LibraryGLClear);
jslib в плагинах Unity проекта позволяет нам вызывать js функции из Unity. Для этого нам надо завести класс в Unity, который отвечает за вызов данного метода. В нашем проекте он называется WebglBridge и на данном этапе будет выглядеть так:
WebglBridge
using System.Runtime.InteropServices;
public static class WebglBridge
{
[DllImport("__Internal")]
private static extern void OnScoreUpdate(int score);
public static void UpdateScore(int score)
{
#if UNITY_WEBGL && ! UNITY_EDITOR
OnScoreUpdate(score);
#endif
}
}
Дальше в React приложении нам нужно подписаться на событие обновления счёта:
UnityContainer
import React from "react";
import { Unity, useUnityContext } from "react-unity-webgl";
function UnityContainer() {
const [score, setScore] = useState(0);
const { unityProvider, addEventListener, removeEventListener } = useUnityContext({
loaderUrl: "./build/UnityBuild.loader.js",
dataUrl: "./build/UnityBuild.data.unityweb",
frameworkUrl: "./build/UnityBuild.framework.js.unityweb",
codeUrl: "./build/UnityBuild.wasm.unityweb",
});
const handleScoreUpdate = useCallback((score)=>{
setScore(score);
});
useEffect(() => {
addEventListener("OnScoreUpdate", handleScoreUpdate);
return () => {
removeEventListener("OnScoreUpdate", handleScoreUpdate);
};
}, [addEventListener, removeEventListener, handleScoreUpdate]);
return <Unity unityProvider={unityProvider} style={{width: "100vw", height: "100vh", overflow: "hidden", zIndex: 0}} />;
}
В данном случае при вызове из Unity метода WebglBridge.UpdateScore(50); в реакт приложении значение score станет равным 50. И будет сказано обновить отрисовку веб приложению. Для подписки и отписки от события используются методы из useUnityContext, которые называются addEventListener и removeEventListener. А в свою очередь handleScoreUpdate это метод обработчик. Важно, чтобы название передаваемое в addEventListener и removeEventListener совпадало с названием метода в файле WebUtils.jslib.
Из удобств то, что подобное событие может обладать множеством параметров или без параметров. Что можно увидеть в финальной реализации WebUtils.jslib. Так же как и то, что для текста нужно переводить переменную в правильную кодировку.WebUtils.jslib
WebUtils.jslib
var LibraryGLClear = {
OnGameOver: function(){
window.dispatchReactUnityEvent(
"OnGameOver"
);
},
OnScoreUpdate: function(scoreValue){
window.dispatchReactUnityEvent(
"OnScoreUpdate",
scoreValue
);
},
OnFloatingText: function(x, y, text){
window.dispatchReactUnityEvent(
"OnFloatingText",
x,
y,
UTF8ToString(text)
);
}
};
mergeInto(LibraryManager.library, LibraryGLClear);
Пишем из React App в Unity
Этот механизм я бы сказал устроен классически. Так как в useUnityContext так же есть метод под названием sendMessage. Который работает как самый обычный Unity SendMessage. В репозитории приводится пример использования без параметров. Но скажем если мы хотим иметь возможность из React приложения ставить игру на паузу, то нам нужно сначала завести класс ReactEventsHandler:
ReactEventsHandler
using UnityEngine;
public class ReactEventsHandler : MonoBehaviour
{
public void Pause()
{
Time.timeScale = 0;
}
}
Дальше мы делаем добавляем объект, который будет принимать SendMessage в сцену (название объекта важно, но его не обязательно называть так же, как и класс):
И в нашем UnityContainer мы просто делаем функцию handlePauseButton где делаем sendMessage("ReactEventsHandler", "Pause"); и вызываем её по какому-то событию, скажем нажатию кнопки:
UnityContainer
import React from "react";
import { Unity, useUnityContext } from "react-unity-webgl";
function UnityContainer() {
const [score, setScore] = useState(0);
const [isPause, setPause] = useState(true);
const { unityProvider, addEventListener, removeEventListener,
sendMessage } = useUnityContext({
loaderUrl: "./build/UnityBuild.loader.js",
dataUrl: "./build/UnityBuild.data.unityweb",
frameworkUrl: "./build/UnityBuild.framework.js.unityweb",
codeUrl: "./build/UnityBuild.wasm.unityweb",
});
function handlePauseButton(){
sendMessage("ReactEventsHandler", "Pause");
setPause(true);
}
const handleScoreUpdate = useCallback((score)=>{
setScore(score);
});
useEffect(() => {
addEventListener("OnScoreUpdate", handleScoreUpdate);
return () => {
removeEventListener("OnScoreUpdate", handleScoreUpdate);
};
}, [addEventListener, removeEventListener, handleScoreUpdate]);
return (<div>
<Unity unityProvider={unityProvider} style={{width: "100vw", height: "100vh", overflow: "hidden", zIndex: 0}} />
<button onClick={handlePauseButton}>Пауза</button>
</div>);
}
На всякий случай повторю, что "ReactEventsHandler" — это имя объекта, а не класса и в этом надо быть внимательным. В данном примере у нас появится кнопка, которая будет ставить игру на паузу. Остальные доработки можно посмотреть в репозиториях. Но ещё один концепт мы разберём.
Скрываем загрузку
В чём основная прелесть подобного контейнера, это в том что можно скрыть загрузку Unity. Конечно в примере я не стал делать "ввод никнейма" и целый набор экранов. Нечто похожее можно посмотреть тут. Тут уже скрывается получше. Но сделаем просто экран с подсказкой управления.
Для этого заведём компоненту Onboarding:
import {isMobile} from "../../utils/utils";
import "./Onboarding.css"
export function Onboarding(props){
return <div className="onboarding">
<div className="onboarding-text">{isMobile.any() ? "Управляйте роботом с помощь свайпа" : "Управляйте роботом клавишами A/D или стрелочками" }</div>
<button disabled={props.isActive} onClick={props.startCallback}>Начать</button>
</div>
}
Пропишем стили:
.onboarding{
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
z-index: 15;
backdrop-filter: blur(10px);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.onboarding-text{
padding-bottom: 40px;
font-size: 18px;
}
И интегрируем её в контейнер. Для этого в useUnityContext есть замечательное поле isLoaded, которое показывает статус загрузки нашего Unity приложения. Выглядеть наш UnityContaner будет как-то так:
UnityContainer
import React from "react";
import { Unity, useUnityContext } from "react-unity-webgl";
function UnityContainer() {
const [score, setScore] = useState(0);
const [isPause, setPause] = useState(true);
const [isShowOnboarding, setOnboarding] = useState(true);
const { unityProvider, addEventListener, removeEventListener,
sendMessage, isLoaded} = useUnityContext({
loaderUrl: "./build/UnityBuild.loader.js",
dataUrl: "./build/UnityBuild.data.unityweb",
frameworkUrl: "./build/UnityBuild.framework.js.unityweb",
codeUrl: "./build/UnityBuild.wasm.unityweb",
});
function handlePauseButton(){
sendMessage("ReactEventsHandler", "Pause");
setPause(true);
}
function handleStartButton(){
handleResumeButton();
setOnboarding(false);
}
const handleScoreUpdate = useCallback((score)=>{
setScore(score);
});
useEffect(() => {
addEventListener("OnScoreUpdate", handleScoreUpdate);
return () => {
removeEventListener("OnScoreUpdate", handleScoreUpdate);
};
}, [addEventListener, removeEventListener, handleScoreUpdate]);
return (<div>
<Unity unityProvider={unityProvider} style={{width: "100vw", height: "100vh", overflow: "hidden", zIndex: 0}} />
<button onClick={handlePauseButton}>Пауза</button>
{isShowOnboarding? <Onboarding isActive={!isLoaded} startCallback={handleStartButton}/> : ""}
</div>);
}
где handleStartButton — это обработчик кнопки начать, при том что онбординг выглядит как-то так:
Я реализовал простейший пример, но если у вас в проекте/игре, есть авторизация, авторизация через соц. сети, профиль пользователя, предварительный выбор каких-то настроек без геймплея. То игра по умолчанию ставится на паузу или в пустой сцене, но юнити контейнер мы всё равно грузим. И в таком случае когда пользователь дойдёт до плеера геймплея, то он уже не увидит никакой загрузки.
И кстати на тему isMobile и прикольного нюанса Unity. Для этого приведу его реализацию.
export const isMobile = {
Android: function () {
return navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/Miui/i);
},
BlackBerry: function () {
return navigator.userAgent.match(/BlackBerry/i);
},
iOS: function () {
return navigator.userAgent.match(/iPhone|iPad|iPod/i);
},
Opera: function () {
return navigator.userAgent.match(/Opera Mini/i);
},
Windows: function () {
return navigator.userAgent.match(/IEMobile/i) || navigator.userAgent.match(/WPDesktop/i);
},
any: function () {
return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
}
};
В Unity есть своя проверка на мобильные платформы работающая по тому же принципу. И видимо взятая отсюда. Но она работает не верно, так же как и ответ на stackoverflow на устройствах Xiaomi, так как Mi Browser говорит, что он Linux. Поэтому я добавил доп. проверку чисто на Mi Browser и эта реализация работает корректнее.
Floating Text
Ещё примера я решил сделать отлетающий текст при сборке очков и т.п. Допустим мы хотим, чтобы при сборе какого-то бонуса React App в котором у нас интерфейс учитывал "где мы его подобрали" и текст отлетал не откуда-то, а именно в нужную интерфейсную панельку.
Предположим, что мы провели все шаги выше, у нас уже есть нужное событие и т.п. Как нам пересчитать координаты. Если игра на весь экран, то всё довольно тривиально, и это показано в репозиториях. В юнити мы получаем значение позиции в координатах вьюпорта.
var playerPos = _Camera.WorldToViewportPoint(_Player.transform.position);
WebglBridge.SetFloatingText(playerPos.x, playerPos.y, $"+{ScoreAddition}");
А дальше передав через коллбек эти значения в нашу компоненту FloatingText имеем такой код:
const width = 300;
const height = 50;
export function FloatingText(props){
return <div style={{
width: width,
height: height,
left:`calc(${props.x * 100}vw - ${width/2}px)`,
top:`calc(${(1 - props.y) * 100}vh - ${height/2}px)`
}} className="floating-text">
{props.text}
</div>
}
(1 - props.y) так как вьюпорт юнити считается с левого нижнего угла, а vw и vh с левого верхнего. А так же умножаем на 100, так как в Unity вьюпорт в значениях от 0 до 1, а vw и vh в процентах.
Для анимации мы в стиле не указываем начальную позицию, но указываем конечную. Так как для простоты я работаю с fixed координатами тут так же всё просто.
.floating-text{
position: fixed;
color: white;
font-size: 32px;
z-index: 1;
-webkit-animation: fly 1s normal ease-out forwards;
animation: fly 1s normal ease-out forwards;
}
@keyframes fly {
0% {
color: rgba(255,255,255,255);
-webkit-animation-timing-function: ease-in;
}
100% {
color: rgba(255,255,255,0);
left: 20px;
top: 20px;
animation-timing-function: ease-out;
}
}
И получаем анимацию отлетающего текста из позиции в Unity в произвольную позицию. Такую же математику только сложнее можно проделать, если Unity приложение открывается не на весь экран или же в iframe, но она будет посложнее и учитывать иерархию блоков в вёрстке.
В заключение
Спасибо за внимание. Надеюсь разбор примеров в статье дал вам понимание о том, как строится интеграция Unity в React. Конечно без экспертизы в React и в JS примеры воспринимать тяжело, но если вы занимаетесь веб проектами я бы рекомендовал изучить какой-то JS Framework и Реакт для этого отлично подходит. Так как настоящий кроссплатформенный разработчик — это тот, кто знает контекст и нюансы множества платформ и умеет в них строить оптимальные решения. В конце я оставлю целиком проект, чтобы при желании вы могли его разобрать и научиться чему-то новому. Либо использовать как некий сниппет в своих будущих проектах.
Комментарии (13)
Pastoral
15.10.2022 22:34А на 4pda как раз была тонкая реклама изготовления на Unity игр для Яндекса. Если разными путями приходят к одному и тому же, наверно это правильно.
Единственно чего я не понял, так как вообще можно сравнивать Unity и Three или Babylon. Если придёт время написать что-то поумнее и заслуживающее 15 секунд ожидания, то какие альтернативы Unity? Разве что Unreal, кто-то где-то как-то его вроде приспосабливает, но никак не pure web. Unity так же думала, скорее всего, когда экспорт в WebGL делала.
Пока читал всё ждал слова PWA, почему не дождался - не понимаю.
DyadichenkoGA Автор
16.10.2022 00:04Да я просто как-то не интересуюсь именно PWA. Не моя специфика)
Что же касается движков. Unreal с вебом вообще не дружит. Там был какой-то плагин от комьюнити, но он давно не поддерживается вроде как. Так что только Unity, если брать прям движки
А про сравнивать. Ну я бы конечно прям не сравнивал, если были бы альтернативы хорошие. Сейчас разрабатывать на pure web почти равно разрабатывать свой движок :)
oblakooblako
Тяжелые проекты в вебджл достаточно долго билдить, как программист тестирует код в юнити, он дублирует интерфейс в каком-то простом стиле?
DyadichenkoGA Автор
Смотря о чём речь. Если юнити - геймплейный плеер. Весь мета гейм вероятно будет на стороне реакта, и тут надо смотреть в частностях. Отображение счёта или валют, или чего-то ещё можно делать на уровне логов. Простые кнопки, моки и команды решают остальное. Там не нужен гуй. Дублирование гуя слишком дорогой инструмент получится с его поддержкой. Контекст меню юнити, хоткеи и т.п. Полный цикл уже в билде, так как тестирует не разработчик, а тестер.
И тут вопрос, что такое долго. Если билд собирается билд роботом, то не особо дольше, чем il2cpp сборки. Юнит тесты скажем так через гуй и при обычной работе не стоит делать.
Особняком тут будут ММО или рпг, где сложный интерфейс сопряжён автоматом с геймплеем. Но всё равно тестить смотря что и смотря как. Если настроены ассембли, если правильно организован с разбивкой ассетов на бандлы, то пересборки просто будут не такими частыми)
oblakooblako
Не очень понял про "весь мета гейм вероятно будет на стороне реакта". Не могу придумать сложный кейс, но представим себе джуниора, ему поставили задачу разработать какую-то ui логику, при работе с этой логикой недостаточно просто вывести логи, допустим это задача связана со взаимным расположением ui компонентов друг относительно друга в зависимости от различных параметров. Без визуального представления такую задачу сложно решить. Исходя из такой логики, я хочу представить идеальный кейс, при котором можно построить работу с комфортом и без ненужной переработки используя эту библиотеку и основания для внедрения ее. С моей точки зрения, я бы применил эту библиотеку, если бы у меня был уже готовый юнити проект на другой платформе и я решил бы зайти на webgl.
DyadichenkoGA Автор
Если коротко, очень зависит от игры. Описанный кейс вероятно будет решать реакт разработчик вообще. Так как весь гуй на стороне реакта)
Такой процесс и подход предполагает, что все проблемы гуя решаются на стороне реакта. А на стороне юнити нужно один раз написать апи для проброса нужных параметров
oblakooblako
Тогда зачем вообще использовать юнити ?
DyadichenkoGA Автор
Для рендера не GUI задач. В основном 3д или же 2д со сложной механикой взаимодействия типа платформера. Игра - это же не гуй. Ну собственно вот частный пример. У меня есть такой продукт https://whitelabelgames.ru/game/ar-bow И это очень простая игра, и очень простая механика. Разработка на три, плейканвасе и т.п. просто по моему опыту медленнее
Когда я пытался ту же самую механику сделать на Three.js и полностью на веб технологиях я потратил в разы больше времени, чем на Unity + React. Ну, а на реакте такое вообще проблематично сделать. Всё очень зависит от игры
Если делается текстовый квест, hidden object и т.п. без VFX или с VFX который проще сделать на стороне веба, то Unity действительно нет нужды использовать. Скажем вот игра целиком на реакте, так как Unity тут ни к чему https://whitelabelgames.ru/game/card-game
DyadichenkoGA Автор
Я бы тут обозначил другую проблему. Данный подход применим только для чисто веб проектов. Потому что смешивая технологии мы теряем возможность сделать адекватное приложение. Можно конечно запихнуть в стим билд засунув туда в сборку локальный сервер и chromium для отрисовки игры. А на мобилке вебвью по сути запускать. Но это "костыль" обладающий всеми недостатками веба. Скажем так сделал Warcraft 3: Reforged с главным меню, и видно что там внутри веб :)
Поэтому делая такую смесь мы теряем в некотором смысле в кроссплатформенности. Плюс это требует двух разработчиков. Но для мобильного веба делать проект чисто на юнити — это натуральное самоубийство. Потому что там постоянно будут лезть проблемы и нужно будет писать воркэраунды, так как официальной поддержки веба нет. Но как я потестировал чаще всего ломаются всякие инпут филды и т.п. Плюс нужно всё равно иметь JS экспертизу, чтобы правильно давать разрешения на скажем гироскоп или камеру. Правильно пробрасывать аналитику и т.п. И поддержка всех платформ становится гемором. Поэтому это решение (как и любое существующее решение) — не серебряная пуля. И всё очень зависит от контекста. Просто есть и такой подход :)
DyadichenkoGA Автор
Просто тут надо сравнивать. И сравнивать смотря с чем. Скажем возьмём чистый веб. Пикси, плейкансвас и т.п. Я пришёл к тому, что процесс Unity + React эффективнее по бизнес причинам масштабирования команды. Unity разработчиков можно найти много на рынке, React разрабов тоже хватает. Но если мне нужно будет масштабировать команду на Pixi или Three.js, да просто повеситься проще :) Тех кто знает эти технологии очень мало. И мы начинаем учить своих. Это долго, дорого и менее эффективно) Чем за гуй отвечает реакт, за механику юнитисты. Тут думаю любому понятно, как масштабировать команду. Это не задача с кучей звёздочек. Так как и те, и другие специалисты на рынке есть :)
Да требуется иметь две экспертизы для поддержки проекта, но это не такая высокая цена с точки зрения бизнеса в долгосрочной перспективе скажем так. Чем когда бюджеты есть и ты просто тратишь пол года на обучение людей твоему фреймворку написанному поверх Three.js, обучая нюансам Three.js. Конечно это не всё, чем ты платишь юзая Unity. Так как скажем ты теряешь в возможности динамически грузить шейдеры, так как их придётся компилировать и пересобирать сборку. Но так как это Web для меня это тоже "малое зло" :)
За цену двух экспертиз поддержки проекта ты получешь отличное и удобное SDK для механик (Unity) с огромным комьюнити. И тоже самое для SDK разработки своего интерфейса (React) с таким же огромным комьюнити. Просто исследуя разные подходы реализации веб проектов я пришёл к этому из соображений мышления студии нежели разработчика :)
oblakooblako
Вот этот аспект построения команды мне как раз и был интересен, спасибо за статью и ответы. Не пробовали ли babylon.js ?
DyadichenkoGA Автор
Я за пол года попробовал всё. Максимально близкое PlayCanvas из-за наличия визуального редактора. Просто визуальный редактор очень сильно влияет на скорость разработки. Но проблема везде одна. Исследовал я для решения на чём и как делать https://whitelabelgames.ru/ я упёрся в то, что представим что проект полетит и мне нужна будет команда или несколько команд. А теперь идём на hh и вбиваем babylon.js в поиск. Видим две вакансии. И как бы прекрасно не было любое веб сдк — так со всеми. А это значит захочешь масштабировать команду — расти своих. А это очень дорого и звучит сильно дороже связки Unity + React. И по скорости разработки фич, и по масштабированию команды, и по много чему ещё. Типа с точки зрения бизнеса я не нашёл ни одного аргумента перебивающего эти недостатки. Технологии то хорошие, просто толку то, если их никто не знает. Да и людям невыгодно учить, так как потом они пойдут на рынок труда и не найдут работу. А это даже немного нечестно по отношению к своей команде :)
Наличие разработчиков на рынке, комьюнити, числа решённых проблем, числа готовых решений которые можно прикрутить — слишком сильно решает. Кто-то идёт в такие риски и думаю работает эффективно, но для своих проектов и продуктов я обосновать такое решение не смог :)
Недостатки Unity я решил и в статье описал как решается большая часть. Или посчитал их несущественными относительно бизнес недостатков остальных решений :) Я же разрабатываю проекты под заказ сейчас для разных компаний. И у меня очень сильно скачет загрузка. Иногда бывает по 10 проектов одновременно. И толку мне от pure web технологий, если я не могу масштабировать команду под такие случаи :)