В предыдущей статье я описал метод генерации лабиринтов из связанных комнат разных размеров. На этот раз мы разберём другой способ генерации лабиринтов, в котором лабиринты будут получаться классической формы. Кроме того, я расскажу о том, как находить самый короткий путь между любыми двумя точками лабиринта.
Я решил что следующую игру буду делать с генерируемыми уровнями и сделал некоторые наброски алгоритма для рандомной генерации лабиринтов из связанных комнат разных размеров. Процесс генерации выглядит следующим образом:
Сам алгоритм проще описать картинками нежели кодом:
Решил написать для себя маленькую шпаргалку по созданию двухмерного и трёхмерного динамического массива и таких же многомерных векторов (STL vector), но понял, что она будет полезна не только мне, поэтому я решил поделиться ей с вами :)
Двухмерный вектор
#include <vector>
using std::vector;
#define HEIGHT 5
#define WIDTH 3
int main() {
vector<vector<double> > array2D;
// Устанавливаем размерность (HEIGHT x WIDTH)
array2D.resize(HEIGHT);
for (int i = 0; i < HEIGHT; ++i)
array2D[i].resize(WIDTH);
// Помещаем некоторые значения
array2D[1][2] = 6.0;
array2D[3][1] = 5.5;
return 0;
}
Она представляет из себя платформер, геймплей которого,
завязан на возможности поворачивать игровой мир при прохождении лабиринта с ловушками.
Сторителинг:
Главный герой — это космический скафандр, оснащённый искусственным интеллектом. Он является членом команды орбитальной станции Протей, состоящей из двоих: из скафандра и его лучшего друга астронавта Джейка.
Однажды экипаж корабля принимает посылку с Земли, в которой помимо научного оборудования, находится робот, отправленный в качестве помощника. Они включают его, но…он выходит из строя и нападает на них. Бракованный робот силой заталкивает скафандр в машину по утилизации космического мусора, а астронавта берёт в заложники.
Герой должен вырваться из плена, пройдя все уровни утилизации и спасти астронавта!
Проект написан на С++ в Qt Creator с использованием SFML и Box2d.
Весь арт нарисован моими сестрёнками, за что им отдельное спасибо. :)
Вся музыка в игре за авторством DST.
Поддерживаемые платформы Windows / Linux.
Игра принимает участие в Games Jam 2014, так что игру можно пощупать ;) вот ссылка https://gamesjam.org/989/. (на конкурсе демо-версия с урезанным количеством уровней)
SFML предоставляет набор классов для отображения простых фигур. Каждый из типов фигур — это отдельный класс, но все они происходят из одного базового класса и потому имеют набор одинаковых методов. Каждый класс добавляет специфичные для него свойства: радиус для круга, размеры для прямоугольника, вершины многоугольника и т.д.
Общие свойства фигур
Трансформация (позиция, поворот, масштаб)
Это свойство является общим для всех графических классов SFML, поэтому они описаны в отдельном руководстве: (пока тут ссылка на не переведённый оригинал).
Цвет
Одно из главнейших свойств фигур — это их цвет, который можно менять с помощью метода setFillColor().
sf::CircleShape shape(50);
// задаём фигуре зелёный цвет
shape.setFillColor(sf::Color(100, 250, 50));
Перед рисованием текста, вам нужно загрузить шрифт. Для работы со шрифтами используется специальный класс sf::Font. Он поддерживает три главных операции: загрузка шрифта, получение из него глифов (визуальных символов) и чтение его атрибутов.
Начнём с загрузки. Самый простой способ загрузить шрифт, это загрузить его из файла на диске, сделать это можно при помощи функции loadFromFile().
sf::Font font;
if (!font.loadFromFile("arial.ttf"))
{
// ошибка...
}
Для рисования объектов с помощью графического модуля, вам необходимо использовать специальный класс окна: sf::RenderWindow. Этот класс является наследником класса sf::Window и обладает всем методами класса родителя. Всё что вы изучили о sf::Window (создание, обработка событий, настройка частоты кадров, смешанное использование с OpenGL и т.д.), применимо и к sf::RenderWindow.
Помимо этого, sf::RenderWindow имеет методы облегчающие задачу рисования объектов. В этом уроке мы сосредоточимся на двух таких методах: clear() и draw(). Они просты как и их названия: clear() очищает окно выбранным цветом, а draw() рисует любой переданный ему объект.
Так выглядит типичный главный цикл с окном визуализации (render window):
Этот урок объясняет как получить доступ к глобальным ресурсам (global inputs): клавиатуре, мыши и джойстику. Не следует путать их с событиями: ресурсы в режиме реального времени позволяют запросить глобальные состояния клавиатуры, мыши и джойстика в любое время («Эта кнопка нажата в данный момент?», «Где мышь?»). В то время как события уведомляют вас когда что-то происходит («Эта кнопка была нажата», «Мышь переместилась»).
Клавиатура
Класс, предоставляющий доступ к состоянию клавиатуры, — sf::Keyboard. Он содержит только один метод isKeyPressed(), который проверяет текущее состояние клавиши (нажата или отпущена). Это статический метод, и поэтому вам не нужно создавать экземпляр класса sf::Keyboard что бы воспользоваться им.
Эта функция напрямую считывает состояние клавиатуры игнорируя факт, что ваше окно активно или неактивно.
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
// нажата клавиша влево: сдвинем нашего героя
character.move(1, 0);
}
Коды клавиш определены в перечислении sf::Keyboard::Key.
Коды некоторых клавиш могут отсутствовать или быть неправильно интерпретированы в зависимости от ОС и раскладки клавиатуры. Это будет улучшено в следующих версиях
SFML.
Этот урок представляет подробный перечень событий окна. Он описывает их и показывает как можно и нельзя использовать события.
Тип sf::Event
Прежде чем перейти к событиям, важно понять что из себя представляет тип sf::Event и как правильно им пользоваться. sf::Event — это объединение, а значит, что только один из членов этого объединения допустим (т.е. существует) в данный момент времени (все элементы объединения хранятся в одной области памяти; за подробностями — в любой учебник по с++). Допустимым членом является тот, который соответствует типу события, например event.key для события KeyPressed. Попытка считывания другого члена приведёт к неопределённому поведению (скорее всего вы получите недопустимые или случайные значения). Поэтому никогда не пытайтесь использовать член события, который не соответствуют типу этого события.
Экземпляры sf::Event в качестве аргументов принимаются методом pollEvent() (или waitEvent()) класса sf::Window. Только две эти функции могут предоставить допустимые события, любая попытка использовать sf::Event без предварительного успешного вызова pollEvent() (или waitEvent()) приведёт к такому же неопределённому поведению, о котором говорилось выше.
Для большей ясности, вот так выглядит типичный цикл с использованием событий:
sf::Event event;
// пока имеются события в очереди...
while (window.pollEvent(event))
{
// проверка типа события...
switch (event.type)
{
// закрытие окна
case sf::Event::Closed:
window.close();
break;
// нажатие клавиш
case sf::Event::KeyPressed:
...
break;
// мы не обрабатываем другие типы событий
default:
break;
}
}