Road Run, или как я свою первую игру делал. Часть 5

4cf7b26e8c10a3cedb341d34ccbfd790.png

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

Введение

Дома есть, деревья — тоже, о них говорилось в предыдущих частях, сейчас пора бы заняться уровнем и его компоновкой. Но где, или вернее, в какой программе лучше компоновать уровень? Для начала следует понять, что уровень в описываемой игре из себя должен представлять. 

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

Во время игры движется дорога на встречу игроку, что и создаёт эффект движения автомобиля вперёд. Игрок, в свою очередь, меняя полосы движения, получает возможность обруливать препятствия на дороге.

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

Но есть здесь и свои отрицательные стороны. Самая главная из них — это множество движущихся объектов. В игре движется всё: машина меняет полосы для движения, а дорога, обочина с домами и деревьями движутся мимо игрока. То есть, на уровне не будет ни одной статичной модели. Отсутствие статичных моделей не даст в игровом движке использовать статическое освещение, которое просчитывается заранее, что снижает нагрузку на ресурсы компьютера.

Может возникнуть вопрос, а зачем нам вообще нужен свет, на моделях есть текстуры, модели и их цвета можно вывести на монитор, и для вывода на монитор освещать ничего не требуется, это же не реальный мир, где мы видим всё в отражённом свете, в конце-то концов?

Да будет свет… и тень

Свет в игре нужен не для того, чтобы видеть окружающее пространство, а для того, чтобы это окружающее пространство отбрасывало тень. Тени очень полезны и важны, они придают глубину и объём изображению, делают картину, предстающую пред взором игрока более реалистичной, ведь мир, в котором мы живём, полон теней. На картинке ниже представлен пример одной и той же сцены: без теней — слева, и с тенями — справа.

Свет и тень

Свет и тень

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

Если подумать, то тут доступно как минимум два варианта: blender, в котором модели уже находятся, и игровой движок Unity, куда модели будут портированы, для создания самой игры. Как уже было сказано выше, в силу движения дороги относительно автомобиля, а не наоборот, применить в игровом движке статическое освещение не получится, а динамическое — слишком затратно по ресурсам. 

Более того, динамическое освещение будет ещё и использоваться неэффективно, так как дорога хоть движется вместе со всеми остальными объектами пейзажа, такими как дома, деревья и прочее. Но относительно дороги ни деревья, ни дома не движутся, только автомобиль игрока, следовательно тени от домов и деревьев на дорожном полотне и обочинах смещаться относительно этого самого полотна и его обочин тоже не станут. Получится, что динамическая тень никогда не будет изменять своего положения на поверхности моделей, но при этом будет просчитываться каждый каждый кадр, тратя драгоценные ресурсы компьютера впустую.

Здесь само собой напрашивается запечь все тени внутрь текстур моделей, раз они не двигаются. Поскольку использовать статическое освещение внутри Unity не получится, остаётся только blender.

Компонуем сегмент

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

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

Добавить модель из одного файла в другой можно при помощи команды «Append…», она находится в меню «File».

Append...

Append…

После запуска команды «Append», откроется окно с именем «Blender File View», в нём выбираем тот файл в котором находится интересующая нас модель и делаем на нём два щелчка левой кнопкой мыши — файл откроется. Внутри файла будут разнообразные папки, для нас интерес представляет папка с именем «Object», если её открыть, то внутри будут все модели, находящиеся в этом файле. Выделяем нужную модель, а кнопкой «Append» добавляем её.

Добавление моделей

Добавление моделей

Для дальнейшей работы понадобятся только те модели, материалы которых содержат растровые текстуры, модели с процедурными материалами использоваться не будут.

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

Во-первых, начать можно с создания базового сегмента дороги. Такой сегмент должен состоять только из тех элементов, которые будут присутствовать на каждом сегменте.

Базовый сегмент дороги

Базовый сегмент дороги

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

Все сегменты должны быть одинаковой ширины, в дальнейшем это сэкономит время и силы на их стыковке между собой в игре. Также в базовом сегменте следует сразу настроить освещение, так как все сегменты дороги должны быть равномерно освещены и иметь одинаковую яркость текстур. Тему освещения я затрону позже, когда доберёмся до запекания теней.

Файл с базовым сегментом можно сохранить на жёсткий диск компьютера, и использовать его как заготовку для создания разнообразных сегментов дороги с лесами и городами.

Во-вторых, игрок видит дорогу через камеру, ну или будет видеть, когда игра сама увидит свет, следовательно, при компоновке сегмента дороги стоит воспользоваться объектом «камера». Камера обычно уже есть на сцене, потому что является одним из трёх стандартных объектов, которые присутствуют «по умолчанию» в только что созданном blender-файле.

Настройка камеры

Настройка камеры

Перемещается и поворачивается камера теми же командами, что и модели: нажатие клавиши «G» вызывает команду перемещения, а клавиши «R» — вращения. Её следует поставить в то место сегмента дороги, откуда игрок будет видеть дорогу в игре.

Войти в режим обзора через камеру можно в редакторе »3D Viewport», нажав на круглую кнопку со стилизованной плёночной кинокамерой белого цвета. При повторном нажатии на эту кнопку — мы выходим из режима обратно.

Если выделить камеру, то в редакторе «Properties» появится вкладка со значком кинокамеры зелёного цвета, на ней основное значение для нас имеет поле «Focal Length» — фокусное расстояние, изменяя значение в нём, мы меняем фокусное расстояние или угол зрения камеры. 

Угол зрения камеры очень важный параметр, от него зависит, как много всего игрок увидит не только перед собой, но и вокруг себя, и с какими искажениями он всё это будет видеть. На картинке внизу показан один и тот же сегмент дороги, но в верхней её части сегмент дороги снят на камеру с фокусным расстоянием 8 мм, то есть с широким углом зрения, а в нижней части съёмка велась с фокусным расстоянием в 50 мм, угол зрения такой камеры уже.

Фокусные расстояния: 8 мм - верхнее, 50 мм - нижнее изображение

Фокусные расстояния: 8 мм — верхнее, 50 мм — нижнее изображение

Кроме искажений и поля зрения фокусное расстояние влияет на наше восприятие скорости движения, чем меньше фокусное расстояние, тем выше кажется скорость, с которой движется автомобиль. Большое фокусное расстояние сжимает изображение, визуально приближая объекты друг к другу, в результате нам кажется, что дистанция между объектами меньше. Малое фокусное расстояние, наоборот, визуально раздвигает объекты так, что они кажутся дальше друг от друга.

На самом же деле дистанция между объектами не меняется, как и время прохождения этой дистанции автомобилем, если он движется без ускорения, но с уменьшением фокусного расстояния, нам кажется, что за тоже самое время мы прошли более длинную дистанцию, а значит для нас скорость визуально увеличилась. Более вытянутые формы окружающего мира ещё больше усиливают эффект, придавая стремительности всему вокруг. На картинке выше хорошо видно, что верхний сегмент дороги выглядит более стремительным, чем нижний.

Сегменты дороги

Сегменты дороги

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

В-четвёртых, чем больше фокусное расстояние, тем дальше вы видите. Этот вполне очевидный факт сильно влияет на количество сегментов, которые должны быть на уровне в игре в любой момент времени. Нельзя просто взять и направить автомобиль игрока к обрыву, кроме того, процесс генерации нового сегмента должен быть скрыт от взгляда игрока, иначе постоянно возникающий из ниоткуда новый кусок дороги с домами и прочими элементами пейзажа будет, как минимум, раздражать.

Выход тут один: перед игроком должно находиться столько сегментов дороги, сколько потребуется, чтобы дорожное полотно уходило в точку на горизонте. Но, чем шире угол зрения и, соответственно, больше искажения пространства, тем быстрее в таком пространстве сходятся линии, и наоборот, чем угол зрения уже, больше фокусное расстояние, тем искажения пространства будут меньше, но и дистанция, на которой линии сойдутся в точку, будет больше.

На рисунке внизу показан пример того, как выглядит дорога одной и той же длины, снятая на камеру с длинным фокусным расстоянием — верхняя часть рисунка, и с коротким — нижняя часть. 

Влияние фокусного расстояния на восприятие дистанции

Влияние фокусного расстояния на восприятие дистанции

Сразу видно, что в нижней части рисунка дорога почти ушла в точку, в то время как вверху потребуется ещё как минимум пара сегментов для достижения аналогичного результата. Дополнительные сегменты — это дополнительная вычислительная мощность для их отображения. Естественно, что тут нужно соблюдать определённый баланс, слишком широкий угол зрения приведёт к большим искажениям, и играть будет неудобно, но слишком большое фокусное расстояние, то есть узкий угол зрения, визуально сожмёт уровни и потребует большого количества сегментов на уровне, что, в свою очередь, скажется на производительности.

Я бы сказал, что пять — шесть, ну максимум семь сегментов это оптимальное их количество.

Свет и тень

С дорогой, её сегментами и фокусным расстоянием, я надеюсь, всё более-менее понятно, остаётся только разобраться с запеканием теней в текстуры моделей и можно будет переходить в игровой движок.

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

Есть два пути решения задачи: можно создать отдельную текстуру, на которую записать данные об освещённости модели, либо записать информацию об освещённости на ту же текстуру, на которой уже записана информация о цветах модели.

Я решил записать все данные о цвете, свете и тенях в одну текстуру. Причина, по которой я решил поступить именно так, заключается в шейдерах, подробнее о них, я надеюсь, поговорим в следующих частях. Сейчас же скажу только то, что шейдеры используются для отображения текстур на моделях в играх, и шейдеры, которые могут использовать сразу несколько текстур, например, одну текстуру для отображения цветов, другую — для теней и так далее, будут требовать больше ресурсов, чем более простые шейдеры, которые могут использовать только одну текстуру. Если игра планируется в том числе и для мобильных устройств, то лучше запечь всё в одну текстуру.

Сам процесс запекания и его настройки будут отличаться от рассмотренного ранее в предыдущих частях. Разница заключается в том, что раньше надо было переносить информацию с одной модели, содержащей цветовую информацию в одном или нескольких материалах, на другую модель, содержащую только один материал и пустую текстуру, куда и записывалась эта информация, теперь же надо добавить на уже запечённую текстуру информацию о том, где должна находится тень.

Добавляем тень

Добавляем тень

Для простоты изложения представим, что у нас имеется следующая сцена: дерево и плоскость, на которой это дерево находится. Пример нашей сцены изображён на левой части рисунка «Добавляем тень». Наша задача сделать так, чтобы наши дерево и плоскость приобрели тени, добавив глубины и объёма изображению, в результате чего сцена бы стала выглядеть, как показано на правой части рисунка.

Текстуры дерева и плоскости показаны на рисунке ниже: слева текстура дерева, справа — плоскости. Текстуры содержат только однотонные цвета без каких либо изменений яркости. 

Текстуры до запекания

Текстуры до запекания

Технически мы будем брать цветовую информацию из той же текстуры, в которую мы будем записывать итоговый результат, поэтому галочки «Selected to Active» и «Clear Image» должны быть сняты. Галочка «Selected to Active» должна быть снята по вполне понятной причине: запись текстур производится во всех выделенных моделях, а не только в одной, которая является активной.

Галочка «Clear Image» должна быть снята, чтобы воспрепятствовать стиранию текстуры перед записью. Проблема заключается в том, что когда настройка «Clear Image» активна, то перед началом запекания, текстура, в которую сохраняется вся итоговая информация о цвете, тенях, отражениях и прочем, что может быть выбрано в настройках, будет полностью очищена, то есть закрашена чёрным цветом. Поскольку информация о цвете берётся из только что стёртой текстуры, то итогом запекания с такой настройкой станут модели полностью чёрного цвета. 

Clear Image

Clear Image

Тип запекания следует оставить тот же, что применялся ранее в других частях — «Diffuse». В блоке настроек влияния «Influence», нужно установить все три галочки раздела «Contributions»: «Direct», «Indirect» и «Color». Если установить только галочку только напротив пункта «Color», тени не запишутся, и текстуры останутся без изменений. Если же сделать наоборот, отметить галочками только пункты «Direct» и «Indirect», ведь цвета на текстуре уже есть, зачем их записывать повторно, то запишутся только тени и отражения, а цвета с текстуры пропадут, оставив вместо себя лишь белое поле. Пример на картинке ниже.

Запись только теней и отражений

Запись только теней и отражений

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

Настройки

Настройки

Но прежде чем нажать на кнопку «Bake» и насладиться результатом, я советую где нибудь сохранить резервную копию всех текстур, которые будут участвовать в запекании, на случай если что-то пойдёт не так, и текстуры запишутся с ошибками.

Для того, чтобы быстро собрать текстуры в одном месте можно воспользоваться описанным далее методом. Сначала упаковываем ресурсы в blender-файл, для чего нужно воспользоваться пунктом «Pack Resources», который находится в подменю «External Data» меню «File». «Pack Resources» просто сохраняет все текстуры внутри blender-файла, не требуя никаких дополнительных настроек.

Следующим шагом распаковываем ресурсы, сразу под пунктом «Pack Resources» находится пункт «Unpack Resources», нажатие на него открывает меню в котором можно выбрать один из способов распаковки ресурсов. Открывшееся меню называется «Unpack 3 Files», цифра в названии обозначает количество файлов с изображениями, которые будут распакованы. Если в нём выбрать пункт «Use files in current directory (create when necessary)», то в той же директории, где находится blender-файл, будет создана папка «textures», внутри неё будут все текстуры моделей из текущего blender-файла. Её можно скопировать в какое-нибудь надёжное место, тем самым создав резервную копию.

Создание резервной копии

Создание резервной копии

Резервные копии созданы, все модели, участвующие в запекании выделены — можно запускать процесс запекания, нажав кнопку «Bake». По истечении некоторого количества времени, процесс завершится, и можно будет увидеть следующие изменения на текстурах моделей.

Текстуры с тенями

Текстуры с тенями

Текстуры теперь содержат не только одни цвета, но и тени, отбрасываемые на поверхности моделей. Сейчас самое время проверить полученные текстуры на наличие ошибок, искажений и прочих артефактов. 

Сохраняем текстуры

Сохраняем текстуры

Если искажений нет, и тени запеклись в текстуру правильно, текстуры нужно сохранить. По умолчанию blender не сохраняет сгенерированные текстуры в файлы, поэтому закрыв программу, мы можем потерять все изменения. Просто сохранить blender-файл при помощи комбинации клавиш «Ctrl + S» не получится, файл, конечно, сохранится, но не изменения в текстурах, их придётся сохранять отдельно. Для чего можно перейти в рабочее пространство «UV Editing» и в редакторе «UV Editor» выбрать пункт «Save All Images» в меню «Image». Запущенная команда сохранит все изменённые текстуры в файлы.

Заключение

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

Немного коснулся настроек камеры, ведь без неё довольно сложно определить влияние величины угла зрения на то, как игрок будет видеть мир игры, и насколько хорошо он будет ощущать скорость движения по дороге. Не зная конечного фокусного расстояния камеры будет сложно компоновать уровни так, чтобы модели были хорошо видны игроку, но при этом больших зазоров между моделями не было.

Описываемую игру можно скачать тут, так же удобно воспользоваться приложением для загрузки и установки игр с itch.io, которое скачивается здесь.

© Habrahabr.ru