Перейти к содержанию
Опубликовано
comment_234

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

Приступим.

 

Для начала создаем обычный консольный проект в VS и создаем файлы game.h, game.cpp, includes.h.

Начнём заполнять наши файлы кодом, для начала я заполню includes.h т.к это проще всего.

includes.h

#pragma once
#include <iostream> 
#include <conio.h>// для функции input control
#include "game.h"
#include <Windows.h> // Что бы замедлить игру 
using namespace std;

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

#pragma once
#include "includes.h"
class Game {
public:
	struct { // структура игровых переменных
		bool gameover; // игра продолжится пока наша переменная равна false
		int gamemode;
		const int width = 20;// ширина нашего поля
		const int lenght = 20;// длина нашего поля
		int SnakeY, SnakeX; // положение нашей змейки по Y и X
		int FruitsY, FruitsX;// положение наших фруктов ( игровых предметов за которые в дальнейшем мы получим "Score"), по Y и X
		int Score; // Наши очки 
		int tailX[100], tailY[100]; // координаты нашего хвоста
		int nTail = 0; // количество элементов в хвосте
	}GameVars;

	enum motion { // перечисляю игровые движения для того что бы в дальнейшем реализовать функцию game_inputcontrol
		STOP = 0, UP, DOWN, LEFT, RIGHT
	}; motion  move;
	void game_setup();// первоначальная настройка нашей игры
	void game_paint();// в данной функции мы будем заниматься отрисовкой обьектов (поля, фруктов, самой змейки) 
	void game_inputcontrol();// тут мы будем отслеживать нажатые клавиши
	void game_logic();// тут мы будем делать условия с помощью которых мы создадим логику игры
};

Теперь приступим писать наш game.cpp, для начала я решил описать int main()

#include "includes.h"
Game game; // создаем обьект класса Game для дальнейшей работы с ним 

int main() {
	game.game_setup();
	while (!game.GameVars.gameover) { // выполняем отрисовку, логику, и контроль нажатия клавиш пока мы играем
		game.game_paint();
		game.game_inputcontrol();
		game.game_logic();
	}
	return 0;
}

Дальше нам нужно описать первую нашу функцию, я опишу функцию game_setup.

void Game::game_setup() {
	game.GameVars.gameover = false; // по стандарту окончание игры = false чтобы игра продолжалась
	game.move = STOP; // По началу мы стоим на месте
	game.GameVars.SnakeX = game.GameVars.width / 2 - 1;// первоначальная настройка положения нашей змейки по X координате 
	game.GameVars.SnakeY = game.GameVars.lenght / 2 - 1;// первоначальная настройка положения нашей змейки по Y координате 
	game.GameVars.FruitsX = rand() % game.GameVars.width; //Рандомно спавним фрукты по нашей карте по X координате 
	game.GameVars.FruitsY = rand() % game.GameVars.lenght;//Рандомно спавним фрукты по нашей карте по Y координате 
	game.GameVars.Score = 0; // первоначальное кол-во баллов равно 0
}

Теперь давай те отрисуем нашу карту, на которой мы  и будем играть

void Game::game_paint() {
	system("cls"); //для постоянного обновления нашей карты
	for (int i = 0; i < game.GameVars.width + 1; i++) // отрисовка наших границ карты
		cout << "#";// решетка - символ которые означают границы нашей карты
	cout << endl;
	for (int i = 0; i < game.GameVars.lenght; i++) {
		for (int u = 0; u < game.GameVars.width; u++) {
			if (u == 0 || u == game.GameVars.width - 1)// отрисовка наших границ карты
				cout << "#";
			cout << " ";
		}
		cout << endl;
	}
	for (int i = 0; i < game.GameVars.width + 1; i++) // отрисовка наших границ карты
		cout << "#";
	cout << endl;	
}

Теперь давай те посмотрим что у нас получилось 

 

Дальше отрисуем нашу змейку, фрукт который надо будет есть и наши очки

void Game::game_paint() {
	system("cls"); //для постоянного обновления нашей карты
	for (int i = 0; i < game.GameVars.width + 1; i++) // отрисовка наших границ карты
		cout << "#";
	cout << endl;

	for (int i = 0; i < game.GameVars.lenght; i++) {
		for (int u = 0; u < game.GameVars.width; u++) {
			if (u == 0 || u == game.GameVars.width - 1)// отрисовка наших границ карты
				cout << "#";
			if (i == game.GameVars.SnakeY && u == game.GameVars.SnakeX)
				cout << "8"; // отрисовка нашей змейки
			else if (i == game.GameVars.FruitsY && u == game.GameVars.FruitsX)
				cout << "0"; // отрисовка нашего фрукта
			else {
				
					cout << " ";
			}
		}
		cout << endl;
	}

	for (int i = 0; i < game.GameVars.width + 1; i++) // отрисовка наших границ карты
		cout << "#";
	cout << endl;
	cout << "    Your Score :" << game.GameVars.Score << endl;// отрисовка наших очков
}

Посмотрим, что у нас вышло 

 

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

void Game::game_inputcontrol() {
	if (_kbhit()) { // данная функция возвращает true если пользователь что либо нажал 
		switch (_getch())// отслеживаем какую именно клавишу нажал пользователь
		{
		case 'a':
			game.move = game.LEFT; // если мы нажмём a - змейка будет двигаться влево и дальше аналогично 
			break;
		case 'd':
			game.move = game.RIGHT;
			break;
		case 's':
			game.move = game.DOWN;
			break;
		case 'w':
			game.move = game.UP;
			break;
		}
	}
}

Теперь напишем простую логику для нашей игры

void Game::game_logic() {
	switch (game.move) {  // Проверяем куда движится наша змейка и в зависимости от движения делаем нужные махинации со самой змейкой
	case game.LEFT:
		game.GameVars.SnakeX--; //Если змейка движется в лево то мы уменьшаем значение на 1 и так далее
		break;
	case game.RIGHT:
		game.GameVars.SnakeX++;
		break;
	case game.DOWN:
		game.GameVars.SnakeY++;
		break;
	case game.UP:
		game.GameVars.SnakeY--;
		break;
	}
}

И что же мы получили? Теперь наша змейка может передвигаться. Давайте допишем логику нашей игры

void Game::game_logic() {
	switch (game.move) {  // Проверяем куда движится наша змейка и в зависимости от движения делаем нужные махинации со самой змейкой
	case game.LEFT:
		game.GameVars.SnakeX--; //Если змейка движется в лево то мы уменьшаем значение на 1 и так далее
		break;
	case game.RIGHT:
		game.GameVars.SnakeX++;
		break;
	case game.DOWN:
		game.GameVars.SnakeY++;
		break;
	case game.UP:
		game.GameVars.SnakeY--;
		break;
	}
	if (game.GameVars.SnakeY == game.GameVars.FruitsY && game.GameVars.SnakeX == game.GameVars.FruitsX) { // простая логика поедания фруктов
		game.GameVars.FruitsX = rand() % game.GameVars.width;// Рандомим новый фрукт
		game.GameVars.FruitsY = rand() % game.GameVars.lenght;
		game.GameVars.Score += 10; // если сьели фрукт прибавляем 10 очков 
	
	}
}

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

void Game::game_logic() {
	int prevX = game.GameVars.tailX[0];
	int prevY = game.GameVars.tailY[0];
	int prev2x, prev2y;

	game.GameVars.tailX[0] = game.GameVars.SnakeX;
	game.GameVars.tailY[0] = game.GameVars.SnakeY;

	for (int i = 1; i < game.GameVars.nTail; i++) {
		prev2x = game.GameVars.tailX[i];
		prev2y = game.GameVars.tailY[i];
		game.GameVars.tailX[i] = prevX;
		game.GameVars.tailY[i] = prevY;
		prevX = prev2x;
		prevY = prev2y;
	} // логигка для хвоста
	switch (game.move) {  // Проверяем куда движится наша змейка и в зависимости от движения делаем нужные махинации со самой змейкой
	case game.LEFT:
		game.GameVars.SnakeX--; //Если змейка движется в лево то мы уменьшаем значение на 1 и так далее
		break;
	case game.RIGHT:
		game.GameVars.SnakeX++;
		break;
	case game.DOWN:
		game.GameVars.SnakeY++;
		break;
	case game.UP:
		game.GameVars.SnakeY--;
		break;
	}
	if (game.GameVars.SnakeY == game.GameVars.FruitsY && game.GameVars.SnakeX == game.GameVars.FruitsX) { // простая логика поедания фруктов
		game.GameVars.FruitsX = rand() % game.GameVars.width;// Рандомим новый фрукт
		game.GameVars.FruitsY = rand() % game.GameVars.lenght;
		game.GameVars.Score += 10; // если сьели фрукт прибавляем 10 очков 
	
	}
}

Теперь отрисуем наш хвостик 

void Game::game_paint() {
	system("cls"); //для постояного обновления нашей карты
	for (int i = 0; i < game.GameVars.width + 1; i++) // отрисовка наших границ карты
		cout << "#";
	cout << endl;

	for (int i = 0; i < game.GameVars.lenght; i++) {
		for (int u = 0; u < game.GameVars.width; u++) {
			if (u == 0 || u == game.GameVars.width - 1)// отрисовка наших границ карты
				cout << "#";
			if (i == game.GameVars.SnakeY && u == game.GameVars.SnakeX)
				cout << "8"; // отрисовка нашей змейки
			else if (i == game.GameVars.FruitsY && u == game.GameVars.FruitsX)
				cout << "0"; // отрисовка нашего фрукта
			else {
				bool print = false;
				for (int k = 0; k < game.GameVars.nTail; k++) {
					if (game.GameVars.tailX[k] == u && game.GameVars.tailY[k] == i) {
						print = true;
						cout << "o"; // отрисовка хвоста
					}
				}
				if (!print)
					cout << " ";
			}
		}
		cout << endl;
	}

	for (int i = 0; i < game.GameVars.width + 1; i++) // отрисовка наших границ карты
		cout << "#";
	cout << endl;
	cout << "    Your Score :" << game.GameVars.Score << endl;// отрисовка наших очков
}

Теперь сделаем так, что когда мы сьедим фрукт то наш хвост увеличится на 1 символ 

void Game::game_logic() {
	int prevX = game.GameVars.tailX[0];
	int prevY = game.GameVars.tailY[0];
	int prev2x, prev2y;

	game.GameVars.tailX[0] = game.GameVars.SnakeX;
	game.GameVars.tailY[0] = game.GameVars.SnakeY;

	for (int i = 1; i < game.GameVars.nTail; i++) {
		prev2x = game.GameVars.tailX[i];
		prev2y = game.GameVars.tailY[i];
		game.GameVars.tailX[i] = prevX;
		game.GameVars.tailY[i] = prevY;
		prevX = prev2x;
		prevY = prev2y;
	} // логигка для хвоста
	switch (game.move) {  // Проверяем куда движится наша змейка и в зависимости от движения делаем нужные махинации со самой змейкой
	case game.LEFT:
		game.GameVars.SnakeX--; //Если змейка движется в лево то мы уменьшаем значение на 1 и так далее
		break;
	case game.RIGHT:
		game.GameVars.SnakeX++;
		break;
	case game.DOWN:
		game.GameVars.SnakeY++;
		break;
	case game.UP:
		game.GameVars.SnakeY--;
		break;
	}
	if (game.GameVars.SnakeY == game.GameVars.FruitsY && game.GameVars.SnakeX == game.GameVars.FruitsX) { // простая логика поедания фруктов
		game.GameVars.FruitsX = rand() % game.GameVars.width;// Рандомим новый фрукт
		game.GameVars.FruitsY = rand() % game.GameVars.lenght;
		game.GameVars.Score += 10; // если сьели фрукт прибавляем 10 очков 
		game.GameVars.nTail++;//если сьели фрукт то наш хвост +1
	}
}

Результат 

 

Наша игра почти готова, теперь добавим в логике проверки на то , что если змея сьест свой хвост - игра закончится ведь мы сьели свой хвост.

Так же добавим Gamemode .

void Game::game_logic() {
	int prevX = game.GameVars.tailX[0];
	int prevY = game.GameVars.tailY[0];
	int prev2x, prev2y;

	game.GameVars.tailX[0] = game.GameVars.SnakeX;
	game.GameVars.tailY[0] = game.GameVars.SnakeY;

	for (int i = 1; i < game.GameVars.nTail; i++) { 
		prev2x = game.GameVars.tailX[i];
		prev2y = game.GameVars.tailY[i];
		game.GameVars.tailX[i] = prevX;
		game.GameVars.tailY[i] = prevY;
		prevX = prev2x;
		prevY = prev2y;
	} // логигка для хвоста

	switch (game.move) {  // Проверяем куда движится наша змейка и в зависимости от движения делаем нужные махинации со самой змейкой
	case game.LEFT:
		game.GameVars.SnakeX--; // Тут всё просто думаю обьяснять не стоит
		break;
	case game.RIGHT:
		game.GameVars.SnakeX++;
		break;
	case game.DOWN:
		game.GameVars.SnakeY++;
		break;
	case game.UP:
		game.GameVars.SnakeY--;
		break;
	}
	if (game.GameVars.SnakeY == game.GameVars.FruitsY && game.GameVars.SnakeX == game.GameVars.FruitsX) { // простая логига поедания фруктов
		game.GameVars.FruitsX = rand() % game.GameVars.width;
		game.GameVars.FruitsY = rand() % game.GameVars.lenght;
		game.GameVars.Score += 10; // если сьели фрукт прибавляем 10 очков 
		game.GameVars.nTail++;//если сьели фрукт то наш хвсот +1
	}
	

		if (game.GameVars.gamemode == 0) { // 1 режим это easy - если змейка зайдет за стену тогда она выйдет с обратной её стороны
			if (game.GameVars.SnakeX >= game.GameVars.width - 1)
				game.GameVars.SnakeX = 0;
			else if (game.GameVars.SnakeX < 0)
				game.GameVars.SnakeX = game.GameVars.width - 2;

			if (game.GameVars.SnakeY >= game.GameVars.lenght)
				game.GameVars.SnakeY = 0;
			else if (game.GameVars.SnakeY < 0)
				game.GameVars.SnakeY = game.GameVars.lenght - 1;
		}
		else { // если змейка столкнётся со стеной тогда мы проиграли - hard 
			if (game.GameVars.SnakeX > game.GameVars.width || game.GameVars.SnakeX < 0 || game.GameVars.SnakeY > game.GameVars.lenght || game.GameVars.SnakeY < 0)
				game.GameVars.gameover = true; 
		}
		for (int i = 0; i < game.GameVars.nTail; i++) { // если мы сьели свой хвост 
			if (game.GameVars.tailX[i] == game.GameVars.SnakeX && game.GameVars.tailY[i] == game.GameVars.SnakeY) {
				game.GameVars.gameover = true; // тогда мы проиграли
			}
		}

	}

Осталось дописать наш int main() 

int main() {
	game.game_setup();
	cout << "Hello, this is a snake game!\n";
	cout << "Select mode : easy or hard\n";
	cout << "0 - easy, 1 - hard please write :";
	cin >> game.GameVars.gamemode;
	while (!game.GameVars.gameover) {
		game.game_paint();
		game.game_inputcontrol();
		game.game_logic();
		Sleep(300); // скорость нашей игры ( вот тут надо смотреть для кого как потому что например на моём железе всё обрабатывается слишком быстро
		// и моя змейка от 1 нажатия улетает за пределы карты ну для моего пк значение 300 для функции Sleep вполне нормально
	}
	return 0;
}

Результат - 

 

Ссылка на готовый source code - Download

Рекомендуемые сообщения

Гость
Эта тема закрыта для публикации ответов.