Ранее, я добавил в игру анимации и возможность использовать переходы, что сделало её более интересной и увлекательной, однако не сделало полноценной. Так уж вышло, что любая игра это не только карта и анимации, это ещё и некоторый около-игровой процесс, на который мало кто обращает внимание, но без которого ничего не работает. Я говорю, конечно, про всевозможные экраны состояний, меню и загрузки. Этим и займёмся.
Что будем делать
Мы продолжим делать игру похожей на игру и для этого:
- Добавим управляемое страницы меню (начальное и меню паузы), на которых картинка должна замирать (а это значит анимации - останавливаться);
- Добавим возможность переключения уровней (с соответствующим набором подгружаемых ресурсов);
- Добавим общие ресурсы, которые загружаются в самом начале игры и не зависят от уровня;
- Добавим отрисовку статичных объектов: не всё в этом мире - анимация.
Немного технических подробностей
На первый взгляд набор задач совсем не сложный, однако, он потребует не просто пересмотра всех действующих механизмов, но и, вероятно, изменения их использования. Я уже не говорю о том, что сам процесс работы потребуется обновить.
Как всё работает сейчас: загружаются ресурсы уровня и когда это происходит начинается игра. Будет так: загружается настройка, меню и статичные ресурсы игры, затем выбирается и загружается уровень, затем, идёт игра, затем - финальный экран. Между уровнями могут быть экраны промежуточных сцен.
Добавляем всеобщую паузу
Замечали в старых играх: вроде погиб персонаж, всё кругом замерло, но анимация продолжается. Противники бегут на месте, пули застыли в воздухе, но продолжают пульсировать. В нашей игре - так же. Это общеигровая пауза и она должна работать иначе. Сейчас пауза работает так:
Такое поведение допустимо, но нежелательно. Я внесу изменения и сделаю анимации зависимыми от состояния игры. Получился такой код:
Данная функция интегрирована со всеми игровыми высокоуровневыми объектами, так что в любой момент времени имеет доступ ко всем состояниям игры и действует соответственно. Если игра приостановлена - анимация тоже будет приостанавливаться.
Давайте теперь проработаем состояния всех объектов.
Улучшенная отрисовка в зависимости от состояния
Пришло время обдумать состояния отображения и составить их карту: состояние - это та анимация (или статичное изображение), которое должно быть. Например, открытая дверь отличается по внешнему виду от закрытой.
Очевидно, что состояние зависит от объекта и должно быть предопределено специальной "картой состояний". Звучит сложно, но на практике это выглядит примерно так:
Я использую функцию, чтобы на её основе выбирать отрисовку; дополнительная информация (label) позволяет добавить больше информации.
Итак, с картой мы разобрались. Чуть позже я её доделаю. А пока - надо сделать два улучшения для отображения: статичный кадр и однократная анимация.
Статичный кадр и однократная анимация
Есть статичные изображения (например пол) и для них анимации не нужны, а есть анимации, которые надо прокрутить только один раз (открытие двери).
Мы интегрируем оба метода внутрь уже имеющейся функции отрисовки, но для этого добавим новые условия внутрь объекта настройки анимации:
- анимация из одного кадра воспринимается как статичный кадр и дополнительно не обрабатывается;
- дополнительный флаг указывает, что анимация воспроизводится один раз.
Исходный код функции отрисовки стал довольно большим, поэтому я не стану его выкладывать сюда.
Неигровые объекты
До сих пор мы отрисовывали столь своеобразным образом исключительно игровые объекты, теперь настало время ландшафта. Сложность заключается в том, что игровые объекты имеют некоторые параметры, а не-игровые - это просто байтовое значение. Однако, существующий подход позволяет подстроиться: для каждого типа ячейки создаётся свой собственный объект, управляющий анимациями и отображением; он и используется.
Возможны случаи, когда потребуется сделать некоторую уникальность для одного или нескольких элементов - в этом случае внутрь объекта ячейки можно поместить параметры, связанные по индексу с указанным элементом.
Я использую случайные цвета и подписи для статичных объектов.
Впрочем, в обозримом будущем нас ждёт некоторое разочарование в плане работы с графикой: замедление отрисовки. Это решается как простой оптимизацией (пропускаем то, что не попадает в окно просмотра), так и сменой графической библиотеки со стандартной на WebGL. Но до этого пока не дошло (на самом деле дошло, но убрав контуры у объектов скорость перерисовки стала приемлемой, а с контурами - всё тормозило нещадно).
Однако оптимизацию отрисовки сделаем уже сейчас: всё то, что вне области просмотра - игнорируется. Я провёл анализ; в среднем на текущую карту 300 ячеек пропускаются при том, что их всего 525. Для игровых объектов показатель более скромный, но, всё же, более чем достойный. Оптимизация удалась.
Управление игрой и общеигровые ресурсы
Сама идея общеигровых ресурсов предполагает, что существует некоторый код, который управляет не только запуском уровней игры, но и промежуточными состояниями. Сейчас его нет; давайте это исправим. Для начала такой вот "скелет" будущего кода:
Потом поменяется наверное, а пока нужна настройка всей структуры игры. Это не сложно.
Сейчас добавлю немного кода и всё заработает. Правда придётся сделать небольшую переработку: ряд функций действуют в рамках одного уровня, а их надо будет сделать применимыми ко всей игре в целом. Например, управление.
Также, внутри игры могут быть (и, скорее всего, будут) общие ресурсы; они начнут загружаться сразу, при старте и если загрузка будет завершена до того момента
Уровень меню
С ним немного сложней: по сути, это отдельное приложение внутри приложения. Я создам простой шаблон, который будет выполнять предначертанные действия, но стараться сделать что-то гибко настраиваемое не с буду: просто нет смысла.
Для анимации указателя я использовал уже существующую функцию анимации, подстроив под новые задачи. Настройка меню простая: по нажатию на кнопку выполняется функция, которая может возвращать массив: откуда взять следующую сцену и с какого номера. Это позволит и уровни включать, и подпункты меню запускать. В общем, получилось примерно так:
Я старался делать всё предельно обобщённо, так что если появится потребность в прочих пунктах меню - они легко могут быть добавлены, хотя управление в них придётся реализовывать дополнительно.
Переход к игре осуществить относительно легко: при выборе "Начать игру" уровень, который ранее инициализировался при загрузке страницы - будет запускаться уже внутри новой системы. Единственно, функция отрисовки будет вынесена из файла обработчика уровня в общий цикл.
Получилось всё относительно легковесно и лаконично (за исключением состояний игры - строки - это не особо эффективно). Единственное, что мне очень не нравится - так это многочисленные операторы ветвления: они всегда снижают производительность, но переписывать вновь код покуда не хочется
Немного об общих ресурсах
Теоретически, у нас должно быть два типа ресурсов: те ресурсы, которые относятся к уровню и общие ресурсы. Общие загружаются в самом начале игры и будут использоваться очень часто (например, анимация главного героя). У меня такие ресурсы (а это не только, например, изображения, но и анимации, которые могут немного меняться от уровня к уровню) прописаны в файле настройки. Выглядит это так:
В настройке каждого этапа есть свои настройки (и ресурсы, и анимации).
Параметры этапа имеют более высокий приоритет, нежели параметры всей игры (в плане ресурсов). То есть для первого этапа, где анимация персонажей не установлена - она будет браться из главного файла; для второго этапа - она будет отличаться. Довольно гибкий подход.
Итоги
Итак, у нас есть игра, которая представляет из себя не просто уровень, а уже полноценные два этапа, в которые можно поиграть (и даже выиграть). Но мы, покуда, реализовывали не очень интересные и показательные, внутренние механизмы. А сделать есть ещё что!
Как обычно, посмотреть игру можно по ссылке.