English

Александр Лысенко

Синдром Утенка

-

Вступление

Игровой джэм (от англ. game jam) - это событие в течение нескольких дней, на котором разработчики создают игры на определенную тему. Само слово “джэм” относится к музыкальной импровизации нескольких человек, когда они подыгрывают на музыкальных инструментах между собой.[1] Программисты, дизайнеры, композиторы, писатели и художники могут собраться в команду и создать игру подобным способом. Есть игры с единственным разработчиком. Люди учатся, закрепляют навыки через практику, и смотрят произведения друг друга.

В мае 2024 года проходил джэм Pixel Game Jam 2024 (с 11 до 20 мая), основной темой которого было создание игры с пиксельным оформлением.[2] К началу события, девять тысяч человек со всего мира присоединились к списку участников. Мероприятие должно было длиться десять дней, в командах размером до трех человек, с разрешением на любые движки или технологии. В этой статье, я опишу общий ход создания игры, которая получила название “Синдром Утенка”.

День 1. Планирование.

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

Была раскрыта дополнительная тема игрового джэма — “Аква”, и все, что связано с водой.[3] Организаторы мероприятия намекнули на солнечный пляж и море. Практически же игра могла быть и про лед, рыбу, даже сантехнику. Сложность была не в том, чтобы добавить водные эффекты и изображения, а в том, чтобы вода стала сущностью игрового процесса. Например, в игре, в которой нужно строить песчаные замки во время прилива, темой является не вода, а скорее песок. Или, в другой игре, в которой дождь только едва виден, или волны только немного слышны, вода также не будет основной темой.

В этот день я рассмотрел разные варианты, но не приступал к работе.

День 2. Пиксельный холст и двухмерная вода.

Я решил написать свою игру на чистом JavaScript, чтобы она запускалась в браузере. На удивление, сделать пикселизованный холст средствами веб-разработчика достаточно просто:

<canvas id="canvas" width="128px" height="72px"></canvas>
<style>
    #canvas {
        image-rendering: pixelated;
    }
</style>

Так как игровое поле состояло из относительно небольшого количества пикселей (128*72=9216), я был искушен добавить в игру динамическую поверхность воды. В тот момент, у меня не было представления, будет это маленькая лужа или большой водоем. Все, что я хотел, это видеть бегущие кругами волны после щелчка, и чтобы это рассчитывались во время исполнения игры. В интернете нашелся алгоритм двумерной воды, упомянутый в нескольких источниках.[4] Похоже, что никто не знает, кто его придумал; и он передавался от одного поколения программистов к другому.

Изб. 1. Волны по алгоритму 2D воды на второй день разработки.

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

for (let i=1; i<buffer1.length-1; i++){
    buffer2[i] = (buffer1[i-1] + buffer1[i+1])/2 - buffer2[i];
    buffer2[i] = buffer2[i] * 0.5;
}

for (let i=0; i<buffer1.length; i++)
    [buffer1[i], buffer2[i]] = [buffer2[i], buffer1[i]];

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

  1. Сглаживание: Когда состояние воды обновляется, высота волны на данном пикселе считается как среднее арифметическое от высот волн на его соседних пикселях. Высокая волна падает, сглаживается и размывается. Таким образом волны распространяются, и бегут от высоких точек к низким (и наоборот).
  2. Затухание: Каждое значение в массиве умножается на коэффициент меньше единицы (например, 0.95), потому что мы ожидаем, что волны затухают с течением времени. Без эффекта затухания, новые добавленные волны копятся, и повышают общий уровень воды. С эффектом затухания, высота волн вскоре возвращается к нулю.
  3. Обращение: После сглаживания, из новой высоты волны на данном пикселе, переданной от соседей, вычитается предыдущая высота волны. Когда мы учитываем и обращаем предыдущую (затухающую) высоту волны, волны колеблются как настоящая поверхность воды. В нескольких словах, без этой операции “волна” на двумерном поле выглядит как яркая точка, которая плавно растекается до блеклого круга, после чего исчезает.

Единственное отличие двумерной реализации в том, что сглаживание происходит по двум осям. Я изменил силу с которой волны бегут вверх и вниз; благодаря этому в игре они выглядят овальными и возникает ощущение перспективы. Это и стало результатом второго дня разработки.

Версия игры 2024-05-13.

День 3. Алгоритм поиска пути.

Изб. 2. Движение персонажа к месту щелчка.

На холсте появился персонаж, который пока выглядел как квадрат. Я не планировал добавлять препятствия, поэтому алгоритм передвижения мог быть также прост. Чтобы персонаж передвигался из одной точки в другую, я использовал известный алгоритм Брезенхэма.[5] Программа добавляет в массив все точки, которые лежат на прямой между игроком и местом щелчка. Игрок последовательно проходит эти точки, пока не окажется рядом с целью.

Версия игры 2024-05-14.

День 4. Главный персонаж и название игры.

Пришло время определиться с главным персонажем и нарисовать его спрайты. В этот день я окончательно решил, что им станет утенок, а игра получит название “Синдром Утенка”. Мне показалось, что утенок выражает водную тему особенно хорошо, и вышла бы интересная игра. В природе, утята привязываются к первой “матери”, которую видят после вылупления, и повсюду за ней следуют.[6] Утенок плыл за курсором мыши, и это стало причиной для такого названия.

Изб. 3. Лист спрайтов утенка на четвертый день разработки.

Я воспользовался программой Krita, чтобы нарисовать утенка в восьми направлениях. Изображения должны были меняться в зависимости от направления к щелчку мыши. Для этого понадобилась функция арктангенс2, которая возвращает угловую меру между осью абсцисс и другим лучом из начала координат.[7] В моем случае, основанием луча было положение утенка, а другой точкой – координаты щелчка. Я забегаю вперед, но за четвертый и пятый дни я также поменял свое мнение на счет единиц измерения углов. Мое первое решение, по привычке, полагалось на излишние градусные преобразования, но в итоге я принял удобство радиан.

Версия игры 2024-05-15.

День 5. Конечный автомат состояний поведения персонажа.

Изб. 4. Диаграмма перехода утенка из одного состояния в другое.

Ранее, как только игрок нажимал мышкой на воду, утенок совершал поворот и рывок в ту сторону моментально. Чтобы поведение игрового персонажа выглядело более реалистично, утенок стал поворачиваться к цели плавно. Это стало возможным после разделения поведения персонажа на три состояния: Бездействует, Поворачивает, и Плывет.[8]

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

Версия игры 2024-05-16.

День 6. Каустики в Blender и границы.

Изб. 5. Ограничивающие воду линии и их крайние координаты.

На шестой день я смоделировал водную емкость в Blender, и добавил солнечные лучи на дно бассейна. Эти меняющиеся формы называются каустиками, и возникают при преломлении света на волнующейся поверхности воды.[9] В интернете есть много видео, объясняющих как воссоздать такой эффект с помощью диаграммы Вороного.[10] Потребовалось время на поиск метода, чтобы сделать бесшовную повторяющуюся анимацию.[11] В моей игре, десять изображений с каустиками меняются со скоростью тридцати кадров в секунду, что и создает иллюзию продолжительного движения воды.

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

Версия игры 2024-05-17.

День 7. Звуковые эффекты.

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

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

Версия игры 2024-05-18.

День 8-9.

Почти без прогресса.

День 10. Завершение проекта.

Изб. 6. Версия игры на десятый день.

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

Когда время приема заявок истекло, было отправлено более 700 проектов от других разработчиков![13] Некоторые полностью посвященные воде, а некоторые с водой совсем не связанные. По сравнению со многими другими работами, моя игра имела очевидные недостатки, так как в ней не было уровней, или условий победы и поражения. Все же, я получил много положительных отзывов и идей, как игру можно улучшить. Разработка игры принесла мне радость, потому что приятно видеть, как из ничего получается что-то. Это не совсем правда, так как я имел предыдущий опыт, и заметки, оставленные другими энтузиастами в интернете.

Версия игры 2024-05-21.

Обновление. Улучшение скорости отрисовки.

Игра работала на моем настольном компьютере, но я заметил визуальные задержки на бюджетных мобильных телефонах. Существуют рекомендации для улучшения скорости отрисовки на холсте.[14] Вместо того, чтобы рисовать пиксели воды один за другим < 9216 раз, я использовал другой метод, в котором компьютер сначала меняет данные пикселей, и после рисует все пиксели за раз.[15] Браузеры также предоставляют функцию requestAnimationFrame() для синхронизации запросов отрисовки с частотой обновления экрана.[16]

Версия игры 2025-01-05.