Коротко расскажу историю процесса
Мне нужно было адаптировать 3d игру к различным экранам мобильных устройств. Начал я, как и любой новичок, конечно же с тупняка посвященного тому, что вообще мне надо делать.
В процессе я получше познакомился с UI и как адаптировать его под различные размеры экранов, а так же немного погрузился в постобработку. Что, кстати, само по себе не решает проблем возникающих на некоторых экранах, если UI должен отображаться поверх объекта на сцене. Кнопочки и надписи то растянуть по размеру экрана и сохранить их местонахождение относительно всего экрана — не сложно. А вот чтобы при этом кнопка осталась поверх нужного объекта и все было хорошо, это надо попотеть. По крайней мере, если не делал ничего похожего и в целом опыта в разработке немного.
Я очень долго пробовал, пытался, искал. Мое виденье того, как должна выглядеть сцена постоянно менялось. Это сбивает с толку, потому что спустя несколько изменений сцены я перестал понимать как я хочу видеть все это, как будет красиво, а как нет. Уже просто что‑то делал, что‑то пробовал, пытался хоть как‑то привести все в порядок. Можно сказать на автомате. И в итоге то ли понял, то ли осознал, то ли окончательно решил что и как именно мне надо сделать. Я начал завершающую фазу адаптации, от финиша меня отделало примерно 5 часов работы. Думать больше не надо, осталось только сделать. Я сделал и улыбнулся, в тот момент я саркастически радовался, потому что осознал, что в какой‑то момент свернул с правильной дорожки и мне надо все пе‑ре‑де‑лАть.
Самое главное, что дала мне эта ошибка — я хорошо понял, как все это делается и чуточку набил руку. И буквально за 3 часа переделал все. Так что я пойму закатанные в небо глаза опытного разработчика, потому что сегодняшний я уже смотрит на эту задачу, как на старые ворота.
Ознакомлю с трудностями
Что ломается:
Сбивается отображение кнопок, которые должны быть поверх куба.
На экранах с челкой используется виньетка, чтобы закрыть слишком большое свободное пространство. А на экране без челки она все портит. Половина сцены становится слишком темной. На устройствах с более широким форматом экрана этого пространства нет. Теперь, кстати, виньетка носит не только декоративно‑ретушный характер, но и стилистический — еще одно хорошее открытие, сделанное благодаря этой преграде.
Меняется расстояние до рамки. Что тоже плохо, потому что, на некоторых устройствах это сильно портит вид. И опять же все объекты смещаются, а значит и кнопки. Нажимая на куб, игрок увидит чудесное ничего(предполагается, что куб в его представлении это кнопка).
Расположение слайдера относительно куба, который описывает за что слайдер отвечает (Sound, Sensetive. Кнопка Reset Levels относительно соответствующего куба.
Все это съезжает из-за того что меняется высота камеры. UI-то растягивается по размеру экрана, но вот только размер и положение объектов уже не соответствуют элементам интерфейса:
Опишу текущее решение
В итоге я сделал несколько массивов, в которых я храню:
Модели устройств для которых нужны специфические настройки.
Элементы UI, расположение которых нужно изменить для этих моделей.
Координаты для каждого элемента UI, и для каждой модели они свои.
Интенсивность виньетки для каждой модели.
Размер камеры для каждой модели (камера в режиме ортографии разумеется).
Логика обработки массивов:
В методе Awake, перебираю все модели из массива и сравниваю с моделью текущего устройства.
При совпадении, смещаю позицию UI элементов на соответствующие этой модели и этим элементами координаты.
Изменяю значения интенсивности виньетки и размера камеры на соответствующие опознанному устройству.
На мой взгляд вышло немного запутано, зато давольно гибко. Можно добавить и изменить настройки любого элемента, для конкретной модели.
public string DeviceModel { get; private set; }
public int IndexDeviceCheck;
public string DeviceName { get; private set; }
public string[] Model;
public RectTransform[] UIElemet; //позиция кнопок для переноса
public Vector3[] Coordinates;//новая позиция для кнопок
public Camera MainCamera;
public float[] SizeCamera; //размеры камер
public PostProcessVolume _postProcessVolume;
private Vignette _vignette; //переменная в которую записывается ссылка для обращения к виньетке в PostProcessVolume
public float[] intensivityVignette;
void Awake()
{
DeviceModel = SystemInfo.deviceModel;
DeviceName = SystemInfo.deviceName;
_postProcessVolume.profile.TryGetSettings(out _vignette); //запись ссылки для обращения к виньетки
CanvasAdaptation();
}
public void CanvasAdaptation()
{
for (int i = 0; i < Model.Length; i++)
{
List<Vector3> Offsetpos = new List<Vector3>(UIElemet.Length); //координаты для перемещения конкретных UI элементов
if (DeviceModel == Model[i])
{
int LenghtUIarray = UIElemet.Length;
int end = (i + 1) * LenghtUIarray - 1;
int start = end - (LenghtUIarray - 1);
for (int b = start; b <= end; b++)
{
Offsetpos.Add(Coordinates[b]);
}
for (int e = 0; e < UIElemet.Length; e++)
{
UIElemet[e].position += Offsetpos[e]; //перемещаем UI на координаты присвенные модели устройства
}
MainCamera.orthographicSize += SizeCamera[i];
_vignette.intensity.value += intensivityVignette[i]; //усиление виньетки для экранов с челкой
}
}
}
А основные настройки, сделаны под устройства с чуть более широкими экранами. В общем они чуть более квадратные. И таких устройств гораздо больше. Поэтому имеет смысл сделать настройки под них, а адоптироваться под более высокие экраны. Ну и разумеется под сумасшедшие форматы, где устройство — это книжка.
Хочу обратить внимание, что объекты, на которых висит аниматор, не меняют свое местонахождение в запущенной игре. То есть они останутся на том же месте где и были, игнорируя логику в скрипте. Мое решение — добавить эти объекты в пустые объекты и аниматор повесить на пустышки. Эти объекты можно будет двигать в рамках пустышки. В моем случае, второй заяц такого решения — это сокращение количества анимаций. Потому как для меня это была сцена меню с разными разделами и вместо кучи анимаций с несколькими UI объектами для каждого раздела, у меня стало три пустышки с шестью анимациями, которые подтягивают и возвращают все кнопочки, слайдеры, тексты для всего раздела целиком.
Подведу итоги
Предполагаю, что это выглядит логично. Сперва я и делал настройки под «квадратный» экран. Но потом я сбился, запутался и как то так вышло, что в итоге основные настройки у меня оказались под меньшее количество устройств, а специфические настройки пришлось бы сделать под большее количество устройств из всех, что есть на данный момент.
Но в итоге я придумал вариант и для тех, и для этих и пока что мне нравится результат. Хотя, я не знаю наверняка правильный он или нет, но какого‑то гайда под мою ситуацию я не нашел, а сам пришел только к такому варианту. Возможно я еще переделаю эту систему. Например, я прописывал значения массивов в инспекторе. Возможно стоит это делать приватно в коде, возможно для хранения этих данных можно использовать какое‑то более правильное или оптимальное решение, например реляционную базу данных. Еще к примеру было бы круто иметь возможность отредактировать эти настройки не выпуская новой версии игры. Поэтому, возможно, в будущем я сделаю так, чтобы конфигурации хранились удаленно и я мог менять их мгновенно для всех игроков, как только это понадобится. Это кстати учебный проект, так что спрячьте свои помидоры, я учусь. Следовательно, хоть игра и примитивная, реализовать такой функционал можно. А потом все же не воспользоваться им, потому что игра слишком простая для такого.
В общем пока так, а там посмотрим. Интересно узнать, как бы вы решили такую задачу? Возможно кто‑то работает разрабом и на коммерческом проекте применяет более совершенную систему адаптации. Приглашаю в комментарии для обсуждения этой темы.
Напоследок, хотелось бы добавить, что планшеты и экстремальные экраны я пока не адаптировал. Но это не должно быть чем то сложным, так что в следующий сеанс разработки займусь именно этим.