Найти в Дзене
Сделай игру

Пишем простую игру на js: делаем то, что игрой не является, но без чего игр не существует

Оглавление

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

Фантазии на тему, как бы ещё могло бы выглядеть подземелье
Фантазии на тему, как бы ещё могло бы выглядеть подземелье

Что будем делать

Мы продолжим делать игру похожей на игру и для этого:

  • Добавим управляемое страницы меню (начальное и меню паузы), на которых картинка должна замирать (а это значит анимации - останавливаться);
  • Добавим возможность переключения уровней (с соответствующим набором подгружаемых ресурсов);
  • Добавим общие ресурсы, которые загружаются в самом начале игры и не зависят от уровня;
  • Добавим отрисовку статичных объектов: не всё в этом мире - анимация.

Немного технических подробностей

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

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

Добавляем всеобщую паузу

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

Пауза включена, но анимация продолжается
Пауза включена, но анимация продолжается

Такое поведение допустимо, но нежелательно. Я внесу изменения и сделаю анимации зависимыми от состояния игры. Получился такой код:

Не только находим кадр и выводим его, но и заглушку, если кадра нет
Не только находим кадр и выводим его, но и заглушку, если кадра нет

Данная функция интегрирована со всеми игровыми высокоуровневыми объектами, так что в любой момент времени имеет доступ ко всем состояниям игры и действует соответственно. Если игра приостановлена - анимация тоже будет приостанавливаться.

Теперь всё нормально: всё замерло
Теперь всё нормально: всё замерло

Давайте теперь проработаем состояния всех объектов.

Улучшенная отрисовка в зависимости от состояния

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

Очевидно, что состояние зависит от объекта и должно быть предопределено специальной "картой состояний". Звучит сложно, но на практике это выглядит примерно так:

Для каждого объекта состояние определяется заранее
Для каждого объекта состояние определяется заранее

Я использую функцию, чтобы на её основе выбирать отрисовку; дополнительная информация (label) позволяет добавить больше информации.

Появилась информация, что дверь закрыта
Появилась информация, что дверь закрыта

Итак, с картой мы разобрались. Чуть позже я её доделаю. А пока - надо сделать два улучшения для отображения: статичный кадр и однократная анимация.

Статичный кадр и однократная анимация

Есть статичные изображения (например пол) и для них анимации не нужны, а есть анимации, которые надо прокрутить только один раз (открытие двери).

Мы интегрируем оба метода внутрь уже имеющейся функции отрисовки, но для этого добавим новые условия внутрь объекта настройки анимации:

  • анимация из одного кадра воспринимается как статичный кадр и дополнительно не обрабатывается;
  • дополнительный флаг указывает, что анимация воспроизводится один раз.

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

Неигровые объекты

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

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

Вот и неигровые объекты появились
Вот и неигровые объекты появились

Я использую случайные цвета и подписи для статичных объектов.

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

Однако оптимизацию отрисовки сделаем уже сейчас: всё то, что вне области просмотра - игнорируется. Я провёл анализ; в среднем на текущую карту 300 ячеек пропускаются при том, что их всего 525. Для игровых объектов показатель более скромный, но, всё же, более чем достойный. Оптимизация удалась.

Пример оптимизации
Пример оптимизации

Управление игрой и общеигровые ресурсы

Сама идея общеигровых ресурсов предполагает, что существует некоторый код, который управляет не только запуском уровней игры, но и промежуточными состояниями. Сейчас его нет; давайте это исправим. Для начала такой вот "скелет" будущего кода:

Пока скудненько
Пока скудненько

Потом поменяется наверное, а пока нужна настройка всей структуры игры. Это не сложно.

Выглядеть будет примерно так
Выглядеть будет примерно так

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

Вместо красного экрана будет, конечно, игра
Вместо красного экрана будет, конечно, игра

Также, внутри игры могут быть (и, скорее всего, будут) общие ресурсы; они начнут загружаться сразу, при старте и если загрузка будет завершена до того момента

Уровень меню

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

Начальное меню
Начальное меню

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

Пока только один вложенный пункт меню
Пока только один вложенный пункт меню

Я старался делать всё предельно обобщённо, так что если появится потребность в прочих пунктах меню - они легко могут быть добавлены, хотя управление в них придётся реализовывать дополнительно.

Как видите, код меню получился не таким уж и большим, хотя есть ситуационные модули
Как видите, код меню получился не таким уж и большим, хотя есть ситуационные модули

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

Общий вид обработчика текущего экрана
Общий вид обработчика текущего экрана

Получилось всё относительно легковесно и лаконично (за исключением состояний игры - строки - это не особо эффективно). Единственное, что мне очень не нравится - так это многочисленные операторы ветвления: они всегда снижают производительность, но переписывать вновь код покуда не хочется

Немного об общих ресурсах

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

Для общего использования
Для общего использования

В настройке каждого этапа есть свои настройки (и ресурсы, и анимации).

Вот для второго этапа
Вот для второго этапа

Параметры этапа имеют более высокий приоритет, нежели параметры всей игры (в плане ресурсов). То есть для первого этапа, где анимация персонажей не установлена - она будет браться из главного файла; для второго этапа - она будет отличаться. Довольно гибкий подход.

Итоги

Итак, у нас есть игра, которая представляет из себя не просто уровень, а уже полноценные два этапа, в которые можно поиграть (и даже выиграть). Но мы, покуда, реализовывали не очень интересные и показательные, внутренние механизмы. А сделать есть ещё что!

Как обычно, посмотреть игру можно по ссылке.

Рекомендуем почитать
Документы, вакансии и контакты