Змейка на PDCurses С++

Змейка на PDCurses С++

Змейка — это классическая игра, в которой вы управляете змейкой. Она ползает по экрану и собирает еду, становясь длиннее. Цель игры - не попасться на свой собственный хвост и не удариться об стену. Чем длиннее змейка, тем сложнее управлять ей.

Игра очень проста в управлении. Вы используете клавиши со стрелками на клавиатуре, чтобы изменить направление движения змейки. Если змея съест еду, то становится длиннее и вы зарабатываете очки. Если змея столкнется со своим хвостом или ударится об стену, игра закончится.

Змейка всегда останется популярной игрой из-за своей простоты и увлекательности. Она является ретро-классикой, в которую играют на компьютере, телефоне или планшете.

Создаём проект С++ и подключаем библиотеку PDCurses.

Змейка на PDCurses С++

При создании проекта в Visual Studio 2022 выбираем шаблон С++ консольные приложения.

Змейка на PDCurses С++

Даём название проекту, устанавливаем галочку, поместить решение и проект в одном каталоге и создаём проект.

Загружаем библиотеку PDCurses c github на любую операционную систему или под Windows Visual Studio с нашего телеграмм канала.

Змейка на PDCurses С++
Змейка на PDCurses С++

Копируем библиотеку в папку с проектом и подключаем.

Змейка на PDCurses С++

Открываем папку в проводнике и копируем библиотеку.

Змейка на PDCurses С++

Подключаем библиотеку PDCurses к проекту

Змейка на PDCurses С++
Змейка на PDCurses С++

В дополнительный каталог включаемых файлов указываем расположение заголовочных фалов библиотеки, в нашем варианте папку с проектом.

Змейка на PDCurses С++

В дополнительных каталогах библиотек указываем расположения файла pdcurses.lib, в нашем случае корневая папка проекта.

Змейка на PDCurses С++

В дополнительные зависимости записываем файл pdcurses.lib.

Нажимаем кнопку применить, тем самым сохраняем внесённые изменения в свойства проекта.

После того как мы удачно подключили библиотеку PDCurses, мы можем начинать писать код.

#include <chrono> #include <random> #include <vector> #include <string> #include <curses.h> using namespace std; using namespace std::chrono; bool game_exit = false; struct vector2di { int x = 0; int y = 0; };

В начале программы подключаем необходимые заголовочные файлы:

сhrono, random (обеспечивают работу генератора случайных чисел), vector (динамический массив для сохранения координат хвоста змейки), string (с помощью функции to_string () конвертируем значение целочисленной переменной в строковое представление), curses (предназначенная для управления вводом-выводом на терминал, позволяет задавать экранные координаты и цвет выводимых символов).

Cообщаем компилятору, что мы хотим использовать всё, что находится в пространстве имен std и std::chrono.

Объявляем и инициализируем глобальную переменную game_exit, которая будет определять завершение программы.

Создаём структуру vector2di, для хранения координат по оси икс и игрек.

Функция main() :

int main() { // переключаем шрифт для отображения кириллицы system("chcp 1251"); // инициализируем экран curses initscr(); // прячем курсор curs_set(0); // запускаем цветной режим start_color(); // режимы цветов 1,2,3,4,5 init_pair(1, COLOR_WHITE, COLOR_BLUE); init_pair(2, COLOR_MAGENTA, COLOR_BLUE); init_pair(3, COLOR_GREEN, COLOR_BLUE); init_pair(4, COLOR_RED, COLOR_BLUE); init_pair(5, COLOR_YELLOW, COLOR_BLACK); //цвет фона, режим 1 bkgd(COLOR_PAIR(1)); // начальные координаты головы змейки vector2di snake_head{ 10,10 }; // направление и шаг перемещения змейки vector2di vector_step{ 1,0 }; // постоянные координаты яблока vector2di apple{ 15,15 }; // случайные координаты яблока vector2di rnd_apple; // динамический массив координат хвоста змеи vector<vector2di> snake_tail; // частота смены кадров int frame_rate = 100; // количество собранных яблок int eaten_apples = 0; // **** начало работы генератора случайных чисел **** // момент системного времени long long seed = system_clock::now().time_since_epoch().count(); // запуск генератора случайных чисел default_random_engine rnd(static_cast<unsigned int>(seed)); // установка диапазона случайных координат яблока uniform_int_distribution<int> apple_x(10, 97); uniform_int_distribution<int> apple_y(5, 22); // **** конец работы генератора случайных чисел **** // разрешаем использовать специальные клавиши в нашем случае стрелки для управления змейкой keypad(stdscr, true); // выключает отображение нажатых клавиш на экране noecho(); // **** игровой цикл **** while (!game_exit) { // присваиваем случайные значения переменным случайных координат яблока rnd_apple.x = apple_x(rnd); rnd_apple.y = apple_y(rnd); // рисуем игровое поле show_map(apple, eaten_apples); // перемещаем змейку по игровому полю move_snake(snake_head, vector_step, snake_tail, apple, rnd_apple, eaten_apples); // частота сменны игрового кадра timeout(frame_rate); // **** управление змейкой и игрой **** // проверка нажатой клавиши switch (getch()) { case KEY_UP: if (vector_step.y == 0) { vector_step.y = -1; vector_step.x = 0; frame_rate = 170; } break; case KEY_DOWN: if (vector_step.y == 0) { vector_step.y = 1; vector_step.x = 0; frame_rate = 170; } break; case KEY_LEFT: if (vector_step.x == 0) { vector_step.x = -1; vector_step.y = 0; frame_rate = 100; } break; case KEY_RIGHT: if (vector_step.x == 0) { vector_step.x = 1; vector_step.y = 0; frame_rate = 100; } break; case 'q': game_exit = true; break; default: break; } // **** конец управления змейкой и игрой **** } // **** конец игрового цикла **** endwin(); return 0; }

В начале функции определяем и инициализируем переменные, включаем и настраиваем терминал curses.

Функция initscr() определяет тип терминала и инициализирует все структуры данных реализации, также вызывает операцию обновления для очистки экрана. Данная функция вызывается только один раз.

Функция curs_set () устанавливает внешний вид курсора на основе значения видимости :

Змейка на PDCurses С++

В нашем коде устанавливаем значение ноль и делаем курсор невидимым.

Функция start_color() – инициализирует использование цветов на терминале.

Процедура init_pair() задаёт определение пары цветов. Она принимает три аргумента: номер пары, номер цвета переднего плана и номер цвета фона.

Константы цветов:

Змейка на PDCurses С++

Функция bkgd () устанавливает цвета для текущего терминала.

При создании генератора случайных чисел обязательно должны быть подключены соответствующие заголовочные файлы. Создаём целочисленную переменную seed и записываем в неё момент времени. Передаём момент времени в генератор случайных чисел объект rnd. Генерируем случайные числа координат яблока в установленном диапазоне по иксу apple_x(10, 97) и по игреку apple_y(5, 22).

В параметрах функции keypad() разрешаем использовать специальные клавиши, в нашем случае стрелки, указав значение true.

Функция noecho() отключает отображение в терминале нажатых клавиш.

Игровой цикл while будет работать пока игра не завершится.

В цикле присваиваем случайные значения переменным rnd_apple.x, rnd_apple.y. Рисуем игровое поле используя функцию show_map() и перемещаем по игровому полю змейку через функцию move_snake().

В параметрах функции timeout() устанавливаем время задержки кадров в миллисекундах.

Создаём переключатель обработки нажатых клавиш через функцию getch(). При нажатии стрелок меняем вектор направления движения змейки и время обновления кадра. При нажатии клавиши < q >, выходим из игры.

Функция endwin() останавливает работу терминала curses.

Функция show_map():

void show_map(const vector2di& apple, const int& eaten_apples) { // очистка экрана clear(); // перемещение курсора move(2, 55); // устанавливаем атрибуты для текста яркость и номер цветовой пары attrset(A_DIM | COLOR_PAIR(1)); // выводим текст в консоль printw("Змейка\t\t"); attrset(A_BOLD | COLOR_PAIR(5)); printw(" количество собранных яблок < "); // значение целочисленной переменной переводим в строковое string s_eaten_apples = to_string(eaten_apples); // выводим значение переменной printw(s_eaten_apples.c_str()); printw(" > "); attrset(A_DIM | COLOR_PAIR(1)); // рисуем игровое поле for (int y = 4; y < 28; y++) { for (int x = 5; x < 112; x++) { if (y == 4 || y == 27 || x == 5 || x == 111) { move(y, x); printw("*"); } } } // рисуем яблоко move(apple.y, apple.x); attrset(A_BOLD | COLOR_PAIR(2)); printw("@"); }

В параметрах функции передаём ссылку на координаты яблока apple и ссылку на количество подобранных яблок eaten_apples.

Функция clear() очищает экран, удаляя весь текст.

Функция move(), перемещает курсор в координаты заданные в параметрах по игреку 2, а по иксу 55.

Функция attrset(), задаём тип и цвет отображения символов.

Возможные параметры функции attrset():

Змейка на PDCurses С++

Функция printw() выводит текст в консоль.

Функция to_string() приводит значение целочисленной переменной eaten_apples в строковой тип для вывода её значения в консоль.

С помощью циклов for() и символа « * » рисуем границы игрового поля, далее отображаем символ « @ » обозначающий яблоко.

Определяем функцию перемещения змейки move_snake():

void move_snake(vector2di& snake_head, vector2di& vector_step, vector<vector2di>& snake_tail, vector2di& apple, vector2di rnd, int& eaten_apples) { // устанавливаем цвет змейки attrset(A_BOLD | COLOR_PAIR(3)); // Если змейка с хвостом, рисуем хвост if (!snake_tail.empty()) { for (auto const& mov : snake_tail) { move(mov.y, mov.x); printw("#"); } } // изменяем координаты головы змейки snake_head.x += vector_step.x; snake_head.y += vector_step.y; // перемещаем курсов в координаты головы змейки move(snake_head.y, snake_head.x); // проверяем символ в установленных координатах курсора auto s = static_cast<char>(winch(stdscr)); // *** Если змейка столкнулась с хвостом или границами игрового поля //  ### Выводим игровое меню ### if (s == '*' || s == '#') { attrset(A_BOLD | COLOR_PAIR(4)); move(13, 55); printw("Конец игры"); move(14, 42); printw("Выход - < q >  Начать заново - < n >"); // *** Цикл выбора игрового меню *** do { if (getch() == 'q') { // выход из игры game_exit = true; return; } if (getch() == 'n') { // рестарт задаём начальные координаты переменным snake_head = { 10,10 }; vector_step = { 1,0 }; snake_tail.clear(); apple = { 15,15 }; eaten_apples = 0; return; } } while (true); // *** Конец цикла выбора игрового меню *** } // ### Конец игрового меню ### //  Если змейка съедает яблоко if (s == '@') { // увеличиваем количество съеденных яблок eaten_apples++; // добавляем хвост змейки snake_tail.push_back({ snake_head.x, snake_head.y }); // рисуем голову змейки printw("$"); do { // задаём новые координаты яблока apple.x = rnd.x; apple.y = rnd.y; move(apple.y, apple.x); auto s = static_cast<char>(winch(stdscr)); // повторяем цикл пока координаты яблока совпадают с хвостом змейки } while (s == '#'); } // Если змейка переместилась в свободное поле else { // рисуем голову змейки printw("$"); // обновляем координаты хвоста змеи if (!snake_tail.empty()) { snake_tail.erase(snake_tail.begin()); snake_tail.push_back({ snake_head.x, snake_head.y }); } } };

В параметрах функции передаём ссылки на: координаты головы змейки snake_head, шаг и вектор перемещения змейки vector_step, динамический массив координат хвоста snake_tail, координаты яблока apple, случайные координаты яблока rnd, количество собранных яблок eaten_apples.

Более подробную инструкцию вы можете получить, посмотрев видео « Игра на С++ Змейка »

Начать дискуссию