![](https://habrastorage.org/getpro/habr/upload_files/288/b2e/34d/288b2e34ddb6dedccb4066de39c50fd8.jpg)
Транскрипция доклада Александра Паничева с UNIGINE Open Air 2022
Сегодня мы узнаем:
можно ли считать людей, которые инвертируют оси в играх, лицами с нетрадиционной ориентацией;
что такое фокусное расстояние объектива и для чего оно нужно;
какой угол обзора надо делать у камер, чтобы монитор выглядел, как окно в виртуальный мир;
как правильно фотографировать;
и сделаем простенький симулятор дрифта.
Камера должна быть:
УДОБНОЙ! Должна помогать игроку играть в игру, а не мешать ему.
АТМОСФЕРНОЙ. Круто, если она будет стараться передавать еще и атмосферу момента. Нагнетать или, наоборот, расслаблять.
Сложно сказать, как сделать камеру удобной. Гораздо проще сказать, в какие моменты она неудобная. Например, Dark Souls и старенькую Супер Марио 3D World все ругают за то, что там часто куда-то убегаешь за стену и не видишь вообще, где персонаж находится. Тут всё очевидно: если неудобно - значит, нехорошо. И надо с этим что-то делать.
![Dark Souls Dark Souls](https://habrastorage.org/getpro/habr/upload_files/08b/3fc/086/08b3fc08665edf7ff23e71b0e59c6795.gif)
![Super Mario 3D World Super Mario 3D World](https://habrastorage.org/getpro/habr/upload_files/f9e/b44/8ce/f9eb448ce13ada9898e178d0e5122f06.gif)
Во-вторых, камера должна погружать в атмосферу, и тому есть два примера: Gears of War и Silent Hill.
![Gears of War Gears of War](https://habrastorage.org/getpro/habr/upload_files/2ee/7b7/751/2ee7b7751413e4769bda24876bf9b3f2.gif)
В Gears of War в момент бега камера становится эдаким виртуальным оператором, который бежит вслед за игроком и создает дополнительное ощущение экшена.
![Silent Hill Silent Hill](https://habrastorage.org/getpro/habr/upload_files/ede/833/180/ede833180e94c46c68e4f7ffe5ca9c65.gif)
А в Silent Hill, с одной стороны, вроде кажется, что камера неудобна, но на самом деле это довольно интересный прием: когда мы только заходим в какую-то комнату, мы не видим, что находится впереди, мы видим только самого игрока. Мы слышим, что где-то есть какой-то монстр, и он приближается, а где именно - не понятно, пока не уткнемся в него.
Начнем разбирать приемы на примерах.
Камера от первого лица.
Субъективно, камера от первого лица делится на две группы:
1. Игрок смотрит на виртуальный мир глазом персонажа. То есть, мы, как игроки - это такой глазик, который ходит, смотрит по сторонам и гуляет.
![](https://habrastorage.org/getpro/habr/upload_files/c4e/0ff/68e/c4e0ff68e5cc9b30135fe35098f66fae.gif)
2. Игрок смотрит на виртуальный мир через виртуальную камеру, которую которую держит персонаж.
![](https://habrastorage.org/getpro/habr/upload_files/c12/c76/3b7/c12c763b7f60bcf7785138ccc78e56dd.gif)
Звучит странно и непонятно, давайте разберем на примере:
![Alien: Isolation Alien: Isolation](https://habrastorage.org/getpro/habr/upload_files/daa/5f0/22d/daa5f022d391ab7f64704875a106da9e.png)
В Alien: Isolation игровая камера имитирует кинопленку, фильм 80-ых годов. На это указывают:
Анаморфные блики линз;
Зерновой шум;
Виньетирование;
Глубина резкости длиннофокусного объектива.
![Аlien: Isolation Аlien: Isolation](https://habrastorage.org/getpro/habr/upload_files/2e6/5ef/ae0/2e65efae0654d47b15fe1b11ff55ca25.gif)
Во время движения камера всегда покачивается, а когда мы прячемся, камера болтается и вообще очень сильно кренится.
Как понять разницу между первым и вторым типом? Если начать наклонять голову от одного плеча к другому, то вы всегда будете знать, где верх, где низ. Мир не будет болтаться и крутиться. Но если снимать на телефон и начать крутить его, то на изображении мир будет вращаться.
В этом главное различие этих двух типов: первый тип комфортен для игроков, их не укачивает, это ближе к тому, что они видят в жизни, а второй тип более кинематографичен, но больше укачивает.
Что еще можно сказать про камеру от первого лица?
Это самый простой тип камер (не зря большинство инди - это игры от первого лица).
Есть куча готовых шаблонов (в Unigine - это PlayerActor и CSPlayerActor из семплов, созданные специально для расширения функционала).
Вопрос: а как мы, собственно, расширяем функционал камер? Что делаем с ними?
Вариантов много. Рассмотрим добавление вторичных анимаций, "второго слоя" у камер.
Процедурная анимация камеры: рандом
Например, происходит взрыв, и камера начинает трястись.
![](https://habrastorage.org/getpro/habr/upload_files/d7a/0c6/595/d7a0c6595f8b8d9e195ac0d3b01f5516.gif)
Здесь используется translate-смещение камеры. То есть мы смещаем камеру рандомными числами с плавным затуханием, чтобы имитировать землетрясение или взрыв.
Camera cam = Game.Player.Camera;
float cur_strength = strength * EaseInCubic(MathLib.Saturate(1 - time));
float x = Game.GetRandomFloat(-cur_strength, cur_strength);
float y = Game.GetRandomFloat(-cur_strength, cur_strength);
cam.Modelview = MathLib.Translate(new vec3(x, y, 0)) * cam.Modelview;
При использовании этого варианта в играх, где очень много взрывов, восприниматься это будет комфортно, игрока не будет тошнить. Какие еще есть варианты?
Процедурная анимация камеры: sin/cos
![](https://habrastorage.org/getpro/habr/upload_files/fed/93e/d3c/fed93ed3c3e37d7aed831ad83fcc9643.gif)
Можно использовать тригонометрию. Есть достаточно известная формула:
![](https://habrastorage.org/getpro/habr/upload_files/159/985/223/159985223d37010ce9c34a78ca8f366c.png)
В UNIGNE:
float amplitude = 0.75f;
float frequency = 0.75f;
float pitch = MathLib.Sin(frequency * 2.0f * Game.Time) * amplitude * 0.5f;
float yaw = MathLib.Cos(frequency * Game.Time) * amplitude;
Camera cam = Game.Player.Camera;
cam.Modelview = new quat(pitch, yaw, 0).Mat4 * cam.Modelview;
Замечательность этой функции в том, что она выглядит графиком бесконечности, и много где используется. Например, если нужно вывести на экран статистику прохождения миссии: поверх статичной картинки это выглядит скучно, и нужно добавить немного объема, довольно удобно.
Но если мы поменяем sin и cos местами, внезапно график станет дугообразным:
![](https://habrastorage.org/getpro/habr/upload_files/c9e/ae1/27b/c9eae127b5d8351e7080978a8cef2d84.png)
В UNIGNE:
float amplitude = 0.375f;
float frequency = 7.5f;
float x = MathLib.Sin(frequency * Game.Time) * amplitude;
float y = -MathLib.Cos(frequency * 2.0f * Game.Time) * amplitude * 0.5f;
Camera cam = Game.Player.Camera;
cam.Modelview = MathLib.Translate(x, y, 0) * cam.Modelview;
![](https://habrastorage.org/getpro/habr/upload_files/808/52c/9e7/80852c9e7fcb9d3b71147557d9caf344.gif)
Таким образом можно имитировать бег персонажа, или как будто ему тяжело, он нагружен тяжелым снаряжением. Можно к этому примеру добавить вращение:
![](https://habrastorage.org/getpro/habr/upload_files/32d/565/827/32d5658272d9cd3653ba74f65e41baaf.gif)
Если уделить пару минут и посмотреть на обе гифки, станет понятно, что на вторую смотреть уже не хочется. На первой бег выглядит более реалистичным, но менее кинематографичным. Второй вариант с вращением лучше подходит для катсцен, когда нужно показать, что происходит какой-то экшн, но долго смотреть на него не хочется.
Вывод: лучше смещать, чем вращать.
Какие еще варианты?
Процедурная анимация камеры: шум Перлина
![](https://habrastorage.org/getpro/habr/upload_files/890/33f/b7e/89033fb7ec6b4cd7c71284c20b95d54f.png)
Шум Перлина - генератор случайных чисел, только сглаженный. Имитирует движение hand-held camera: мы живые люди, мы не можем четко сфокусироваться на объекте и снимать его, руки все равно немного болтаются.
![](https://habrastorage.org/getpro/habr/upload_files/bb1/585/47b/bb158547b69db09c53069f48ea187264.gif)
В UNIGINE это функция GameGetNoise:
float amplitude = 5.0f;
float frequency = 0.5f;
float pitch = Game.GetNoise1(Game.Time * frequency, 100.0f, 1) * amplitude;
float yaw = Game.GetNoise1(10.0f + Game.Time * frequency, 100.0f, 1) * amplitude;
float roll = Game.GetNoise1(15.0f + Game.Time * frequency, 100.0f, 1) * amplitude * 0.5f;
Camera cam = Game.Player.Camera;
cam.Modelview = new quat(pitch, yaw, roll).Mat4 * cam.Modelview;
Ничего сложного не используем, а картинка сразу становится живой. Можно применять, когда игрок останавливатся, а когда пошел - выключаем. Тогда игра будет живенькая, не будет казаться статичной.
Камера для платформера
Существует достаточно много подходов. Все они, скорее, дополняют друг друга, нежели являются противоположными вещами.
![](https://habrastorage.org/getpro/habr/upload_files/20e/aa7/56d/20eaa756d8c5ee12e35d802efe73b026.png)
Платформер: фиксированная камера
![GameMakerStation - Matharoo GameMakerStation - Matharoo](https://habrastorage.org/getpro/habr/upload_files/b42/bb1/ff6/b42bb1ff6096c31eed32e16bb9c90705.gif)
Плюсы:
легко реализовать. Просто центрируем персонажа, и больше ничего делать не нужно: он всегда находится по центру экрана, и насколько сместился персонаж, настолько сместилась и камера.
Минусы:
Вызывает тошноту у игроков при постоянных прыжках от крена камеры и резких вертикальных перемещений;
При резком разгоне и моментальной остановке сложно следить за обстановкой слева и справа от персонажа;
Иными словами, так делать не надо. А какие еще варианты?
Платформер: lerp (плавно следящая по средней)
Вот так выглядит:
![Cinemachine for 2D: Tips and Tricks Cinemachine for 2D: Tips and Tricks](https://habrastorage.org/getpro/habr/upload_files/947/4c0/ffd/9474c0ffd1218242c5627ae2090d9f47.gif)
![Easy Smooth Camera Follow With Cinemachine Easy Smooth Camera Follow With Cinemachine](https://habrastorage.org/getpro/habr/upload_files/22f/111/c81/22f111c8193b82082c11f8fa3694450b.gif)
![Spelunky Spelunky](https://habrastorage.org/getpro/habr/upload_files/338/16f/f62/33816ff62ca6704255e00b5b9225bb5f.gif)
Камера "догоняет" персонажа, плавно к нему подходит.
Вот так пишется:
cam_pos = lerp(cam_pos, player_pos, rate * deltaTime);
Плюсы:
Более приятное и реалистичное поведение камеры;
Реализация в одну строчку без дополнительных переменных! Это ли не истинное счастье?
Минусы:Не frame-independent. Плавность камеры зависит от фреймрейта;
Всё еще вызывает тошноту при постоянных прыжках.
Что можно с этим сделать? Можно подправить один из моментов, сделав функцию frame-independent, используя Exponential Decay / Smoothing / Moving Average (экспоненциальное затухание / сглаживание / скользящая средняя):
position = lerp(position, target, 1.0f - exp(-rate * ifps));
С первой причиной, почему lerp не любят в среде геймдева, разобрались, сделав функцию frame-independent. Вторая причина - не понятно, за сколько секунд камера достигнет персонажа, или нельзя высчитать скорость, с которой камера доедет до персонажа за секунду. Мало контроля. Не нашли решения в интернете, поэтому изобрели свое:
position = lerp(position, target, 1.0f - exp(-rate * ifps));
duration = log(range / epsilon) / rate;
rate = log(range / epsilon) / duration;
![](https://habrastorage.org/getpro/habr/upload_files/25f/935/f5f/25f935f5fe074f7cef768438712f7bd6.png)
Все работает, можно пользоваться. И при линейной интерполяции можно и позицию, и скорость, и продолжительность найти, все известно. Но есть и другие, более интересные варианты.
Платформер: окна
![](https://habrastorage.org/getpro/habr/upload_files/1ac/04d/7f2/1ac04d7f2ae1efec9e49f16401a93079.gif)
Появляется некое виртуальное окно, в пределах которого камера не движется: если персонаж ходит внутри него, то камера стоит на месте. Как только игрок пытается выйти за пределы, начинается смещение.
Вот так пишется:
if (isPlayerOutsideWindow())
correctCameraPosition();
Плюсы:
Камера не болтается во время экшена и при хаотичных движениях игрока;
Если окно достаточно большое по вертикали, то прыжки не смещают камеру - играть комфортно!
Минусы:
Придется программировать камеру. Но… Разве это сложно? :)
Вот как это делается для платформера в 3D:
Перевести координаты игрока в экранное пространство:
Unigine: Player::getScreenPosition(), Unity: Camera.WorldToScreenPoint()Проверить, выходим ли мы за пределы окна;
Если да, то нам надо сместить камеру по плоскости, проходящей через игрока (в отличие от 2D, в 3D у нас есть некая плоскость, которую мы можем контролировать). Для этого нам надо найти два вектора: вектор из камеры в сторону игрока и вектор из камеры в сторону ближайшей границы окна.
Unigine: Player::getDirectionFromScreen(), Unity: Camera.ScreenPointToRay()Находим точки пересечения векторов с плоскостью (которую выбираем сами!) и их разница - это и есть то самое искомое смещение для камеры:
Unigine: Math::rayPlaneIntersection(), Unity: Plane.Raycast()Смещаем камеру на полученное значение!
Есть еще интересные возможности в 3D: задав плоскость по-разному, мы получаем разное поведение камеры, когда персонаж хочет выйти за границы окна.
Например, если нормаль поверхности смотрит вверх, т.е. параллельно плоскости земли, то при выходе за границы камера не перемещается ни выше, ни ниже, всегда находится на одном уровне и движется параллельно плоскости Земли.
![plane_normal = vec3_up; plane_normal = vec3_up;](https://habrastorage.org/getpro/habr/upload_files/f25/a38/9f4/f25a389f49ab91348141832d203e712a.gif)
Второй случай - плоскость выставлена так, что она перпендикулярна точке зрения камеры и в этом случае получается, что если персонаж приближается, камера опускается. Можно делать какие-нибудь прикольные эффекты, где в конце игры игрок уходит в закат и камера плавно поднимается, а там небо красивое :)
![plane_normal = -camera_direction; plane_normal = -camera_direction;](https://habrastorage.org/getpro/habr/upload_files/f6b/91a/00f/f6b91a00f56a31dc42547143b564fd3e.gif)
Хотите подробнее разобраться в этом? Добро пожаловать на GitHub, там есть полные исходники этого примера с инструкцией.
Ну а мы едем дальше.
Плюсы окон в том, что они подходят не только для платформеров, но и используются в 3D-играх, в футбольных/хоккейных симуляторах - когда нужно следить за мячом, но мягко: чтобы он не был всегда строго по центру, а находился в поле зрения, а еще лучше, чтобы и сам футболист/хоккеист и шайба.
![](https://habrastorage.org/getpro/habr/upload_files/d9b/67f/be3/d9b67fbe31552d08a7de283dda79c0cc.gif)
Что еще еще добавляют?
Платформер: окна + скользящая средняя + ...
Рассмотрим на примере:
![Tiny Thor Tiny Thor](https://habrastorage.org/getpro/habr/upload_files/4ea/9f0/b05/4ea9f0b05fb1f15e26c517b917e7e416.gif)
![Tiny Thor Tiny Thor](https://habrastorage.org/getpro/habr/upload_files/d0b/227/53a/d0b22753aa357cba9621fc6a616ca4de.gif)
В статье разработчики Tiny Thor подробно рассказали, какие приемы они используют:
- Правило третей (спереди видно ⅔ экрана, а сзади только ⅓, чтобы видеть всех врагов впереди);
- Смещают вертикально камеру только тогда, когда персонаж приземляется, что уменьшает количество вертикальных болтаний;
- Два окна: в пределах внутреннего окна, про которое было сказано выше, камера смещается плавно, а в пределах внешнего (окно паники) - резко.
Ну а теперь можно спокойно переходить к следующему большому разделу.
Камера от третьего лица
Рассмотрим ее на примере гонок. В целом, все повторяется и все достаточно просто. Итак, пример первый:
Камера от третьего лица: фиксированная
У нас есть два объекта: машина и камера, делаем камеру дочерним объектом к машине, и все! Теперь камера привязана к машине, и куда едет машина, туда и смотрит камера.
Как это выглядит:
![](https://habrastorage.org/getpro/habr/upload_files/e09/315/b69/e09315b69a05bf56790fe94ce36f727a.png)
![](https://habrastorage.org/getpro/habr/upload_files/b70/bf3/082/b70bf3082b39d1c075a9ced7fd19d80e.gif)
![](https://habrastorage.org/getpro/habr/upload_files/431/db0/815/431db0815d517b706d04e7ee2af332ab.gif)
![](https://habrastorage.org/getpro/habr/upload_files/39f/2f5/2c8/39f2f52c8f946e05a53d8a3370ebdcec.gif)
Минусы:
Не видно, что за поворотом, пока окончательно не повернешь;
Дрифт делать сложно - ничего не понятно;
Любое мелкое движение сильно кренит и болтает камеру.
Неприкольно! Что будем делать? Использовать линейню интерполяцию.
Камера от третьего лица: через lerp
Как писать:
float rate = 10.0f;
float distance = 5.0f;
vec3 cam_dir = (car_pos - cam_pos).normalize();
vec3 cam_pos_final = car_pos - cam_dir * distance;
cam_pos = lerp(cam_pos, cam_pos_final, 1.0f - exp(-rate * ifps));
cam_node->setWorldPosition(cam_pos);
cam_node->setWorldDirection(cam_dir, vec3_up, AXIS_NZ);
Как выглядит:
![](https://habrastorage.org/getpro/habr/upload_files/4d6/aa8/2d7/4d6aa82d7b2b0fe1988c1db26a451cbf.gif)
![](https://habrastorage.org/getpro/habr/upload_files/58c/89b/ab1/58c89bab19004642049278392173e6c3.gif)
Плюсы:
Уже можно дрифтовать более-менее с комфортом. Уже видно, в какую сторону автомобиль движется;
Чувствуется ускорение и торможение автомобиля. Когда начинаем ехать, камера "тормозит" и доезжает до цели медленнее, а при торможении камера "набегает"
на автомобиль, и все это одной строчкой кода.
Минусы:
Относительно мало контроля: мы не можем сделать так, чтобы во время ускорения поведение было одно, а во время торможения - другое. А если куда-нибудь вправо или влево движемся, не можем сделать вторичную анимацию ускорений и торможений. Например, как при торможении сидя за рулем, мы движемся сначала вперед к лобовому стеклу, а потом нас резко откидывает назад, или даже немного болтаемся. С помощью lerp это сделать нельзя.
Какие еще есть варианты? Поскольку мы разбираем пример с гонками, то всегда можно из машины "вытащить" скорость и ускорение. Вообще, это полезно делать, даже если вы не гонки делаете, а любую игру с камерой от третьего лица. Скорость и ускорение доставать легко, это просто дифференцирование.
Камера от третьего лица: velocity + acceleration
Ускорение (acceleration) мы используем для смещения камеры, а скорость (velocity) - для вращения.
Что получается? В случае со смещением, когда мы начинаем торможение, вектор ускорения начинает тянуться назад, мы берем отрицательное значение ускорения, и тем самым камеру начинаем приближать к лобовому стеклу. Как только остановились, вектор может чуть-чуть туда-сюда поболтаться, и создается натуральное ощущение ого, что мы притормозили, или начинаем разгоняться, когда камера плавно отъезжать назад, а заnем возвращается в свое нулевое состояние, и мы еще и можем контролировать это ускорение по разным осям. В разных ситуациях можем задать, чтобы отрицательное ускорение чуть быстрее увеличивается, а положительное остается неизменным, и т.д.
Скорость (velocity) позволяет нам делать дрифт. Во время дрифта мы знаем, куда движется автомобиль, камера старается смотреть. Помимо того, что исчезают крены, становится более-менее комфортно.
![](https://habrastorage.org/getpro/habr/upload_files/691/a7f/eab/691a7feab24d886ac7db7dc4c20d4d16.gif)
![](https://habrastorage.org/getpro/habr/upload_files/834/31f/425/83431f42535627ad2f8e4a805e4e8db1.gif)
![](https://habrastorage.org/getpro/habr/upload_files/99e/3f8/747/99e3f874748ec63a2923da331e1d3366.gif)
Плюсы:
Максимум контроля;
Можно имитировать перегрузку во время ускорения и торможения.
Минусы:
Много данных, с которыми надо работать.
Псевдокод:
camera_offset = -car_acceleration;
camera_rotation = lookAt(car_velocity);
На самом деле, кода там уже достаточно прилично: надо использовать lerp, надо использовать DSP (Digital Signal Processing) -приемчики, чтобы все шероховатости сгладить. Общий вектор такой.
Сравним еще раз, от первого лица.
Фиксированная камера:
![](https://habrastorage.org/getpro/habr/upload_files/4ad/215/8b4/4ad2158b40206575723d4537bd125bf8.gif)
Попадая в занос, вообще не понятно, в какую сторону движется автомобиль и вариантов того, чтобы его выровнять в игре, достаточно мало.
Скорость и ускорение:
![](https://habrastorage.org/getpro/habr/upload_files/0c2/5ed/538/0c25ed5386ec63f77a4643d88687edfd.gif)
Магическим образом камера начинает смотреть в сторону движения автомобиля, и так больше похоже на реальную жизнь, и видим, куда она летит, в столб или на встречку. Все, можно делать игру с гонками :)
Ноооо есть еще один вариант, что можно делать с камерой от третьего лица в гонках, но это уже из области дзена и пограничных приколов.
Камера от третьего лица: смотрим вперед вдоль пути
Можно направлять камеру по направлению пути, если мы знаем, где она окажется через 2-3 секунды, с какой скоростью автомобиль едет сейчас, с какой скоростью будет ехать позже, то мы можем плавно доводить камеру до точки, где мы окажемся. Т.е имитируется реальное поведение, когда мы, например, должны повернуть направо через 200 м, и мы уже стараемся смотреть туда и плавно поворачиваем, когда нужно.
![](https://habrastorage.org/getpro/habr/upload_files/c37/d03/e8b/c37d03e8b5abdb45c7390204a5d00e48.gif)
![](https://habrastorage.org/getpro/habr/upload_files/0cd/fcf/f45/0cdfcff455a1fe7e9ce6d8c70c719fde.png)
camera_rotation =
lookAt(car_predicted_position);
Предполагаемую позицию игрока легче всего найти в каких-нибудь кольцевых гонках, потому что мы трек создаем сами, как правило, это какие-то сплайны (splines), либо вейпойнты (waypoints) для ботов. Для этого просто достаточно найти, где находится следующая точка, и с текущей скоростью мы можем понять, через сколько секунд в каком месте мы окажемся.
Это самое натуральное поведение. Но тут возникает уже другой вопрос: от игрока уже мало, что зависит, и насколько нужно отбирать у игрока контроль, или стоит ему оставить больше свободы? Тут уже зависит от того, какую гонку вы делаете: казуальную или хардкорную.
Тем не менее, такой вариант есть, и его тоже нужно иметь в виду.
Переход от катсцен к геймплею
Возьмем Code Vein. Игрушка классная, но переходы от катсцен к геймплею какие-то "рваные". Камера подъезжает к персонажу - БАХ - и наступает геймплей. Почему нельзя сделать более плавный переход или хотя бы fadein/fadeout? Причем, много катсцен, где камера останавливается около игрока, но не там, не в той точке, не под тем углом - печально.
![Code Vein Code Vein](https://habrastorage.org/getpro/habr/upload_files/a3d/8c3/eab/a3d8c3eab0840d06f3e3941bf80f8dd5.gif)
Совсем другое дело - Naughty Dog с их Last of Us и Uncharted. Отличный пример "бесшовного" перехода от катсцен к геймплею:
![Uncharted 4 Uncharted 4](https://habrastorage.org/getpro/habr/upload_files/380/d73/118/380d7311882f85b1b63ba397d6834212.gif)
![Uncharted 4 Uncharted 4](https://habrastorage.org/getpro/habr/upload_files/bf3/1c7/14f/bf31c714f37d1426f212bc9b8d66dade.gif)
AAA-тайтлы отличаются плавностью камеры, плавностью анимаций и переходов от одной фазы к другой. Стоит стремиться к подобной плавности при разработке игры мечты :) Остановимся подробнее на том, как это реализовать. Смотрим на пример плавного перехода:
![Пример плавного перехода от катсцены к геймплею Пример плавного перехода от катсцены к геймплею](https://habrastorage.org/getpro/habr/upload_files/e51/51e/1bb/e5151e1bb012ec007e5cf964a356ac6e.gif)
Для наглядности, смотрим на пример без плавного перехода:
![Без плавного перехода Без плавного перехода](https://habrastorage.org/getpro/habr/upload_files/738/7aa/7bf/7387aa7bfbd3744afaff31dee1e8c14c.gif)
Разница очень выразительная. Во втором примере переход от катсцены к геймплею очень резкий. В виде графика это можно представить следующим образом:
![](https://habrastorage.org/getpro/habr/upload_files/09a/7b5/cc9/09a7b5cc99143a7ffed110bb142fca4b.png)
Представим, что ось ординат - это время, а ось абсцисс - примерно положение смещения. Три полосы, которые вы видите на графике - это три камеры. Первая - это камера катсцены, которая как-то перемещается, едет к своему окончанию, переход - это вторая камера, которая плавно интерполируется между катсценой и геймплеем, и третья, соответственно, геймплей, с этого момента уже можно управлять игрой. Вот такой пример без плавного перехода, прямо как в Code Vein. По факту, нету тут второй камеры, нехорошо. Как мы можем улучшить ситуацию? Можем использовать ту же самую линейную интерполяцию.
Выглядит это так:
![Линейная интерполяция между концом катсцены и позицией камеры Линейная интерполяция между концом катсцены и позицией камеры](https://habrastorage.org/getpro/habr/upload_files/2dc/edf/da7/2dcedfda760c5445eb9b5ebcffc4bfa4.gif)
График можно представить следующим образом:
![](https://habrastorage.org/getpro/habr/upload_files/fd5/f69/1dd/fd5f691dde22e71e4864ff40253d1bb1.png)
Что происходит? Идет катсцена, заканчивается, переключаемся на вторую камеру, которая плавно интерполируется между вторым и третьим положением, и на конце включаем геймплейную камеру. Смотрми на изображение: вроде бы все должно было пойти по плану, но нет: камера сначала сильно запаздывает, а потом летит в машину и резко останавливается. Видеооператоры так не делают, не умеют. Конечно же, должно быть несколько фаз: ускорение, торможение, вот это вот все. Что делать? Экстраполируем камеру катсцены после ее окончания и линейно интерполируем до игровой камеры:
![](https://habrastorage.org/getpro/habr/upload_files/357/76b/5d7/35776b5d7172f853cdce753d5591f60e.gif)
На графике:
![](https://habrastorage.org/getpro/habr/upload_files/b47/c80/7af/b47c807afca4d44a876a5048ce34d51f.png)
Мы знаем, как у нас выглядит катсцена, где оказывается камера вконце, знаем предыдущие кадры - из этого мы можем найти скорость и ускорение, причем как линейное, так и угловое, и виртуально продолжить движение камеры. Вторая камера точно также по линейной интерполяции будет доходить до геймплейной. Уже выглядит неплохо, но последняя фаза все равно достаточно резкая. Оператор должен чуть-чуть замедлиться, и в начале чуть-чуть подождать.
Если доводить до идеала, то нужно вместо линейной интерполяции использовать функции плавности Easing Function, в данном случае - easeInOutQuad. Подробнее о функциях плавности рассказывали в этом посте. Итак, экстраполируем камеру катсцены после ее окончания и нелинейно интерполируем до игровой камеры через easeInOutQuad (функцию плавности, easing function).
Результат:
![](https://habrastorage.org/getpro/habr/upload_files/9a6/030/571/9a6030571ffea11a40ab1e1e4ca8e61c.gif)
График:
![](https://habrastorage.org/getpro/habr/upload_files/6be/d30/099/6bed30099e257701df29eec63e2db149.png)
Все у нас получилось, камера двигается, как должна, можно переходить к следующей теме.
![Плавный переход от катсцены к геймплею Плавный переход от катсцены к геймплею](https://habrastorage.org/getpro/habr/upload_files/366/d88/18c/366d8818c023f18209e02916e04dc47a.gif)
Объективы. Кое-что про углы зрения.
![](https://habrastorage.org/getpro/habr/upload_files/9b4/6b0/8f1/9b46b08f128d128b3fb4622a96ad63b8.png)
Фокусное расстояние - это расстояние от оптического центра объектива до сенсора, когда линза сфокусирована на бесконечность. А оптический центр – это место схождения всех лучей в одной точке. Сильно углубляться в тему не будем, достаточно лишь сказать, что фокусное расстояние выражено в миллиметрах на объективах, и влияет на угол обзора.
Самая главная цель этого раздела - донести, что для съемки фильмов, для фотографий используются определенные типы объективов для определенных сцен. Если мы снимаем какой-то красивый пейзаж, мы используем широкоугольники, т.е. примерно 17 мм, для портретов - 85-135 мм, если же мы хотим отразить то, как мы видим реальный мир, берем 50 мм объектив, это тот самый угол обзора человеческого глаза.
В таблице приведены углы обзора соответственно фокусному расстоянию различнх объективов:
![](https://habrastorage.org/getpro/habr/upload_files/781/113/158/7811131586c11f89087deb5ecb46646b.png)
Стандартный угол обзора, который в движке установлен по умолчанию, 60 градусов - это примерно 21 мм, что соответсвует умеренно широкоугольному объективу. Примерно такой объектив ставится на современные смартфоны. Это "ширик", но не ультра.
Как это влияет на восприятие изображений?
![](https://habrastorage.org/getpro/habr/upload_files/c05/706/333/c057063332566b064817434d5beb951d.png)
Чем больше фокусное расстояние, тем меньше угол обзора, и тем больше эффект размытия заднего фона. Об этом нужно помнить при создании катсцен.
16 мм и 200 мм - это две крайности, в которых людей обычно не снимают. 85 мм в этом случае - это идеальное фокусное расстояние, когда и лицо не выглядит ни плоским, ни вытянутым, и нет перспективных искажений.
Наглядно:
![](https://habrastorage.org/getpro/habr/upload_files/9da/f3f/222/9daf3f222cd436f04950c16467e8ac1b.png)
Одна и та же модель, временная разница между снимками - пара минут, все дело в объективе. Зачастую инди-разработчики не меняют стандартных 60 градусов при переходе от геймплея к катсцене, и персонажи, объекты выглядят, мягко говоря, не супер. Делаем катсцену - угол обзора сразу уменьшаем, до 27-17 градусов и получаем красоту.
FOV 60 vs 17 градусов
![Стандартный угол обзора: 60 градусов Стандартный угол обзора: 60 градусов](https://habrastorage.org/getpro/habr/upload_files/ab6/933/322/ab6933322367ec4b717795b3a6d01f60.png)
Смотрим на изображение: довольно большие перспективные искажения, морда автомобиля выглядит непропорционально большой, переднее колесо огромное, ландшафт странно выглядит, еще и этот светофор. Некий эффект создает, имеет право на существование, но обычно для журналов автомобили так не снимают. Снимают так:
![Угол обзора: 17 градусов Угол обзора: 17 градусов](https://habrastorage.org/getpro/habr/upload_files/601/347/fed/601347fed6ec784a10c2ae670ee6e4ae.png)
Длиннофокусный объектив, нет перспективных искажений, нет фонарных столбов и светофоров, легче делать композицию, меньше визуального мусора попадает в кадр, легче кадрировать. Картинка более ортографическая, а если еще и размыть фон, то вообще идеально, при этом объект смотрится естественно. К чему это все?
Окно в виртуальный мир
Какой угол зрения стоит делать по умолчанию?
Все зависит от платформы, для которой создается игра.
![](https://habrastorage.org/getpro/habr/upload_files/561/331/7d2/5613317d251ac22bb93642e4992bbd27.png)
Если игра создается под ПК и подразумевается, что игрок сидит близко к экрану и имеет возможность еще приблизиться (расстояние до экрана 45-70 см), то угол обзора нужно делать побольше, в числах это примерно 70 градусов.
![](https://habrastorage.org/getpro/habr/upload_files/cbb/dcb/d00/cbbdcbd002eaf61905bf75044658c078.png)
Если игра создается под консоли, а в этом случае расстояние до телевизора составляет 1,8-2м, визуально телевизор всегда выглядит меньше монитора, то при угле обзора в 70 градусов играть будет неудобно: мелкие детали на таком расстоянии сложно рассмотреть. Предпочтительно уменьшить угол обзора, тем самым "приблизив" все детали изображения.
Обратите внимание на размер оружия относительно экрана на двух изображениях выше: в первом случае оно небольшое, а во втором занимает чуть ли не четверь экрана, потому что так комфортнее.
Именно поэтому при запуске мультиплатформенной игры на ПК после консоли все кажется непривычно близким и мелким. Но все чаще игроки могут в одну и ту же игру сначала за ПК поиграть, потом пересесть на диван и продолжить с того же места на консоли, так что должна быть возможность настройки угла обзора камеры, чтобы всем было комфортно.
А какую настройку конкретно нужно поставить?
Окно в виртуальный мир + Head Tracking
Тут нам на помощь придет Head Tracking. Обычно это какие-то приборы с шариками, которые надеваются на голову, а инфракрасная камера светит и определяет положение головы в пространстве.
![emguCV (работает от обычной вебкамеры) emguCV (работает от обычной вебкамеры)](https://habrastorage.org/getpro/habr/upload_files/45f/48d/15f/45f48d15f6b91f52edbb9796525a2323.gif)
Есть, к примеру, emguCV, которая работает от веб-камеры. Есть и другие бюджетные варианты, где обычная веб-камера старается увидеть ваше лицо, фокусируется на одном из глаз и искажает перспективу, двигает камеру, крутит ее, для того, чтобы у вас было полное ощущение погружения в виртуальный мир. Соответственно, если вы приближаетесь к монитору, угол обзора увеличивается, если отходите далеко, угол обзора уменьшается. Прикольная штука, попробуйте. Становится понятно, какой угол обзора должен быть, чтобы игра ощущалась максимально кайфово, как будто вы находитесь не у себя на стуле, а прям вот там.
В рамках этой статьи осталось разобрать еще одну тему.
Инверсия камеры. Все ли нормально с теми, кто это использует?
Во-первых, разберемся, что такое инверсия. Вот заходите вы в настройки игры, и встречаете там такое поле: "инвертировать вид", и не жмете "да" от греха подальше. А некоторые жмут.
![](https://habrastorage.org/getpro/habr/upload_files/0d2/268/f26/0d2268f2638817758e01458b3ced1e4a.png)
Что это все значит?
Держите вы геймпад в руках, и там есть правый грибок.
![Геймпад Геймпад](https://habrastorage.org/getpro/habr/upload_files/f12/8b4/d29/f128b4d29429c17fb118690528cbb431.png)
В нормальной ситуации, когда вы отклоняете его вверх, виртуальная камера поднимается вверх, а когда отклоняете вниз - виртуальная камера опускается вниз. Все логично. Но те, кто любит инфертировать вид, считают, что при отклонении грибка вверх камера должна идти вниз, а при отклонении вниз - вверх, но при этом влево - это влево, а вправо - это вправо. Это что вообще?
Сложно найти какую-то официальную статистику, но если полазить по форумам, то картина складывается следующая:
Статистика "у нас"
![](https://habrastorage.org/getpro/habr/upload_files/950/28e/365/95028e3658b2eafb4fbe1651443780cf.png)
90-95% используют “нормальную" камеру, 5-10% инвертирует камеру в играх. Не так уж и много, но не так уж и мало. А что зарубежом?
![](https://habrastorage.org/getpro/habr/upload_files/3fd/99e/166/3fd99e166937c2b9e9645c817d8a9ff2.png)
В Америке и Европе уже каждый пятый инвертирует оси камеры. Если пойти дальше и походить по форумам консольщиков, то там статистика уже просто страшная:
![Страшная зарубежная статистика Страшная зарубежная статистика](https://habrastorage.org/getpro/habr/upload_files/a75/3eb/812/a753eb81233ca693c8128b337302607f.png)
![Еще одна страшная зарубежная статистика Еще одна страшная зарубежная статистика](https://habrastorage.org/getpro/habr/upload_files/e1d/05a/35c/e1d05a35ccb642faa2636c6a67ba5e60.png)
Оси инветирует примерно половина игроков. А еще 4% инвертируют еще и ось Х. Как так вообще? Давайте разбираться.
Все дело в том, что в 80-е в Америке стали популярны летные симуляторы. Железо стало помощнее, научилось тянуть хоть какое-то условное 3D, и появилось много игр такого жанра. И там, естественно, привычно, что если тянешь штурвал на себя (вниз), самолет летит вверх, а если от себя, то самолет летит вниз. При поворотах налево и направо самолет кренится в соответствующую сторону, инвертируется только ось Y.
![](https://habrastorage.org/getpro/habr/upload_files/62d/70d/695/62d70d6951eb76a68809c4906346c70f.png)
Переносимся в 2000-е года. Люди, игравшие в 80-е и 90-е, выросли, и стали разработчиками игр, а тут появились консоли PlayStation 1 и 2 с их геймпадами.
![](https://habrastorage.org/getpro/habr/upload_files/b9c/4cb/23b/b9c4cb23b0556e9a6bbb49b3fc001847.png)
![](https://habrastorage.org/getpro/habr/upload_files/0fb/ad1/fc3/0fbad1fc3368fd176f7abda256a82882.png)
Вот есть "грибочек", управляющий камерой. Как будем делать? Наверное, как привычно: если тянем наверх, то камера идет вниз, а если тянем вниз, то камера идет вверх. Логично? Логично.
Было довольно много игр, где оси были инвертированы по умолчанию: Red Faction, Final Fantasy XII.
![Red Faction Red Faction](https://habrastorage.org/getpro/habr/upload_files/bd6/ff5/288/bd6ff52882c62001865cca258261e74c.png)
![Final Fantasy XII Final Fantasy XII](https://habrastorage.org/getpro/habr/upload_files/727/181/c09/727181c09ffe5336563e0bb0ac676680.png)
Из-за этого появилось второе поколение игроков, которые изначально привыкли к такому инвертированному управлению, и теперь считают это нормальным. А у нас в 80-е кто играл в авиасимуляторы? Да никто. Да и консолей у нас тоже не так чтобы много, у нас основная платформа - это ПК, а на ПК у нас мышь, где вполне понятно и адекватно: вверх тянем, курсор пошел вверх, вниз тянем - курсор пошел вниз. Вот мы теперь как-то подсознательно считаем, что запуская игру, первым делом мышку наверх, и камера тоже едет наверх. Логично? Логично. А вот у консольщиков привычки другие.
Но есть ведь еще и люди, которые инвертируют не только ось Y, но и X, и их 4-5%. Почему? Дело в психологии. Такие люди чаще всего - манипуляторы, они не ассоциируют себя с вируальным персонажем, они - кукловоды. Они представляют, что кладут руку на голову персонажа, тянут на себя - его голова отклоняется вверх, толкают от себя - голова отклоняется вниз, тянут налево, а взгляд персонажа уходит вправо, и наоборот. Полная инверсия.
![](https://habrastorage.org/getpro/habr/upload_files/eab/181/d9d/eab181d9d988b2977a8e6b6f04b0538f.png)
Еще одну аналогию можно провести с операторами больших видеокамер, закрепленных на штативе и управляемых с помощью ручки. Механизм тот же: тянут ручку вниз - камера смотрит вверх, и наоборот, тянут ручку влево - камера поворачивается направо. Тоже вполе логично, можно понять таких людей.
![](https://habrastorage.org/getpro/habr/upload_files/3e9/a3a/18b/3e9a3a18b79a5b778578e96e28f1674e.png)
А вообще, идеальные настройки камеры - это когда у вас есть одельно настройка и для оси Y, и для оси X. Особенно, если вы планируете выход на международный рынок, т.к. мы выяснили, что процент людей, инвертирующих оси, зарубежом больше.
![](https://habrastorage.org/getpro/habr/upload_files/aa8/1a5/4bd/aa81a54bdb0601a326f1b99780d29ec5.png)
Например, существуют игры, которые в самом начале просят вас с помощью геймпада посмотреть наверх и вних, вправо/влево. И неважно, в какую сторону вы двигаете грибок - важно то, что игра это запомнит и сформирует соответствующую конфигурацию. Игроку комфортно, особенно тем, для кого ваша игра - первая.
Выводы
Всегда помним про базовые инструменты программиста: рандом, синус/косинус, шум Перлина, lerp, проекции точек на экран и перевод их обратно в мировое пространство и т.д.
Если можно достать скорость и ускорение - достаем их. В хозяйстве пригодятся
Переход от катсцен к геймплею делаем плавным. Или, на худой конец, через Fade In - Fade Out (затемнение экрана)
Не используем широкоугольные камеры в катсценах. Ставим FOV в 27 или 17 градусов (аналоги 50мм и 85мм объективов). Особенно, когда показываем лица персонажей
Надо давать возможность настраивать FOV в игре
Надо поддерживать инвертированное управление камеры, если собираетесь выходить на мировой рынок
P.S. Не забываем про наших любимых девчонок и отныне всегда фотографируем их правильно!