Я - один из тех людей, которые стали программистами потому-что любили компьютерные игры, а теперь получают 300кк/сек за перекладывание джисонов и спрашивают себя "госпади, зачем это всё?!".
Но, хоть это (слава богу) и не стало профессией, я до сих пор люблю видеоигры. И, следствием из этого, у меня есть интерес и желание делать что-то своё. Так в этом году я вкатился в новое хобби - геймдев.
Геймдев, а точнее инди-разработку, очень легко романтизировать: "Вот ты такой классный, придумал интересную идею для игры, сел да и сделал". Творчество, самовыражение, ляпота! На деле оказалось, что геймдев это постоянный поиск решения проблем о существовании которых ты час назад даже не подозревал.
Что для меня стало открытием - часто проблема с игрой находится за её пределами. Как говорили мудрые старцы, "проблема между монитором и креслом". О таких проблемах и пойдёт речь ниже.
Спойлер: Люди - не роботы, и не могут идеально подавать команды в видеоигре
Делаем 2D-платформер мечты
Пару недель назад я начал делать очередной всратый 2D-платформер (для меня - первый).
Начал с идеи: "обычных платформеров уже миллион, хочу чтобы мой отличался на уровне механик. Пусть у моего героя будет длииинная палка, и платформинг будет происходить с её использованием."
Сказано - сделано! Создал проект, накидал полигонов в качестве тестовой площадки, научил бездушную машину слушаться команд с клавиатуры:
- Можно ходить влево-вправо
- Если стоишь на земле или на палке, то можно прыгать
- Если ты уже в воздухе, то нельзя прыгать
- Если ударить палкой об стену, то отскочишь от неё
- Если ударить палкой об пол, то запрыгнешь на палку
Вроде всё готово, заходим тестировать иии... Всё плохо! Часть нажатий игрока теряется или игнорируется, прыжки от стен не всегда срабатывают, прыгать между двумя стенами чтобы забраться наверх практически не возможно. Что же пошло не так?
Прыжки с края платформы
Симптом: При прыжке с края платформы игрок иногда "соскальзывает" вместо того чтобы прыгнуть
Диагноз: Игрок пытается нажать прыжок в самый последний момент, и иногда делает это позже чем надо
Иногда действие игрока запаздывает на пару кадров, в этом случае кажется что ты всё сделал правильно, а прыжок не сработал.
Для этой проблемы есть фикс - можно разрешить игроку прыгнуть если он только-только начал падать. Игрок не заметит этого (если не будет специально вглядываться), а удобство прыжка с платформы сильно вырастет.
(обычно тут всего пара кадров в которые можно прыгнуть, но для наглядности я увеличил)
Это одна из самых известных проблем такого типа в платформерах, и её почти всегда решают именно так. Даже смешное название придумали - coyote time. Да, это в честь того самого койота который не сразу понимал что он висит в воздухе.
Прыжки с палки
Симптом: Не всегда работает прыжок с палки, вместо этого иногда персонаж просто "падает" с палки на землю
Диагноз: Иногда перед нажатием кнопки "прыжка" игрок отпускает кнопку "удара", из-за чего игра решает что игрок хочет слезть с палки
Тут нужен фикс, похожий на первый. Смотрим предыдущее состояние игрока, если он недавно был на палке - то так уж и быть разрешим ему сейчас прыгнуть.
Прыжки от стен
Симптом: При прыжках от стен иногда тыкаешь не в ту сторону в которую хотел
Диагноз: Игрок хочет отскочить от стены и сразу после этого изменить направление движения стиком, иногда в этих двух действиях путается порядок
Тут уже проблема чуть более серьёзная, приходится думать.
Во первых, почему игрок хочет сразу развернуться? - Потому-что в воздухе можно ускоряться в нужном направлении (это платформеры, тут так принято) и игрок хочет преодолеть максимальное расстояние. Тут можно полечить необходимость идеально разворачиваться, если зафиксировать скорость отскока от стены на долю секунды (чтобы у игрока было это время изменить положение стика).
Во вторых, осталась сама проблема что игрок меняет направление раньше удара. Самое простое решение которое я нашёл - смотреть не менял ли игрок недавно направление, и если менял - то пробовать оттолкнуться от стены за спиной.
Input buffer
Симптом: Иногда часть нажатий пропадает, например когда пытаешься прыжками двигаться по земле или прыгать на палке
Диагноз: Это другие проявления той же проблемы с нажатием кнопок раньше чем игра готова их обработать. Если персонаж ещё в воздухе, то он не может прыгнуть ещё раз. Даже если игрок нажал кнопку всего на один кадр раньше
Вот эта проблема точно есть почти во всех видеоиграх - на экране персонаж совершает действия, а игрок пытается сделать что-то сразу после них. Например, комбо-удары в любом файтинге. Клиент Игрок всегда прав, и если он нажал на кнопку - значит он что-то хотел.
Обычно это решают таким образом: если игрок нажал кнопку, а игра ещё не готова - запоминаем это нажатие и обрабатываем как только будем готовы. Если после переката идёт прыжок, то сначала ждём завершения переката.
В моём прототипе это виднее всего в двух местах - цепочка прыжков по земле, и прыжки с палкой. В первом случае ждём приземления на землю, во втором случае ждём пока залезем на палку перед прыжком.
Последствия костылирования
Из хорошего - в игру наконец-то стало возможно играть! Бездушная машина уступила и пытается делать то что хочет пользователь, а не то что он подаёт на вход.
(совершенно точно первый дубль и не перезаписывал много раз)
Из плохого интересного - все эти костыли наслаиваются друг на друга, и позволяют комбинировать их для достижения неожиданных для разработчика результатов.
Например, у меня получилось запрыгнуть на платформу к которой не должно быть доступа (ведь потолок мешает прыгнуть).
(так и рождаются техники для спидранов)
Вместо заключения
В итоге пока делал все эти фиксы пришёл примерно к такой мысли:
Ты не можешь изменить человека, поэтому остаётся только менять его окружение
То есть чтобы игра казалась честной игроку, нужно сломать её таким образом чтобы скомпенсировать ущербность игрока.
Интересно, в каких ещё местах приходится применять такую логику?
Желаю тебе никогда не разрабатывать игру с вероятностями и рандомом, потому что окажется что там тоже надо накостылять прям очень много, чтобы игроку было "честно" по ощущениям
Напомнило раздел «Player Forgiveness» из ролика про движение в игре Celeste:
Кайф статья, нет желание на DTF запосить?
😱 Комментарий удален его автором...
Спасибо за рассказ! А у меня сразу вопрос вдогонку к последней фразе (которая тоже вопрос про «в каких местах применяется такая логика»): неужели до сих пор нет приличных библиотек для платформеров, которые бы всё это уже делали? Чтоб только задать допуски типа времени койота и т. п., сконфигурировать граф состояний — и игра получала бы готовые сигналы?
@IlyaZverev,
Celeste и Spelunky — это два обязательных must read/must watch по реализацию платформеров и procedural generation. Благо, про них много рассказано и показано (в одном из комментариев есть ссылка на Celeste).
Еще полезные:
Math for Game Programmers: Building a Better Jump
Math for Game Programmers: Juicing Your Cameras With Math
Если хочется войти в геймдев как хобби, рекомендую участвовать в геймджемах. Засесть за свой проект мечты - это большой шанс сразу же и сгореть)
Сделали вот такую штуку, чтобы тыкать в разные модификаторы передвижения в платформерах
https://gmtk.itch.io/platformer-toolkit
Не знаю как в играх, но мы один раз делали свою реализацию многоуровневого выпадающего меню для движка интернет-магазина.
Суть в том, что если человек выбрал пункт меню и хочет выбрать что-то в подменю справа, и он в процессе движения мышью случайно задевает край вышестоящей или нижестоящей строки, то переключается всё подменю. Нужно двигать мышью горизонтально, но с первого раза никто так не делает.
И тоже, куча таймингов и замеров скорости движения мыши, чтобы не было случайных переключений. В итоге после реализации меню решили, что такими темпами будем лет 5 делать весь магазин и решили забить.
Концепт напомнил Getting Over It от которой хотелось все вокруг уничтожить :)
А ты знал про Getting over it когда придумывал механику?