Пишем свою игру в жанре Roguelike
33К открытий33К показов
Рассказывает Ido Yehieli
Игры в жанре roguelike, такие как Dungeons of Dredmor, Spelunky, The Binding of Isaac и FTL, в последнее время стали очень популярны, а различные комбинации элементов этого жанра теперь добавляют многим играм глубины и реиграбельности.
Следуя инструкциям этого руководства, вы создадите традиционный «рогалик», используя игровой движок Phaser на JS+HTML5. Кстати, недавно мы публиковали обзор таких движков. В результате вы получите полнофункциональную игру в жанре «roguelike», запускаемую в браузере. (Под рогаликом мы подразумеваем одиночный рандомизированный пошаговый dungeon-crawler с одной жизнью.)
Примечание: хотя в этом руководстве и используются JavaScript, HTML и Phaser, вы можете использовать эти принципы для реализации на любом другом языке и движке.
Подготовка
Вам понадобятся текстовый редактор и браузер. Я использую Notepad++ и Google Chrome, но это не принципиально.
Затем вы должны скачать исходники и начать с папки init
: она содержит файлы Phaser, HTML и JS, необходимые для нашей игры. Наш код мы будем писать в пустом файле rl.js
.
Файл index.html
file просто загружает Phaser и вышеупомянутый файл с кодом игры:
Инициализация и определения
Сейчас для нашей игры мы будем использовать символы ASCII — впоследствии их можно заменить bitmap-графикой, но сейчас проще взять ASCII.
Давайте зададим несколько констант для размера шрифта, размера карты и количества персонажей:
Также инициализируем Phaser и слушатели сигналов с клавиатуры, так как мы создаём пошаговую игру и хотим создавать действие после каждого нажатия клавиши:
Так как ширина стандартных моноширинных шрифтов равна 60% от высоты, мы зададим размер поля как 0.6 * размер шрифта * количество столбцов
. Мы также говорим Phaser, что он должен вызвать нашу функцию create()
сразу после завершения инициализации, когда инициализируется и управление с клавиатуры.
Можете взглянуть на нашу игру — правда, там пока и смотреть не на что:)
Карта
Клеточная карта отражает нашу игровую зону: дискретный двумерный массив клеток, представленных символами ASCII, которые могут изображать либо стену (#
: блокирует перемещение), либо пол (.
: не блокирует перемещение):
Давайте будем использовать простейшую форму процедурной генерации карт: каждая клетка принимает одно из двух значений случайным образом:
Таким образом мы получим карту, примерно на 20% занятую стенами.
Мы инициализируем новую карту в функции create()
сразу после запуска слушателей клавиатуры:
Можете посмотреть, что получилось — пока что мы всё равно не отрисовали наши карту.
Экран
Настало время вывести нашу карту на созданный экран:
Тем не менее, перед отрисовкой карты экран нужно инициализировать. Вернёмся к функции create()
:
Теперь при запуске проекта вы должны видеть случайную карту.
Персонажи
Теперь займёмся персонажами: нашим игроком и его врагами. Каждый персонаж будет объектом с тремя полями: координаты x
и y
и хитпоинты hp
.
Мы будем хранить всех персонажей в массиве actorList
(его первый элемент — игрок). Мы также будем хранить ассоциативный массив с позициями персонажей в качестве ключей для быстрого поиска; это поможет нам, когда мы займёмся перемещением и боёвкой.
Мы создаём всех персонажей и рандомно размещаем их на свободных ячейках карты:
Настало время показать персонажей! Мы изобразим всех врагов буквой e
, а игрока — количеством его хитпоинтов:
Возьмём только что написанные функции и передадим их в create()
:
Теперь мы можем увидеть размещённых на поле противников и игрока!
Блокирующие и неблокирующие клетки
Нам нужно убедиться, что персонажи не выходят за пределы уровня и не проходят сквозь стены, поэтому добавим простую проверку:
Перемещение и сражение
Наконец-то мы дошли до движухи! Так как в классических рогаликах персонажи атакуют друг друга при столкновении, мы обработаем это в функции moveTo()
, которая принимает персонажа и направление (направление задаётся разностью координат x
и y
текущей и желаемой клеток):
Вкратце:
- Мы убеждаемся, что персонаж может переместиться в эту клетку.
- Если в ней есть другой персонаж, мы атакуем его (и убиваем, если счётчик его хитпоинтов достигает нуля).
- Если клетка пуста, мы перемещаемся в неё.
Заметим также, что мы выводим простое сообщение о победе после смерти последнего врага и возвращаем false
или true
в зависимости от того, валидно ли желаемое перемещение.
Теперь вернемся к функции onKeyUp()
и изменим её так, чтобы при каждом нажатии клавиши мы стирали предыдущие положения персонажей (отрисовывая поверх них карту), перемещали игрока и снова отрисовывали персонажей:
Скоро мы введем переменную acted
, чтобы узнать, должны ли после перемещения игрока действовать враги.
Простой ИИ
После того, как мы закончили с реализацией игрока, займёмся врагами. Напишем простой алгоритм поиска пути, по которому враг будут двигаться к игроку, если расстояние между ними не превышает шести шагов, а в противном случае будет перемещаться случайно. О различных алгоритмах поиска мы уже коротко рассказывали в нашей недавней статье.
Заметим, что противнику неважно, кого атаковать: таким образом, при правильном размещении противники будут уничтожать друг друга, пытаясь догнать игрока. Прям как в классическом Doom!
Также мы добавили сообщение, которое выводится на экран при смерти игрока.
Теперь нам осталось сделать так, чтобы враги перемещались с каждым ходом игрока. Дополним функцию onKeyUp()
:
Бонус: версия на Haxe
Изначально я писал это руководство на Haxe, кроссплатформенном языке, компилирующемся в JavaScript (и не только). Эту версию мы можете найти в папке haxe
в исходниках.
Сперва вам потребуется установить компилятор haxe, после чего скомпилировать написанный в любом текстовом редакторе код, вызвав haxe build.hxml
и дважды кликнув по файлу build.hxml
. Я также добавил проект FlashDevelop, если вы предпочитаете пользоваться удобной IDE: просто откройте rl.hxproj
и нажмите F5 для запуска.
Заключение
Вот и всё! Мы закончили создание простой roguelike-игры со случайной генерацией карты, движением, боёвкой, ИИ и условиями победы/поражения.
Вот некоторые фичи, который вы могли бы добавить:
- несколько уровней;
- бонусы;
- инвентарь;
- аптечки;
- снаряжение.
Наслаждайтесь!
33К открытий33К показов