Space Invaders «с нуля» — Часть 3: создаём клон игры с минимумом зависимостей
В третьей части серии «Space Invaders с нуля» мы переходим от основ к геймплею: добавляем игрока и рои пришельцев, вводим анимацию спрайтов и делаем игровой цикл на фиксированном шаге времени с V-sync. Пошагово разбираем, как структурировать данные и оживить игровую сцену на C++.
88 открытий1К показов
Это перевод статьи автора Nick Tasios. Мы уже публиковали перевод первой части с настройками окна и контекста и перевод второй части с настройкой шедеров и отрисовкой спрайта. В этой части создадим клон классической аркадной игры Space Invaders на C++, используя минимум зависимостей.
В этой части мы сделаем несколько ключевых шагов:
- реализуем игровой цикл с фиксированным временным шагом,
- добавим игрока и пришельцев,
- и, наконец, добавим анимацию спрайтов.
Этот этап превращает наш базовый прототип в настоящую игру — с управлением, движением врагов и визуальной динамикой. Код третьей части можно посмотреть в репозитории на Github.
Добавление игрока и роя пришельцев
Прежде чем добавить игрока и рой пришельцев, создадим два агрегата данных — то есть структуры (struct):
И у структуры игрока, и у структуры пришельца есть координаты позиции x и y, заданные в пикселях относительно нижнего левого угла окна. В структуру Player также добавляется поле с количеством жизней игрока.
В классических аркадных играх Space Invaders существует три типа пришельцев, которые отличаются только своими спрайтами. Этот параметр мы кодируем в поле type.
Кроме того, мы вводим отдельную структуру, предназначенную для хранения всех переменных, связанных с состоянием игры.
Это включает ширину и высоту игры в пикселях, объект игрока и массив пришельцев, выделяемый динамически.
Как и ранее, мы добавляем спрайт для игрока, закодированный в виде растрового изображения (bitmap).
Затем мы создаём и инициализируем структуру Game.
Мы задаём количество пришельцев равное 55 — как в оригинальной аркадной игре, — даём игроку 3 жизни и размещаем его рядом с нижней центральной частью экрана. Затем инициализируем позиции пришельцев, выбрав для них разумные координаты.
Наконец, в основном игровом цикле мы отрисовываем игрока и всех пришельцев.
Получаем вот такой результат:
Это уже начинает напоминать Space Invaders, но пока всё довольно статично!
Анимация спрайтов
Чтобы сделать игру более динамичной, нам, конечно, нужно реализовать ввод от игрока, но также требуется способ анимировать спрайты. Анимация спрайтов в видеоиграх достигается заменой текущего спрайта последовательностью спрайтов один за другим. Поэтому мы создаём структуру данных для хранения различной информации об анимации спрайта.
Структура SpriteAnimation по сути представляет собой массив Sprite. Здесь мы используем тип «указатель на указатель» для хранения спрайтов, чтобы их можно было шарить (совместно использовать). Если хочется повысить эффективность, спрайты можно упаковать в атласы (spritesheets). Кроме того, мы добавляем флаг, указывающий, нужно ли зацикливать анимацию или воспроизводить её лишь один раз, интервал между последовательными кадрами и время, проведённое в текущем экземпляре анимации. Зная число кадров и желаемую длительность анимации, интервал между кадрами легко вычислить. Ниже мы вводим дополнительный спрайт для нашего инопланетянина…
…и создаём двухкадровую анимацию, используя два спрайта пришельца.
Обратите внимание: чтобы упростить задачу, мы измеряем длительность кадра и время в игровых циклах — то есть в количестве итераций игрового цикла. Чтобы это работало корректно, нужно зафиксировать частоту кадров. Для простой игры, подобной нашей, можно использовать V-sync — опцию, при которой обновления видеокарты синхронизируются с частотой обновления монитора.
Большинство современных мониторов имеют частоту обновления 60 Гц, что означает обновление экрана 60 раз в секунду. Включив V-sync, мы получим частоту кадров 60 FPS или кратное значение, синхронизированное с частотой дисплея.
Однако есть и недостаток: на мониторах с более высокой частотой обновления, например, 120 Гц или 240 Гц, игра будет работать быстрее. Чтобы включить V-sync, вызывается функция GLFW:
В конце каждого кадра мы обновляем все анимации, увеличивая значение времени.
Если анимация дошла до конца, мы либо удаляем её, либо сбрасываем время обратно в 0, если это зацикленная анимация.
Обратите внимание, что на данный момент у нас есть только одна анимация, которую нужно обновлять.
В основном цикле, где происходит отрисовка пришельцев, мы изменяем цикл рисования так, чтобы отображался соответствующий кадр анимации.
Нужный кадр вычисляется на основе времени, прошедшего с начала анимации, и длительности одного кадра.
Чтобы сделать процесс более интересным, мы также добавляем простое движение игрока, введя переменную, которая управляет направлением его движения…
…и обновляем положение игрока в конце каждого кадра в зависимости от значения этой переменной.
Условия if выполняют простую проверку столкновений спрайта игрока с границами игрового поля, гарантируя, что игрок остаётся внутри этих границ.
Выше вы можете видеть анимированный GIF с результатом.
Заключение
В этом посте мы создали несколько структур (struct), чтобы логически сгруппировать данные для игры, игрока и пришельцев. Ещё важнее то, что мы заложили основу для анимации спрайтов. Для этой простой версии Space Invaders мы используем фиксированные игровые циклы, задав постоянную частоту кадров с помощью включения вертикальной синхронизации (V-sync). Это позволяет нам выполнять анимацию спрайтов в игровых циклах, а не в реальном времени.
Единственное, что остаётся сделать, прежде чем игра станет играбельной, — реализовать обработку пользовательского ввода. Источников ввода может быть много, но в нашем случае мы ограничимся клавиатурой.
88 открытий1К показов






