Захват перспективы в игре Renovation

Игр, которые используют механику типа "what you see is what you get", существует уже немало. Наверное один из самых заметных релизов последнего времени - Superliminal.

В нашем проекте мы еще экспериментируем с механиками, которые лучше помогли бы раскрыть одну из тем игры - ограниченность внимания и восприятия. Так почему бы в 2.5D платформере не дать игроку возможность "заглядывать за угол"?

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

Идея

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

Все прекрасно знаю, как выглядит нормальная перспектива и как она меняется при движении камеры.

В полностью двухмерных играх, если вы хотите изобразить комнату, можно нарисовать ее с какой-то определенной фиксированной перспективой, как это сделано в Dex:

Захват перспективы в игре Renovation

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

Сторонний наблюдатель

Проще всего описать этот эффект, если представить себе стороннего неподвижного наблюдателя. Именно он смотрит на наш объект всегда под одним углом. И "фиксирует" тем самым перспективу для этой модели. И мы хотим перенести его "видение" в нашу главную камеру.

Зеленый - наблюдатель. Синий - главная камера.

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

Вот так объекты "следят" за камерой в уже готовой реализации:

Реализация

Как вы уже догадались, имплеменьтить эту механику будем на Unity. И в качестве "стороннего наблюдателя" мы используем еще одну камеру.

Реальность, как обычно, оказалась сложнее теории. Математика в "наивной реализации" довольно простая. Знай, находи себе пересечения лучей с плоскостью. Но камеры в Unity устроены сложнее. И наша исходная идея не учитывает, что камера может быть повернута. А это важный момент для игры. Сейчас многие фиксирующие перспективу камеры смотрят на платформы сверху и под небольшим углом. Так выглядит симпатичнее. А еще поворот наблюдателя может делать вот что:

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

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

Захват перспективы в игре Renovation

Каждое преобразование точки из одной системы координат в другую - умножение матрицы на вектор с координатами точки.

Честно, я долго пытался придумать, как просто описать этот процесс, не объясняя, что такое однородная система координат (Homogeneous Coordinates). И не придумал. Поэтому если вам интересно, или вы забыли - достаточно хорошо вся математика описана здесь. А сейчас мы для простоты будем считать, что нахождения экранных координат нам нужно умножить наши матрицы на координаты исходной точки:

Coords = Projection*View*Model*Vector3 (математика сработает только в таком порядке, а читать типа надо справа налево)

То самое место, где в учебниках по математике пишут "Очевидно, что..."

Вообще матрицы проекций - богатая тема для всяких интересных эффектов в играх.

Но мы отвлеклись.
Алгоритм нашего преобразования вершин объекта примерно такой:

  1. Проецируем исходный объект в экранные координаты камеры-наблюдателя.
  2. Переносим полученные координаты из пространства экрана наблюдателя в пространство экрана главной камеры.
  3. Осуществляем обратную трансформацию. Теперь уже из экранных координат переносим назад в трехмерные координаты нашей сцены. Поскольку в одну точку экрана можно спроецировать бесконечное количество точек на луче взгляда, мы решаем эту задачу, сохраняя исходную координату Z.
Захват перспективы в игре Renovation

Поскольку речь идет о преобразовании вершин объекта, нам не обойтись без шейдера! Я не планировал это как туториал по написанию шейдеров, поэтому просто оставлю здесь исходный код. Он довольно прямо следует процессу, нарисованному на картинке.

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

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

Движение

Хорошо, мы имеем статическую картинку, похожую на нарисованную. Как теперь получить желаемый эффект?

Тут все элементарно. Мы просто начинаем перемещать нашего наблюдателя, и перспектива меняется!

Внимание на правую сторону

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

Физика

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

Когда дела касалось визуала, мы хотели сразу увидеть более-менее готовый вариант. Важно было понять, как это будет видеть игрок.
А вот с физикой можно и упростить себе жизнь. Эта механика экспериментальная, и если она таки пойдет в работу - мы подумаем над генерированием новых коллайдеров на лету. А сейчас хочется скорее поиграть в то, что у нас получилось! Поэтому пойдем по пути fake it until you make it.

Посмотрим на нашу сцену. Поскольку в плоскости Z=0 у нас никакая геометрия не искажается - все коллайдеры можно расставить руками статично. Так, чтобы они обрисовывали уровень. В этом прототипе мы используем двумерную физику для простоты.

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

Начнем с того, что временно выключим захват перспективы и создадим BoxCollider2D, который бы полностью вписывал эту поверхность.

Захват перспективы в игре Renovation

А теперь дело за малым. Надо совершить ровно те же преобразования с точками коллайдера, что и с вершинами модели. Но в этом упрощенном случае будет достаточно нашего самого первого варианта: просто найти точки пересечения луча из камеры через вершину с плоскостью Z=0.

И вот результат:

Заглянем с другой стороны

Геймдизайн

Статья задумывалась технической. Но нельзя не упомянуть, какие перспективы это открывает с точки зрения дизайна уровней. Вот только некоторые из них:

  • Прятать предметы и части террейна
  • Давать возможность уменьшать расстояние между краями ям
  • Открывать скрытые проходы
  • Закрывать открытые проходы)
  • Подымать предметы или ронять их
  • Обманывать врагов
  • Менять угол склонов, делая их проходимыми или непроходимыми
  • ???

Сейчас проверка этих гипотез - наша актуальная задача. А еще поиск видения, как это будет сочетаться с финальной графикой игры.

Давайте так. Если статья наберет 10 лайков, то до конца фестиваля я сделаю пост с гифками как минимум 10 возможных ситуаций в игре, где это механика может себя интересно показать. Если не наберет - я хорошо отдохну на выходных. Оба варианта классные :)

Все украдено до нас

Идея этой механики вроде бы пришла к нам в голову довольно спонтанно. Но покопавшись в Интернете мы, конечно, выяснили, что далеко не первые, кто пришел к такому видению. Тем лучше! Столько референсов кругом.

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

Надеюсь, вам было так же интересно читать, как нам это придумывать. Если статье не хватает каких-то подробностей, смело пишите в комментах.

Наши прошлые публикации для фестиваля:

Мы в социальных сетях:

1010
2 комментария

Классный трюк!

1
Ответить

Залогинился, что бы добавить в закладки 😃😉

Ответить