Загрузить исходный код С++ WinAPI Visual Studio 2019 ЗДЕСЬ
Ссылка на видео на youtube о создинии игры Змейка a Создание игр на C++ : Змейка, автор видео использует С++ и OpenGL.
Директива define определяет основные параметры игрового поля (заголовочный файл MyApp.h).
#define SCALE 25 //size one square on screen (in pixels) #define FIELD_WIDTH 30 //field width #define FIELD_HEIGHT 20 //field height #define WINDOW_WIDTH (FIELD_WIDTH * SCALE) #define WINDOW_HEIGHT (FIELD_HEIGHT * SCALE)
Сначала создаем игровое поле, этим занимается функция void CMyApp::PrintField().
Теперь создадим змейку. Для описания змейки нужны переменные:
static int Dir = 3; //направление движения от 0 до 3 static int Num = 4; //чиcло квадратиков нашей змейки, для начала игры 4
Направление может принимать значения:
Змейка движеться назад Dir = 0; Змейка движеться влево Dir = 1; Змейка движеться вправо Dir = 2; Змейка движеться вперед Dir = 3;
Число квадратиков змейки увеличивается, когда змейка съедает фрукт, и уменьшается когда змейка сама себе наскочила на хвост.
Сама змейка представляет из себя следующую структуру:
//структура описывает змейку struct { int x; //текущий квадрат змейки по ширине int y; //текущий квадрат змейки по высоте } s[100]; //максимальное количество квадратиков //которое может быть у змейки 100
Как видим у каждого квадрата змейки есть своя позиция на игровом поле x,y. Змейка не может выходить за пределы игрового поля, т.е. координата x змейки не может быть больше FIELD_WIDTH, не может быть меньше нуля, и координата y змейки не может быть больше FIELD_HEIGHT и не может быть меньше нуля.
Напишем код который дает 1 шаг на игровом поле для нашей змейки (направление не важно).
Что означает что змейка делает 1 шаг- это значит ее головной элемент передвинулся в направлении Dir на одну координату (клетку). При движении змейки вверх координата змейки y увеличивается на 1, при движении вниз координата змейки y уменьшается на 1, при движении вправо координата змейки x увеличивается на 1, при движении влево координата змейки x уменьшается на 1.
Как движеться головной элемент змейки понятно, как движуться остальные элементы змейки? Остальные элементы просто перемещаются на место предыдущего элемента. Т.е. к примеру последний элемент станет на место предпоследнего.
void CMyApp::RenderScene() { static bool BeginGame = true; if(BeginGame) { //инициализация змейки перед игрой //у змейки изначально четыре квадратика s[0].x = 10; s[0].y = 10; s[1].x = 10; s[1].y = 11; s[2].x = 10; s[2].y = 12; s[3].x = 10; s[3].y = 13; } //3)движение всех элементов змейки после головного for (int i = Num ; i > 0 ; --i) { s[i].x = s[i - 1].x; s[i].y = s[i - 1].y; } //2)движение головного элемента змейки if(Dir == 0) s[0].y += 1; //змейка движется вверх if(Dir == 1) s[0].x -= 1; //змейка движется влево if(Dir == 2) s[0].x += 1; //змейка движется вправо if(Dir == 3) s[0].y -= 1; //змейка движется вниз //4)рисуем нашу змейку - выводим елементы змейки на экран hBrush = CreateSolidBrush(RGB(0, 0, 255)); hOldBrush = (HBRUSH)SelectObject(m_hBackBuffer, hBrush); for ( int i = 0; i < Num; i++) { Rectangle( m_hBackBuffer, s[i].x * SCALE, s[i].y * SCALE, (s[i].x + 1) * SCALE, (s[i].y + 1) * SCALE ); }
После запуска данного кода змейка на экране движется вверх, теперь добавим управление:
if(linput & IN_FORWARD) { Dir=3; } if(linput & IN_BACK) { Dir=0; } if(linput & IN_LEFT) { Dir=1; } if(linput & IN_RIGHT) { Dir=2; }
Теперь сделаем что бы когда змейка наскакивает на фрукт она увеличивалась в размере.
Создадим класс Fructs который описывает фрукты на игровом поле (MyApp.h):
//класс описывает фрукты для нашей змейки class Fructs { public: int x,y; //позиция фрукта на игровом поле //функция нужно если змейка наехала на яблоко //тогда это яблоко появляется в новом месте void New () { x = rand() % FIELD_WIDTH; y = rand() % FIELD_HEIGHT; } } m[10]; //всего 10 фруктов на игровом поле
Если змейка наскочила на фрукт, то фрукт появляется в новом месте - функция New() класса Fructs.
Функция которая выводит на экран все фрукты:
//6)функция выводит фрукты на экран hBrush = CreateSolidBrush(RGB(0, 200, 0)); hOldBrush = (HBRUSH)SelectObject(m_hBackBuffer, hBrush); for (int i = 0; i < 10; i++) { Rectangle( m_hBackBuffer, m[i].x * SCALE, m[i].y * SCALE, (m[i].x + 1) * SCALE, (m[i].y + 1) * SCALE ); } SelectObject(m_hBackBuffer, hOldBrush); DeleteObject(hBrush);
Так же во время инициализации игры нужно создать фрукты на экране:
srand((unsigned)time( NULL )); //создаем 10 фруктов на поле for (int i=0;i<10;i++) m[i].New();
Теперь у нас есть 10 фруктов на игровом поле, но когда змейка наскакивает на фрукт пока ничего не происходит. Добавим код. Здесь в цикле преряем, если головной элемент змейки (координата) совпала с каким нибудь из 10 фруктов, увеличиваем змейку Num++ (например было 4 стало 5 элементов змейки), и рисуем фрукт в новом месте игрового поля.
//7)если головной элемент змейки наехал на фрукт for (int i = 0; i < NUM_FRUITS; i++) { if( (s[0].x == m[i].x) && (s[0].y == m[i].y) ) { Num++; //увеличиваем змейку m[i].New(); //выводим фрукт в новом месте игрового поля } }
Добавим проверку- змейка выходит за границы игрового поля, и когда змейка наскакивает на свой хвост- она должна сокращаться. Когда змейка выходит за границы игрового поля можно так же сделать Game Over. Но в нашем случае когда змейка выходит за границы игрового поля она меняет направление движения на противоположное. Тут один момент- змейка будет наскакивать на свой хвост и сокращаться. Как делается проверка наскочила змейка на себя или нет- если головной элемент (координаты) совпал с каким нибудь хвостовым элементом змейки- то сокращается количество элементов змейки.
//8)если змейка вышла за пределы игрового поля //меняем направление движеня на противоположное //змейка будет наскакивать на свой хвост при движении //в противоположном направлении (за экраном гол.эл.) //поэтому будет уменьшаться в размере if(s[0].x > FIELD_WIDTH) Dir = 1; if(s[0].x < 0) Dir = 2; if(s[0].y > FIELD_HEIGHT) Dir = 3; if(s[0].y < 0) Dir = 0; //9)если наскочила змейка на себя //головной элемент пересекаеться с хвостом for ( int i = 1; i < Num; i++ ) { if(s[0].x == s[i].x && s[0].y == s[i].y) Num = i; }
Причем последовательность кода такая:
void CMyApp::RenderScene() { 1)управление, контроль змейки и фруктов 2)отображение на экране линий игрового поля 3)отображение на экране фруктов 4)отображение на экране змейки }