Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Roguelike
Gamedevelopment

Как сделать свою первую игру в жанре Roguelike

by
Difficulty:IntermediateLength:LongLanguages:

Russian (Pусский) translation by Dmitriy Sirosh (you can also view the original English article)

Игры в жанре roguelike, такие, как Dungeons of Dredmor, Spelunky, The Binding of Isaac и FTL, в последнее время стали очень популярны, они охватывают широкую аудиторию и получили признание критиков. Этот жанр давно любим хардкорными игроками в определенной крошечной нише, а различные комбинации элементов жанра roguelike теперь добавляют многим играм глубины и реиграбельности.

Wayfarer a 3D roguelike currently in development
3D-рогалик Warfarer, который в настоящее время находятся в разработке.

Следуя инструкциям этого руководства, вы создадите традиционный «рогалик», используя JavaScript и игровой HTML5 движок Phaser. В результате вы получите полнофункциональную игру в жанре «roguelike», запускаемую в браузере! (Под рогаликом мы подразумеваем одиночный рандомизированный пошаговый dungeon-crawler с одной жизнью.)

Click to play the game
Нажмите, чтобы сыграть.

Примечание. Хотя в этом руководстве и используются JavaScript, HTML и Phaser, вы можете использовать эти принципы для реализации на любом другом языке и движке.


Подготовка

Для этого руководства вам понадобится текстовый редактор и браузер. Я использую Notepad++ и Google Chrome за его обширные инструменты для разработчиков, но рабочий процесс будет практически таким же, как с любым текстовым редактором и браузером, который вы выберете.

Затем вы должны скачать исходники и начать с папки init: она содержит файлы Phaser, HTML и JS, необходимые для нашей игры. Наш код мы будем писать в пустом файле rl.js.

Файл index.html просто загружает Phaser и наш вышеупомянутый файл с кодом игры:


Инициализация и определения

Сейчас для нашей roguelike игры мы будем использовать ASCII графику — впоследствии ее можно заменить bitmap-графикой, но сейчас проще взять ASCII.

Давайте определим некоторые константы для размера шрифта, размеров нашей карты (то есть уровня) и количества персонажей:

Также инициализируем Phaser и слушатели сигналов с клавиатуры, так как мы создаём пошаговую игру и хотим создавать действие после каждого нажатия клавиши:

Так как ширина стандартных моноширинных шрифтов равна 60% от высоты, мы зададим размер поля как 0.6 * размер шрифта * количество столбцов. Мы также говорим Phaser, что он должен вызвать нашу функцию create() сразу после завершения инициализации, когда инициализируется и управление с клавиатуры.

Можете взглянуть на нашу игру здесь — правда, там пока и смотреть не на что!


Карта

Клеточная карта представляет собой игровую зону: дискретный двумерный массив клеток, представленных символами ASCII, которые могут изображать либо стену (#: блокирует перемещение), либо пол (.: не блокирует перемещение):

Давайте будем использовать простейшую форму процедурной генерации карт: каждая клетка принимает одно из двух значений случайным образом:

Таким образом мы получим карту, где 80% ячеек являются стенами, а остальные - полами.

Мы инициализируем новую карту в функции create() сразу после запуска прослушивателей событий клавиатуры:

Здесь вы можете посмотреть демо, хотя, опять же, смотреть пока нечего, потому, что мы всё равно не отрисовали нашу карту.


Экран

Настало время вывести нашу карту на созданный экран! Наш экран будет представлять собой 2D-массив текстовых элементов, каждый из которых содержит один символ:

Прорисовка карты заполнит содержимое экрана значениями карты, так как оба являются простыми символами ASCII:

Тем не менее, перед отрисовкой карты экран нужно инициализировать. Вернёмся к нашей функции create():

Теперь при запуске проекта вы должны увидеть случайно сгенерированную карту.

Click to view the game so far
Нажмите для просмотра результата.

Персонажи

Теперь займёмся персонажами: наш игровой персонаж и враги, которых он должен победить. Каждый персонаж будет объектом с тремя полями: координаты x и y и хитпоинты hp.

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

Мы создаём всех персонажей и рандомно размещаем их на свободных ячейках карты:

Настало время показать персонажей! Мы изобразим всех врагов буквой e, а игрока — количеством его хитпоинтов:

Возьмём только что написанные функции и передадим их в функцию create():

Теперь мы можем увидеть размещённых на поле противников и игрока!

Click to view the game so far
Нажмите для просмотра результата.

Блокирующие и неблокирующие клетки

Нам нужно убедиться, что персонажи не выходят за пределы уровня и не проходят сквозь стены, поэтому добавим простую проверку:


Перемещение и сражение

Наконец, мы пришли к определенному взаимодействию: движению и бою! Так как в классических рогаликах персонажи атакуют друг друга при столкновении, мы обработаем это в функции moveTo(), которая принимает персонажа и направление (направление задаётся разностью координат x и y текущей и желаемой клеток):

Вкратце:

  1. Мы убеждаемся, что персонаж может переместиться в эту клетку.
  2. Если в ней есть другой персонаж, мы атакуем его (и убиваем, если счётчик его хитпоинтов HP достигает нуля).
  3. Если клетка пуста, мы перемещаемся в неё.

Заметим также, что мы выводим простое сообщение о победе после смерти последнего врага и возвращаем false или true в зависимости от того, валидно ли желаемое перемещение.

Теперь вернемся к функции onKeyUp() и изменим её так, чтобы при каждом нажатии клавиши мы стирали предыдущие положения персонажей (отрисовывая поверх них карту), перемещали игрока и снова отрисовывали персонажей:

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

Click to view the game so far
Нажмите для просмотра результата.

Базовый искусственный интеллект

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

Заметим, что противнику неважно, кого атаковать: таким образом, при правильном размещении противники будут уничтожать друг друга, пытаясь догнать игрока. Прям как в классическом Doom!

Также мы добавили сообщение, которое выводится на экран если один из противников убивает игрока.

Теперь нам осталось сделать так, чтобы враги перемещались с каждым ходом игрока. Дополним функцию onKeyUp():

Click to view the game so far
Нажмите для просмотра результата.

Бонус: версия на Haxe

Изначально я писал это руководство на Haxe, кроссплатформенном языке, компилирующемся в JavaScript (и не только). Хотя я перевел версию выше вручную, чтобы убедиться, что мы получим идиосинкразированный JavaScript. Если вы предпочитаете haxe, а не JavaScript, тогда вы можете найти ее в папке haxe в исходниках.

Сперва вам потребуется установить компилятор haxe, после чего скомпилировать написанный в любом текстовом редакторе код, вызвав haxe build.hxml и дважды кликнув по файлу build.hxml. Я также добавил проект FlashDevelop, если вы предпочитаете пользоваться удобной IDE: просто откройте rl.hxproj и нажмите F5 для запуска.


Резюме

Вот и всё! Мы закончили создание простой roguelike-игры со случайной генерацией карты, движением, боем, ИИ и условиями победы/поражения.

Вот некоторые фичи, которые вы можете добавить в свою игру:

  • несколько уровней;
  • бонусы;
  • инвентарь;
  • аптечки;
  • снаряжение.

Наслаждайтесь!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.