diff --git a/README.md b/README.md index 6a57d33..2786d64 100644 --- a/README.md +++ b/README.md @@ -6,5 +6,40 @@
-

В 1-м примере заложен основной цикл игры

+

В 1-м примере в программе будет заложен основной цикл игры.

+
+ +
+ +

Во 2-м примере добавлено управление персонажем.

+
+ +
+ +

В 3-м примере заложен основной цикл игры.

+
+ +
+ +

В 4-м примере заложен основной цикл игры.

+
+ +
+ +

В 5-м примере заложен основной цикл игры.

+
+ +
+ +

В 6-м примере заложен основной цикл игры.

+
+ +
+ +

В 7-м примере заложен основной цикл игры.

+
+ +
+ +

В 8-м примере заложен основной цикл игры.

diff --git a/img/cmake_add_to_path.png b/img/cmake_add_to_path.png deleted file mode 100644 index f2baba3..0000000 Binary files a/img/cmake_add_to_path.png and /dev/null differ diff --git a/img/gameloop_chart.png b/img/gameloop_chart.png new file mode 100644 index 0000000..ebdcc07 Binary files /dev/null and b/img/gameloop_chart.png differ diff --git a/img/preview_1.png b/img/preview_1.png new file mode 100644 index 0000000..0291e7d Binary files /dev/null and b/img/preview_1.png differ diff --git a/img/preview_2.png b/img/preview_2.png new file mode 100644 index 0000000..5dae1c5 Binary files /dev/null and b/img/preview_2.png differ diff --git a/img/preview_3.png b/img/preview_3.png new file mode 100644 index 0000000..3398362 Binary files /dev/null and b/img/preview_3.png differ diff --git a/img/preview_4.png b/img/preview_4.png new file mode 100644 index 0000000..7abbcea Binary files /dev/null and b/img/preview_4.png differ diff --git a/img/preview_5.png b/img/preview_5.png new file mode 100644 index 0000000..f375844 Binary files /dev/null and b/img/preview_5.png differ diff --git a/img/preview_6.png b/img/preview_6.png new file mode 100644 index 0000000..2abc2a1 Binary files /dev/null and b/img/preview_6.png differ diff --git a/img/screenshot_1.png b/img/screenshot_1.png new file mode 100644 index 0000000..e36cfec Binary files /dev/null and b/img/screenshot_1.png differ diff --git a/img/screenshot_2.png b/img/screenshot_2.png new file mode 100644 index 0000000..a8c26b9 Binary files /dev/null and b/img/screenshot_2.png differ diff --git a/img/screenshot_3.png b/img/screenshot_3.png new file mode 100644 index 0000000..46c022e Binary files /dev/null and b/img/screenshot_3.png differ diff --git a/img/screenshot_4.png b/img/screenshot_4.png new file mode 100644 index 0000000..961e780 Binary files /dev/null and b/img/screenshot_4.png differ diff --git a/img/screenshot_5.png b/img/screenshot_5.png new file mode 100644 index 0000000..03c9e91 Binary files /dev/null and b/img/screenshot_5.png differ diff --git a/img/screenshot_6.png b/img/screenshot_6.png new file mode 100644 index 0000000..15e4fed Binary files /dev/null and b/img/screenshot_6.png differ diff --git a/pacman_1/README.md b/pacman_1/README.md new file mode 100644 index 0000000..45e5c18 --- /dev/null +++ b/pacman_1/README.md @@ -0,0 +1,43 @@ +# Слой 1: основной цикл игры + +Этот слой содержит: + + 1. создание окна и вывод фигуры на экран + 2. основной цикл игры + +![Скриншот](../img/screenshot_1.png) + +## Основной цикл игры + +[Основной цикл игры](https://martalex.gitbooks.io/gameprogrammingpatterns/content/chapter-3/3.2-game-loop.html) — это шаблон проектирования кода, который применяется практически в каждой игре. Он диктует, как должна выглядеть основная процедура игры (`main` в языках C/C++): сначала происходит инициализация, а затем начинается цикл, выход из которого происходит непосредственно перед закрытием приложения. Общая схема игрового цикла: + +![Схема](../img/gameloop_chart.png) + +Основной цикл пока что очень прост: + + - перед рисованием кадра происходит опрос внешних событий. Если события есть, то метод `window.pollEvent` вернёт булево значение `true` и скопирует информацию о событии в 1-й аргумент. + +```cpp +sf::Event event; +while (window.pollEvent(event)) +{ + if (event.type == sf::Event::Closed) + { + window.close(); + } +} +``` + + - игрового состояния в данный момент нет, поэтому обновлять нечего + + - после опроса событий окно очищается (заливается чёрным цветом), затем на нём рисуется фигура, затем очередной кадр отправляется операционной системе, которая отобразит кадр окна на экране + +```cpp +window.clear(); +window.draw(shape); +// метод display отдаёт кадр операционной системе, а также может ожидать +// сигнала вертикальной синхронизации монитора (vsync). +window.display(); +``` + +Современные операционные системы обычно обновляют экран с частотой 60Гц, и такой же частоты ожидают от приложений. Поэтому на выполнение одной итерации цикла отведено `1.0 / 60.0 = 0.016(6)` секунд, то есть чуть более 16 миллисекунд. Естественно, все современные игры стремятся уложиться в этот интервал независимо от сложности моделируемого игрового мира. diff --git a/pacman_2/README.md b/pacman_2/README.md new file mode 100644 index 0000000..bf847a5 --- /dev/null +++ b/pacman_2/README.md @@ -0,0 +1,111 @@ +# Слой 1: основной цикл игры + +Этот слой содержит: + + 1. обработку ввода - нажатий на клавиши-стрелки + 2. перемещение персонажа в выбранном направлении + 3. проект игры разделён на два разных cpp-файла, один из которых будет содержать основной цикл игры, а другой - функции и константы, относящиеся к пакману + +![Скриншот](../img/screenshot_2.png) + +## Обработка событий клавиатуры + +Данные о событии нажатия или освобождения клавиши хранит структура [sf::Event::KeyEvent](https://www.sfml-dev.org/documentation/latest/structsf_1_1Event_1_1KeyEvent.php), доступная в поле `.key` структуры `sf::Event`. + +Типичный путь обработки событий клавиш - это switch по кодам клавиш. Подробнее читайте на сайте SFML в руководстве [Events explained](https://www.sfml-dev.org/tutorials/2.4/window-events.php). + +Также вы можете прочитать руководство [Keyboard, mouse and joystick (sfml-dev.org)](http://www.sfml-dev.org/tutorials/2.4/window-inputs.php) + +## Представим направление движения с помощью перечислений (enum) + +На данный момент пакман представлен единственным объектом типа [sf::CircleShape](www.sfml-dev.org/documentation/latest/classsf_1_1CircleShape.php). Теперь нам потребуется ещё и хранить направление движения пакмана. + +Можно было бы кодировать направление движения в виде целого числа, и считать, что 0 означает отсутствие движения, 1 — движение влево, 2 — движение вправо, и так далее. Но такие обозначения вносят путаницу, о них легко забыть, а с ростом количества кода они вообще перестают нормально восприниматься. + +К счастью, в языке C есть перечислимые типы данных. Существуют они в двух вариантах: более древний простой `enum`-тип и новый, более строгий `enum struct` (он же `enum class`): + +```cpp +// Старый синтаксис языка C, легко порождает конфликты с другими константами. +// добавляет во внешнюю область видимости константы NONE == 0, +// UP == 1, DOWN == 2 и так далее. +// константа NONE - слишком часто употребляемое имя. +enum Direction +{ + NONE, + UP, + DOWN, + LEFT, + RIGHT +}; + +// Новый синтаксис C++ 2011, позволяет избегать конфликтов +// добавляет во внешнюю область видимости константы Direction::NONE == 0, +// Direction::UP == 1 и так далее. Имя Direction::NONE ни с чем не конфликтует. +enum struct Direction +{ + NONE, + UP, + DOWN, + LEFT, + RIGHT +}; +``` + +Мы, конечно же, применим более строгий перечислимый тип `enum struct Direction`, как указано в втором варианте объявления Direction. Добавьте в проект новый файл `pacman.h` и перенесите в него объявление `enum struct Direction`. + +## Замена нескольких параметров на структуру (struct) + +Мы не будем использовать глобальные переменные. Код, содержащий глобальные переменные, с ростом размера становится чрезмерно запутанным: невозможно анализировать и развивать код отдельной функции, не держа в голове все глобальные переменные и все остальные функции, влияющие на них. + +Всё необходимое для работы функции можно передать через параметры. Например, функция для рисования пакмана была объявлена так: + +```cpp +void render(sf::RenderWindow & window, sf::CircleShape & shape) +{ + window.clear(); + window.draw(shape); + window.display(); +} +``` + +Но если отказаться от глобальных переменных, со временем могут возникнуть новые проблемы: + +- код функции разрастается и становится неудобным для чтения +- число параметров функции возрастает +- возникает необходимость как-то возвращать из функции несколько значений + +Первая проблема решается путём дальнейшего деления на вспомогательные функции. Остальные две проблемы можно решить с помощью составных типов данных, например, структур `struct` или контейнеров стандартной библиотеки C++. Синтаксис определения пользовательской структуры: + +```cpp +// синтаксис (упрощённый) +struct Идентификатор +{ + Тип_поля_№1 имя_поля_№1; + Тип_поля_№2 имя_поля_№2; + // и так далее ... +}; +// пример +struct Pacman +{ + sf::CircleShape shape; + Direction direction; +}; +``` + +Обращение к полям структуры можно выполнить с помощью оператора ".": + +```cpp +void initializePacman(Pacman &pacman) +{ + pacman.direction = Direction::NONE; + pacman.shape.setRadius(20); + pacman.shape.setFillColor(sf::Color::Green); + pacman.shape.setPosition(100, 0); +} + +// старое длинное определение без структур: +void initializePacman(sf::CircleShape & shape, Direction & direction) +{ + // ... +} +```