Serpent gourmand en langage C, serpent gourmand réalisation de code en langage C Daquan

1. Préface à l'implémentation du code serpent en langage C

L'objectif principal de la conception de Snake Game est de permettre à chacun de consolider les bases du langage C, d'entraîner la réflexion sur la programmation, de cultiver des idées de résolution de problèmes et d'apprécier le langage C coloré.

Snake est un jeu très classique. Cette fois, nous simulons l'implémentation du jeu Snake sur la console, qui consiste à réaliser les fonctions de base de Snake. Par exemple, dans la carte, utilisez "↑↓←→" pour contrôler le mouvement du direction du serpent, après avoir mangé la nourriture, le corps du serpent grandira et ainsi de suite. . . .

Tout d'abord, nous devons analyser certaines situations que nous rencontrerons dans le jeu.

①La partie du serpent, le corps du serpent est section par section, la structure de données la plus facilement associée à ce moment est la liste de séquences, la liste chaînée, si vous comparez le serpent à la liste de séquences ou à la liste chaînée, quand vous manger de la nourriture plus tard, le corps deviendra certainement plus long, ce qui implique l'opération d'insertion, donc pour une plus grande efficacité, nous utilisons une liste chaînée pour mettre en œuvre notre partie de serpent. Initialement, nous avons imprimé le corps du serpent à l'écran selon quatre nœuds.

②Le mouvement du serpent.Le mouvement du serpent sur l'écran donne l'impression que tout le corps est avancé d'une unité, mais le principe est que nous réimprimons le serpent à un autre endroit de l'écran et supprimons le corps de serpent précédent.

③Pour la génération de nourriture, un nœud est généré aléatoirement sur la carte.Lorsque les coordonnées de la tête du serpent et les coordonnées de la nourriture sont répétées, la nourriture disparaît et le corps du serpent est allongé, c'est-à-dire le nombre de nœuds du serpent augmente de un.

④ Plusieurs états du serpent à l'intérieur, état normal : les coordonnées du nœud de la tête du serpent ne coïncident pas avec les coordonnées du mur et les coordonnées de son propre corps,

Tué par vous-même : les coordonnées de la tête de serpent coïncident avec les coordonnées du corps du serpent et frappent le mur : les coordonnées de la tête de serpent coïncident avec les coordonnées du mur. À travers les murs : les serpents ne s'écraseront pas contre les murs et ne mourront pas.

Deuxièmement, le code serpent en langage C pour réaliser la description du jeu

1. Un grand nombre de codes sources de jeux de projets en langage C peuvent être obtenus en suivant le compte public WeChat : "C et C plus plus" Réponse : "TC" suffit

2. Il n'y a pas de descriptions de boutons associées imprimées dans l'interface du jeu Snake en langage C. Ici, nous les listons un par un . Les descriptions des boutons du jeu Snake en langage C :

  • Appuyez sur les touches fléchées haut, bas, gauche et droite pour changer la direction de déplacement du serpent.

  • Appuyez et maintenez enfoncée l'une des touches fléchées haut, bas, gauche et droite pendant une courte période pour accélérer le mouvement du serpent dans cette direction pendant une courte période.

  • Appuyez sur la barre d'espace pour faire une pause, puis appuyez sur n'importe quelle touche pour reprendre le jeu.

  • Appuyez sur la touche Esc pour quitter le jeu directement.

  • Appuyez sur la touche R pour redémarrer le jeu.

De plus, le jeu dispose également d'un système de notation, qui peut enregistrer le record du joueur le plus élevé de l'histoire.

Trois, affichage de l'effet de jeu de code serpent en langage C

La vitesse de déplacement du serpent dans le jeu Snake peut être ajustée. Dans l'animation, la vitesse est ajustée plus lentement (la vitesse est trop rapide et le corps du serpent n'est pas entièrement affiché dans l'animation). Le code ci-dessous ajuste la vitesse de le serpent à une vitesse appropriée, vous pouvez essayer

Quatrièmement, le code du jeu de serpent en langage C est le suivant

Vous pouvez copier le code suivant dans votre propre compilateur pour exécuter :

#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>

#define ROW 22 //游戏区行数
#define COL 42 //游戏区列数

#define KONG 0 //标记空(什么也没有)
#define WALL 1 //标记墙
#define FOOD 2 //标记食物
#define HEAD 3 //标记蛇头
#define BODY 4 //标记蛇身

#define UP 72 //方向键:上
#define DOWN 80 //方向键:下
#define LEFT 75 //方向键:左
#define RIGHT 77 //方向键:右
#define SPACE 32 //暂停
#define ESC 27 //退出

//蛇头
struct Snake
{
	int len; //记录蛇身长度
	int x; //蛇头横坐标
	int y; //蛇头纵坐标
}snake;

//蛇身
struct Body
{
	int x; //蛇身横坐标
	int y; //蛇身纵坐标
}body[ROW*COL]; //开辟足以存储蛇身的结构体数组

int face[ROW][COL]; //标记游戏区各个位置的状态

//隐藏光标
void HideCursor();
//光标跳转
void CursorJump(int x, int y);
//初始化界面
void InitInterface();
//颜色设置
void color(int c);
//从文件读取最高分
void ReadGrade();
//更新最高分到文件
void WriteGrade();
//初始化蛇
void InitSnake();
//随机生成食物
void RandFood();
//判断得分与结束
void JudgeFunc(int x, int y);
//打印蛇与覆盖蛇
void DrawSnake(int flag);
//移动蛇
void MoveSnake(int x, int y);
//执行按键
void run(int x, int y);
//游戏主体逻辑函数
void Game();

int max, grade; //全局变量
int main()
{
#pragma warning (disable:4996) //消除警告
	max = 0, grade = 0; //初始化变量
	system("title 贪吃蛇"); //设置cmd窗口的名字
	system("mode con cols=84 lines=23"); //设置cmd窗口的大小
	HideCursor(); //隐藏光标
	ReadGrade(); //从文件读取最高分到max变量
	InitInterface(); //初始化界面
	InitSnake(); //初始化蛇
	srand((unsigned int)time(NULL)); //设置随机数生成起点
	RandFood(); //随机生成食物
	DrawSnake(1); //打印蛇
	Game(); //开始游戏
	return 0;
}

//隐藏光标
void HideCursor()
{
	CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量
	curInfo.dwSize = 1; //如果没赋值的话,光标隐藏无效
	curInfo.bVisible = FALSE; //将光标设置为不可见
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
	SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
}
//光标跳转
void CursorJump(int x, int y)
{
	COORD pos; //定义光标位置的结构体变量
	pos.X = x; //横坐标
	pos.Y = y; //纵坐标
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
	SetConsoleCursorPosition(handle, pos); //设置光标位置
}
//初始化界面
void InitInterface()
{
	color(6); //颜色设置为土黄色
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if (j == 0 || j == COL - 1)
			{
				face[i][j] = WALL; //标记该位置为墙
				CursorJump(2 * j, i);
				printf("■");
			}
			else if (i == 0 || i == ROW - 1)
			{
				face[i][j] = WALL; //标记该位置为墙
				printf("■");
			}
			else
			{
				face[i][j] = KONG; //标记该位置为空
			}
		}
	}
	color(7); //颜色设置为白色
	CursorJump(0, ROW);
	printf("当前得分:%d", grade);
	CursorJump(COL, ROW);
	printf("历史最高得分:%d", max);
}
//颜色设置
void color(int c)
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
	//注:SetConsoleTextAttribute是一个API(应用程序编程接口)
}
//从文件读取最高分
void ReadGrade()
{
	FILE* pf = fopen("贪吃蛇最高得分记录.txt", "r"); //以只读的方式打开文件
	if (pf == NULL) //打开文件失败
	{
		pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
		fwrite(&max, sizeof(int), 1, pf); //将max写入文件(此时max为0),即将最高得分初始化为0
	}
	fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头
	fread(&max, sizeof(int), 1, pf); //读取文件当中的最高得分到max当中
	fclose(pf); //关闭文件
	pf = NULL; //文件指针及时置空
}
//更新最高分到文件
void WriteGrade()
{
	FILE* pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
	if (pf == NULL) //打开文件失败
	{
		printf("保存最高得分记录失败\n");
		exit(0);
	}
	fwrite(&grade, sizeof(int), 1, pf); //将本局游戏得分写入文件当中
	fclose(pf); //关闭文件
	pf = NULL; //文件指针及时置空
}
//初始化蛇
void InitSnake()
{
	snake.len = 2; //蛇的身体长度初始化为2
	snake.x = COL / 2; //蛇头位置的横坐标
	snake.y = ROW / 2; //蛇头位置的纵坐标
	//蛇身坐标的初始化
	body[0].x = COL / 2 - 1;
	body[0].y = ROW / 2;
	body[1].x = COL / 2 - 2;
	body[1].y = ROW / 2;
	//将蛇头和蛇身位置进行标记
	face[snake.y][snake.x] = HEAD;
	face[body[0].y][body[0].x] = BODY;
	face[body[1].y][body[1].x] = BODY;
}
//随机生成食物
void RandFood()
{
	int i, j;
	do
	{
		//随机生成食物的横纵坐标
		i = rand() % ROW;
		j = rand() % COL;
	} while (face[i][j] != KONG); //确保生成食物的位置为空,若不为空则重新生成
	face[i][j] = FOOD; //将食物位置进行标记
	color(12); //颜色设置为红色
	CursorJump(2 * j, i); //光标跳转到生成的随机位置处
	printf("●"); //打印食物
}
//判断得分与结束
void JudgeFunc(int x, int y)
{
	//若蛇头即将到达的位置是食物,则得分
	if (face[snake.y + y][snake.x + x] == FOOD)
	{
		snake.len++; //蛇身加长
		grade += 10; //更新当前得分
		color(7); //颜色设置为白色
		CursorJump(0, ROW);
		printf("当前得分:%d", grade); //重新打印当前得分
		RandFood(); //重新随机生成食物
	}
	//若蛇头即将到达的位置是墙或者蛇身,则游戏结束
	else if (face[snake.y + y][snake.x + x] == WALL || face[snake.y + y][snake.x + x] == BODY)
	{
		Sleep(1000); //留给玩家反应时间
		system("cls"); //清空屏幕
		color(7); //颜色设置为白色
		CursorJump(2 * (COL / 3), ROW / 2 - 3);
		if (grade > max)
		{
			printf("恭喜你打破最高记录,最高记录更新为%d", grade);
			WriteGrade();
		}
		else if (grade == max)
		{
			printf("与最高记录持平,加油再创佳绩", grade);
		}
		else
		{
			printf("请继续加油,当前与最高记录相差%d", max - grade);
		}
		CursorJump(2 * (COL / 3), ROW / 2);
		printf("GAME OVER");
		while (1) //询问玩家是否再来一局
		{
			char ch;
			CursorJump(2 * (COL / 3), ROW / 2 + 3);
			printf("再来一局?(y/n):");
			scanf("%c", &ch);
			if (ch == 'y' || ch == 'Y')
			{
				system("cls");
				main();
			}
			else if (ch == 'n' || ch == 'N')
			{
				CursorJump(2 * (COL / 3), ROW / 2 + 5);
				exit(0);
			}
			else
			{
				CursorJump(2 * (COL / 3), ROW / 2 + 5);
				printf("选择错误,请再次选择");
			}
		}
	}
}
//打印蛇与覆盖蛇
void DrawSnake(int flag)
{
	if (flag == 1) //打印蛇
	{
		color(10); //颜色设置为绿色
		CursorJump(2 * snake.x, snake.y);
		printf("■"); //打印蛇头
		for (int i = 0; i < snake.len; i++)
		{
			CursorJump(2 * body[i].x, body[i].y);
			printf("□"); //打印蛇身
		}
	}
	else //覆盖蛇
	{
		if (body[snake.len - 1].x != 0) //防止len++后将(0, 0)位置的墙覆盖
		{
			//将蛇尾覆盖为空格即可
			CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
			printf("  ");
		}
	}
}
//移动蛇
void MoveSnake(int x, int y)
{
	DrawSnake(0); //先覆盖当前所显示的蛇
	face[body[snake.len - 1].y][body[snake.len - 1].x] = KONG; //蛇移动后蛇尾重新标记为空
	face[snake.y][snake.x] = BODY; //蛇移动后蛇头的位置变为蛇身
	//蛇移动后各个蛇身位置坐标需要更新
	for (int i = snake.len - 1; i > 0; i--)
	{
		body[i].x = body[i - 1].x;
		body[i].y = body[i - 1].y;
	}
	//蛇移动后蛇头位置信息变为第0个蛇身的位置信息
	body[0].x = snake.x;
	body[0].y = snake.y;
	//蛇头的位置更改
	snake.x = snake.x + x;
	snake.y = snake.y + y;
	DrawSnake(1); //打印移动后的蛇
}
//执行按键
void run(int x, int y)
{
	int t = 0;
	while (1)
	{
		if (t == 0)
			t = 3000; //这里t越小,蛇移动速度越快(可以根据次设置游戏难度)
		while (--t)
		{
			if (kbhit() != 0) //若键盘被敲击,则退出循环
				break;
		}
		if (t == 0) //键盘未被敲击
		{
			JudgeFunc(x, y); //判断到达该位置后,是否得分与游戏结束
			MoveSnake(x, y); //移动蛇
		}
		else //键盘被敲击
		{
			break; //返回Game函数读取键值
		}
	}
}
//游戏主体逻辑函数
void Game()
{
	int n = RIGHT; //开始游戏时,默认向后移动
	int tmp = 0; //记录蛇的移动方向
	goto first; //第一次进入循环先向默认方向前进
	while (1)
	{
		n = getch(); //读取键值
		//在执行前,需要对所读取的按键进行调整
		switch (n)
		{
		case UP:
		case DOWN: //如果敲击的是“上”或“下”
			if (tmp != LEFT&&tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
			{
				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
			}
			break;
		case LEFT:
		case RIGHT: //如果敲击的是“左”或“右”
			if (tmp != UP&&tmp != DOWN) //并且上一次蛇的移动方向不是“上”或“下”
			{
				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
			}
		case SPACE:
		case ESC:
		case 'r':
		case 'R':
			break; //这四个无需调整
		default:
			n = tmp; //其他键无效,默认为上一次蛇移动的方向
			break;
		}
	first: //第一次进入循环先向默认方向前进
		switch (n)
		{
		case UP: //方向键:上
			run(0, -1); //向上移动(横坐标偏移为0,纵坐标偏移为-1)
			tmp = UP; //记录当前蛇的移动方向
			break;
		case DOWN: //方向键:下
			run(0, 1); //向下移动(横坐标偏移为0,纵坐标偏移为1)
			tmp = DOWN; //记录当前蛇的移动方向
			break;
		case LEFT: //方向键:左
			run(-1, 0); //向左移动(横坐标偏移为-1,纵坐标偏移为0)
			tmp = LEFT; //记录当前蛇的移动方向
			break;
		case RIGHT: //方向键:右
			run(1, 0); //向右移动(横坐标偏移为1,纵坐标偏移为0)
			tmp = RIGHT; //记录当前蛇的移动方向
			break;
		case SPACE: //暂停
			system("pause>nul"); //暂停后按任意键继续
			break;
		case ESC: //退出
			system("cls"); //清空屏幕
			color(7); //颜色设置为白色
			CursorJump(COL - 8, ROW / 2);
			printf("  游戏结束  ");
			CursorJump(COL - 8, ROW / 2 + 2);
			exit(0);
		case 'r':
		case 'R': //重新开始
			system("cls"); //清空屏幕
			main(); //重新执行主函数
		}
	}
}

Cinq, explication détaillée du code de jeu de serpent en langage C

1. Le langage Snake C implémente la construction d'un framework de jeu

Définissez d'abord la taille de l'interface de jeu, définissez le nombre de lignes et de colonnes dans la zone de jeu.

 

Ici, la zone où le serpent est actif s'appelle la zone de jeu, et la zone où le score est indiqué s'appelle la zone d'indice (la zone d'indice occupe une ligne).

 

De plus, nous avons besoin de deux structures pour représenter la tête et le corps du serpent. La structure de tête de serpent stocke la longueur du corps de serpent actuel et les coordonnées de position de la tête de serpent.

 

Les coordonnées de position du corps du serpent sont stockées dans la structure du corps du serpent.

 

En même temps, nous avons besoin d'un tableau à deux dimensions pour marquer le statut de chaque position dans la zone de jeu (vide, mur, nourriture, tête de serpent et corps de serpent).

 

Afin d'augmenter la lisibilité du code, il est préférable d'utiliser des macros pour définir l'état de chaque position, plutôt que de changer l'état de chaque position avec des nombres secs dans le code.

 

Bien sûr, pour la lisibilité du code, nous ferions mieux de définir les valeurs clés des clés que nous devons utiliser avec les macros.

2. Cachez le curseur

Masquer le curseur est relativement simple. Définissez une variable de structure des informations du curseur, puis affectez les informations du curseur, et enfin utilisez la variable de structure des informations du curseur pour définir les informations du curseur.

3. Saut de curseur

Cursor jump, c'est-à-dire laisser le curseur sauter à la position spécifiée pour la sortie. Semblable aux étapes de l'opération de masquage du curseur, définissez d'abord une variable de structure de la position du curseur, puis définissez les coordonnées horizontales et verticales du curseur, et enfin utilisez la variable de structure de la position du curseur pour définir la position du curseur.

4. Interface d'initialisation

L'interface d'initialisation termine l'impression du "mur" de la zone de jeu et l'impression de la zone de prompt.

Il y a deux points auxquels il faut faire attention lors du processus d'impression :

Dans la fenêtre cmd, un petit carré occupe deux unités d'abscisse et une unité d'ordonnée.

La fonction de saut de curseur CursorJump reçoit les coordonnées horizontales et verticales de la position où le curseur sautera.

Par exemple, utiliser la fonction CursorJump pour sauter à la ligne i et à la colonne j (avec un petit carré comme unité) équivaut à sauter le curseur à la coordonnée (2*j, i).

//初始化界面
void InitInterface()
{
	color(6); //颜色设置为土黄色
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if (j == 0 || j == COL - 1)
			{
				face[i][j] = WALL; //标记该位置为墙
				CursorJump(2 * j, i);
				printf("■");
			}
			else if (i == 0 || i == ROW - 1)
			{
				face[i][j] = WALL; //标记该位置为墙
				printf("■");
			}
			else
			{
				face[i][j] = KONG; //标记该位置为空
			}
		}
	}
	color(7); //颜色设置为白色
	CursorJump(0, ROW);
	printf("当前得分:%d", grade);
	CursorJump(COL, ROW);
	printf("历史最高得分:%d", max);
}

Remarque :  Lors de l'initialisation de l'interface, pensez à marquer l'état de la position correspondante dans la zone de jeu.

5. Réglage des couleurs du jeu Snake en langage C

La fonction de réglage de la couleur est de changer la couleur du contenu de sortie à la couleur spécifiée. Le paramètre reçu c est le code de couleur. Le tableau des codes de couleur décimaux est le suivant :

 

La déclaration de la fonction set color dans son fichier d'en-tête est la suivante :

 

6. Initialiser le serpent

Lors de l'initialisation du serpent, la longueur du corps du serpent est initialisée à 2, la position de départ de la tête de serpent est au centre de la zone de jeu et la tête de serpent est le 0e corps de serpent et le premier corps de serpent à droite.

 Après avoir initialisé les informations sur le serpent, n'oubliez pas de marquer l'état de l'emplacement dans l'aire de jeu.

7. Générez de la nourriture au hasard

Pour générer de la nourriture de manière aléatoire dans la zone de jeu, il est nécessaire de juger les coordonnées générées.Ce n'est que lorsque l'emplacement est vide que la nourriture peut être générée ici, sinon les coordonnées doivent être régénérées. Une fois les coordonnées alimentaires déterminées, l'état de l'emplacement dans la zone de jeu doit être marqué.

8. Serpent d'impression vs serpent de couverture

Les serpents d'impression et de couverture sont implémentés directement à l'aide d'une fonction. Si le drapeau du paramètre entrant est 1, le serpent sera imprimé ; si le paramètre entrant est 0, le serpent sera remplacé par des espaces.

imprimé serpent :

Obtenez d'abord les coordonnées de la tête de serpent en fonction de la variable de structure serpent et imprimez la tête de serpent à la position correspondante.

Ensuite, obtenez les coordonnées du corps du serpent à tour de rôle en fonction du corps du tableau de structure et imprimez à la position correspondante.

Couvrir le serpent :

Couvrez simplement le dernier corps de serpent avec un espace.

Cependant, il faut faire attention pour déterminer si la position couverte est la position (0,0) avant de couvrir, car lorsque la longueur du corps du serpent augmente après le score, il faut couvrir le serpent courant (puis imprimer le serpent avec la longueur augmentée), et à ce moment, le nouveau corps de serpent est ajouté. Il n'a pas encore été assigné (le compilateur est généralement initialisé à 0 par défaut), les coordonnées que nous avons obtenues en fonction du dernier corps de serpent sont ( 0,0), et le mur à la position (0,0) sera recouvert d'un espace. Après la mise en œuvre de la fonction de serpent mobile suivante, nous la comprendrons. (Vous pouvez également d'abord supprimer ce jugement, observer le changement du mur à la position (0,0) après que le serpent a mangé la nourriture, puis l'analyser)

//打印蛇与覆盖蛇
void DrawSnake(int flag)
{
	if (flag == 1) //打印蛇
	{
		color(10); //颜色设置为绿色
		CursorJump(2 * snake.x, snake.y);
		printf("■"); //打印蛇头
		for (int i = 0; i < snake.len; i++)
		{
			CursorJump(2 * body[i].x, body[i].y);
			printf("□"); //打印蛇身
		}
	}
	else //覆盖蛇
	{
		if (body[snake.len - 1].x != 0) //防止len++后将(0, 0)位置的墙覆盖
		{
			//将蛇尾覆盖为空格即可
			CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
			printf("  ");
		}
	}
}

9. Serpent en mouvement

La fonction de la fonction de serpent en mouvement est de couvrir d'abord le serpent actuellement affiché, puis d'imprimer le serpent déplacé.

Description du paramètre :

x : le changement d'abscisse après le déplacement du serpent par rapport à l'abscisse du serpent actuel.

y : le changement d'ordonnée après le déplacement du serpent par rapport à l'ordonnée du serpent actuel.

Après le déplacement du serpent, diverses informations doivent changer :

Le dernier corps de serpent dans la zone de jeu doit être marqué comme vide.

La position de la tête du serpent doit être re-marquée en tant que corps du serpent dans la zone de jeu.

Dans le corps du tableau de structure qui stocke les informations de coordonnées du corps de serpent, les informations de coordonnées du corps de serpent du i-ème segment doivent être mises à jour avec les informations de coordonnées du corps de serpent du i-1 segment, et le 0ème segment, c'est-à-dire que les informations de coordonnées du premier segment du corps du serpent doivent être mises à jour. Mettez à jour les informations de coordonnées de la tête de serpent actuelle.

Les informations de coordonnées de la tête de serpent doivent être recalculées en fonction des paramètres entrants x et y.

10. Fonction logique principale du jeu

Logique du corps principal :

Entrez d'abord la fonction pour la première fois, le serpent par défaut se déplace vers la droite, puis exécute la fonction d'exécution.

Jusqu'à ce que le clavier soit tapé, revenez de la fonction d'exécution à la fonction de jeu pour la lecture des clés.

Après avoir lu la valeur de la clé, vous devez ajuster la clé de lecture (c'est nécessaire).

Après le réglage, effectuez l'exécution de la clé, puis la lecture de la clé, et ainsi de suite.

Mécanisme de réglage des boutons :

Si vous appuyez sur la touche "Haut" ou "Bas" et que la dernière fois que la direction de déplacement du serpent n'était pas "Gauche" ou "Droite", définissez la direction de déplacement du serpent suivant sur la direction de déplacement du dernier serpent, c'est-à-dire la direction de déplacement constant.

Si la touche "gauche" ou "droite" est enfoncée et que la direction de déplacement du dernier serpent n'est pas "haut" ou "bas", la direction de déplacement du serpent suivant est définie sur la direction de déplacement du dernier serpent, c'est-à-dire la constante de direction de déplacement.

Si la touche enfoncée est Espace, Échap, r ou R, aucun réglage n'est effectué.

Les autres touches ne sont pas valides et la direction de déplacement du serpent suivant est définie sur la direction de déplacement du dernier serpent, c'est-à-dire que la direction de déplacement reste inchangée.

//游戏主体逻辑函数
void Game()
{
	int n = RIGHT; //开始游戏时,默认向后移动
	int tmp = 0; //记录蛇的移动方向
	goto first; //第一次进入循环先向默认方向前进
	while (1)
	{
		n = getch(); //读取键值
		//在执行前,需要对所读取的按键进行调整
		switch (n)
		{
		case UP:
		case DOWN: //如果敲击的是“上”或“下”
			if (tmp != LEFT&&tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
			{
				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
			}
			break;
		case LEFT:
		case RIGHT: //如果敲击的是“左”或“右”
			if (tmp != UP&&tmp != DOWN) //并且上一次蛇的移动方向不是“上”或“下”
			{
				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
			}
		case SPACE:
		case ESC:
		case 'r':
		case 'R':
			break; //这四个无需调整
		default:
			n = tmp; //其他键无效,默认为上一次蛇移动的方向
			break;
		}
	first: //第一次进入循环先向默认方向前进
		switch (n)
		{
		case UP: //方向键:上
			run(0, -1); //向上移动(横坐标偏移为0,纵坐标偏移为-1)
			tmp = UP; //记录当前蛇的移动方向
			break;
		case DOWN: //方向键:下
			run(0, 1); //向下移动(横坐标偏移为0,纵坐标偏移为1)
			tmp = DOWN; //记录当前蛇的移动方向
			break;
		case LEFT: //方向键:左
			run(-1, 0); //向左移动(横坐标偏移为-1,纵坐标偏移为0)
			tmp = LEFT; //记录当前蛇的移动方向
			break;
		case RIGHT: //方向键:右
			run(1, 0); //向右移动(横坐标偏移为1,纵坐标偏移为0)
			tmp = RIGHT; //记录当前蛇的移动方向
			break;
		case SPACE: //暂停
			system("pause>nul"); //暂停后按任意键继续
			break;
		case ESC: //退出
			system("cls"); //清空屏幕
			color(7); //颜色设置为白色
			CursorJump(COL - 8, ROW / 2);
			printf("  游戏结束  ");
			CursorJump(COL - 8, ROW / 2 + 2);
			exit(0);
		case 'r':
		case 'R': //重新开始
			system("cls"); //清空屏幕
			main(); //重新执行主函数
		}
	}
}

11. Bouton Exécuter

Description du paramètre :

x : le changement d'abscisse après le déplacement du serpent par rapport à l'abscisse du serpent actuel.

y : le changement d'ordonnée après le déplacement du serpent par rapport à l'ordonnée du serpent actuel.

Étant donné un certain intervalle de temps, si le clavier est tapé dans cet intervalle de temps, la fonction d'exécution sera quittée et la fonction de jeu sera renvoyée pour lire les touches. S'il n'est pas tapé, jugez d'abord si le serpent atteint la position déplacée pour marquer ou si le jeu est terminé, puis déplacez la position du serpent.

Si le clavier n'a pas été tapé, la fonction while de la fonction run sera toujours exécutée et le serpent continuera à se déplacer dans une direction jusqu'à la fin du jeu.

//执行按键
void run(int x, int y)
{
	int t = 0;
	while (1)
	{
		if (t == 0)
			t = 3000; //这里t越小,蛇移动速度越快(可以根据次设置游戏难度)
		while (--t)
		{
			if (kbhit() != 0) //若键盘被敲击,则退出循环
				break;
		}
		if (t == 0) //键盘未被敲击
		{
			JudgeFunc(x, y); //判断到达该位置后,是否得分与游戏结束
			MoveSnake(x, y); //移动蛇
		}
		else //键盘被敲击
		{
			break; //返回Game函数读取键值
		}
	}
}

12. Pointage du jugement et fin

Note de jugement :

Des points sont attribués si l'endroit où la tête de serpent est sur le point d'atteindre est de la nourriture. Après avoir marqué, le corps du serpent doit être allongé et le score actuel doit être mis à jour. De plus, la nourriture doit être régénérée.

Le jugement est passé :

Si la position que la tête de serpent est sur le point d'atteindre est un mur ou un corps de serpent, le jeu se termine. Une fois le jeu terminé, comparez le score de ce jeu avec le score le plus élevé de l'histoire, donnez la phrase d'invite correspondante et demandez au joueur s'il souhaite jouer à un autre jeu, et vous pouvez jouer librement.

//判断得分与结束
void JudgeFunc(int x, int y)
{
	//若蛇头即将到达的位置是食物,则得分
	if (face[snake.y + y][snake.x + x] == FOOD)
	{
		snake.len++; //蛇身加长
		grade += 10; //更新当前得分
		color(7); //颜色设置为白色
		CursorJump(0, ROW);
		printf("当前得分:%d", grade); //重新打印当前得分
		RandFood(); //重新随机生成食物
	}
	//若蛇头即将到达的位置是墙或者蛇身,则游戏结束
	else if (face[snake.y + y][snake.x + x] == WALL || face[snake.y + y][snake.x + x] == BODY)
	{
		Sleep(1000); //留给玩家反应时间
		system("cls"); //清空屏幕
		color(7); //颜色设置为白色
		CursorJump(2 * (COL / 3), ROW / 2 - 3);
		if (grade > max)
		{
			printf("恭喜你打破最高记录,最高记录更新为%d", grade);
			WriteGrade();
		}
		else if (grade == max)
		{
			printf("与最高记录持平,加油再创佳绩", grade);
		}
		else
		{
			printf("请继续加油,当前与最高记录相差%d", max - grade);
		}
		CursorJump(2 * (COL / 3), ROW / 2);
		printf("GAME OVER");
		while (1) //询问玩家是否再来一局
		{
			char ch;
			CursorJump(2 * (COL / 3), ROW / 2 + 3);
			printf("再来一局?(y/n):");
			scanf("%c", &ch);
			if (ch == 'y' || ch == 'Y')
			{
				system("cls");
				main();
			}
			else if (ch == 'n' || ch == 'N')
			{
				CursorJump(2 * (COL / 3), ROW / 2 + 5);
				exit(0);
			}
			else
			{
				CursorJump(2 * (COL / 3), ROW / 2 + 5);
				printf("选择错误,请再次选择");
			}
		}
	}
}

Remarque : Si le score de ce jeu est supérieur au score le plus élevé de l'historique, vous devez mettre à jour le score le plus élevé dans le fichier.

13. Lisez le score le plus élevé du fichier

Tout d'abord, vous devez utiliser la fonction fopen pour ouvrir le fichier "Enregistrement du score le plus élevé de Snake.txt". Si vous exécutez le code pour la première fois, le fichier sera créé automatiquement et l'enregistrement historique le plus élevé sera défini sur 0, puis lire l'historique dans le fichier. L'enregistrement le plus élevé est stocké dans la variable max et le fichier peut être fermé.

 

14. Mettre à jour le score le plus élevé du fichier

Tout d'abord, utilisez la fonction fopen pour ouvrir le fichier "enregistrement du score le plus élevé de Snake.txt", puis écrivez le score du jeu dans le fichier (écraser).

15. Fonction principale

Avec le support des fonctions ci-dessus, écrire la fonction principale est assez simple, mais vous devez faire attention aux trois points suivants :

La note variable globale doit être initialisée à 0 dans la fonction principale et ne peut pas être initialisée à 0 globalement, car nous devons réinitialiser la note actuelle à 0 lorsque le joueur appuie sur la touche R pour rejouer.

Il est recommandé de définir le point de départ de la génération de nombres aléatoires dans la fonction principale.

L'instruction #pragma dans la fonction main est utilisée pour éliminer les avertissements tels que :

 

Six, à travers la version murale du code de langage Snake C

1. Affichage de l'effet de jeu

Personnellement, je trouve que la version murale du serpent est plus fun que la version normale, qu'en pensez-vous ?

2. Code de jeu en langage Snake C

Le code suivant peut être exécuté directement, bienvenue pour essayer :

#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>

#define ROW 23 //界面行数
#define COL 42 //界面列数

#define KONG 0 //标记空(什么也没有)
#define FOOD 1 //标记食物
#define HEAD 2 //标记蛇头
#define BODY 3 //标记蛇身

#define UP 72 //方向键:上
#define DOWN 80 //方向键:下
#define LEFT 75 //方向键:左
#define RIGHT 77 //方向键:右
#define SPACE 32 //暂停
#define ESC 27 //退出

//蛇头
struct Snake
{
	int len; //记录蛇身长度
	int x; //蛇头横坐标
	int y; //蛇头纵坐标
}snake;

//蛇身
struct Body
{
	int x; //蛇身横坐标
	int y; //蛇身纵坐标
}body[ROW*COL]; //开辟足以存储蛇身的结构体数组

int face[ROW][COL]; //标记界面当中各个位置的信息

//隐藏光标
void HideCursor();
//光标跳转
void CursorJump(int x, int y);
//初始化界面
void InitInterface();
//颜色设置
void color(int c);
//从文件读取最高分
void ReadGrade();
//更新最高分到文件
void WriteGrade();
//初始化蛇
void InitSnake();
//随机生成食物
void RandFood();
//判断得分与结束
void JudgeFunc(int x, int y);
//打印蛇/覆盖蛇
void DrawSnake(int flag);
//移动蛇
void MoveSnake(int x, int y);
//执行按键
void run(int x, int y);
//游戏主体逻辑函数
void Game();

int max, grade; //全局变量
int main()
{
#pragma warning (disable:4996) //消除警告
	max = 0, grade = 0; //初始化变量
	system("title 贪吃蛇"); //设置cmd窗口名称
	system("mode con cols=84 lines=23"); //设置cmd窗口大小
	HideCursor(); //隐藏光标
	ReadGrade(); //从文件读取最高分
	InitInterface(); //初始化界面
	InitSnake(); //初始化蛇
	srand((unsigned int)time(NULL)); //设置随机数生成起点
	RandFood(); //随机生成食物
	DrawSnake(1); //打印蛇
	Game(); //开始游戏
	return 0;
}

//隐藏光标
void HideCursor()
{
	CONSOLE_CURSOR_INFO curInfo; //定义光标信息的结构体变量
	curInfo.dwSize = 1; //如果没赋值的话,光标隐藏无效
	curInfo.bVisible = FALSE; //将光标设置为不可见
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
	SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
}
//光标跳转
void CursorJump(int x, int y)
{
	COORD pos; //定义光标位置的结构体变量
	pos.X = x; //横坐标
	pos.Y = y; //纵坐标
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
	SetConsoleCursorPosition(handle, pos); //设置光标位置
}
//初始化界面
void InitInterface()
{
	color(7); //颜色设置为白色
	CursorJump(0, 0);
	printf("当前得分:%d", grade);
	CursorJump(COL, 0);
	printf("历史最高得分:%d", max);
	color(11); //颜色设置为浅蓝色
	for (int i = 1; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if (i == 1 && j != 0 && j != COL - 1) //打印游戏区的上界
			{
				CursorJump(2 * j, i);
				printf("__");
			}
			else if (i == ROW - 1 && j != 0 && j != COL - 1) //打印游戏区的下界
			{
				CursorJump(2 * j, i);
				printf("▔▔");
			}
			else if (j == 0 && i != 1 && i != 0 && i != ROW - 1) //打印游戏区的左界
			{
				CursorJump(2 * j, i);
				printf(" >");
			}
			else if (j == COL - 1 && i != 1 && i != 0 && i != ROW - 1) //打印游戏区的右界
			{
				CursorJump(2 * j, i);
				printf("< ");
			}
			else
			{
				face[i][j] = KONG; //其余位置标记为空(非常必要)
			}
		}
	}
}
//颜色设置
void color(int c)
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
}
//从文件读取最高分
void ReadGrade()
{
	FILE* pf = fopen("贪吃蛇最高得分记录.txt", "r"); //以只读的方式打开文件
	if (pf == NULL) //打开文件失败
	{
		pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
		fwrite(&max, sizeof(int), 1, pf); //将max写入文件(此时max为0),即将最高得分初始化为0
	}
	fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头
	fread(&max, sizeof(int), 1, pf); //读取文件当中的最高得分到max当中
	fclose(pf); //关闭文件
	pf = NULL; //文件指针及时置空
}
//更新最高分到文件
void WriteGrade()
{
	FILE* pf = fopen("贪吃蛇最高得分记录.txt", "w"); //以只写的方式打开文件
	if (pf == NULL) //打开文件失败
	{
		printf("保存最高得分记录失败\n");
		exit(0);
	}
	fwrite(&grade, sizeof(int), 1, pf); //将本局游戏得分写入文件当中
	fclose(pf); //关闭文件
	pf = NULL; //文件指针及时置空
}
//初始化蛇
void InitSnake()
{
	snake.len = 2; //蛇身长度初始化为2
	snake.x = COL / 2; //蛇头位置的横坐标
	snake.y = ROW / 2; //蛇头位置的纵坐标
	//蛇身坐标的初始化
	body[0].x = COL / 2 - 1;
	body[0].y = ROW / 2;
	body[1].x = COL / 2 - 2;
	body[1].y = ROW / 2;
	//将蛇头和蛇身位置进行标记
	face[snake.y][snake.x] = HEAD;
	face[body[0].y][body[0].x] = BODY;
	face[body[1].y][body[1].x] = BODY;
}
//随机生成食物
void RandFood()
{
	int i, j;
	do
	{
		//随机生成食物的横纵坐标
		i = rand() % ROW;
		j = rand() % COL;
		//若食物生成位置不在游戏区,或者生成食物的位置不为空,则重新生成
	} while (i <= 1 || i == ROW - 1 || j == 0 || j == COL - 1 || face[i][j] != KONG); 
	face[i][j] = FOOD; //将食物位置进行标记
	color(9); //颜色设置为深蓝色
	CursorJump(2 * j, i);
	printf("●");
}
//判断得分与结束
void JudgeFunc(int x, int y)
{
	int nextX = snake.x + x;
	int nextY = snake.y + y;

	if (nextX == COL - 1)
		nextX = 1;
	if (nextX == 0)
		nextX = COL - 2;

	if (nextY == ROW - 1)
		nextY = 2;
	if (nextY == 1)
		nextY = ROW - 2;
	//若即将到达的位置是食物,则得分
	if (face[nextY][nextX] == FOOD)
	{
		snake.len++; //蛇身加长
		grade += 10; //更新当前得分
		color(7); //颜色设置为白色
		CursorJump(0, 0);
		printf("当前得分:%d", grade); //重新打印当前得分
		RandFood(); //重新随机生成食物
	}
	//若即将到达的位置是蛇身,则游戏结束
	else if (face[nextY][nextX] == BODY)
	{
		Sleep(1000); //留给玩家反应时间
		system("cls"); //清空屏幕
		color(7); //颜色设置为白色
		CursorJump(2 * (COL / 3), ROW / 2 - 3);
		if (grade > max)
		{
			printf("恭喜你打破最高记录,最高记录更新为%d", grade);
			WriteGrade();
		}
		else if (grade == max)
		{
			printf("与最高记录持平,加油再创佳绩", grade);
		}
		else
		{
			printf("请继续加油,当前与最高记录相差%d", max - grade);
		}
		CursorJump(2 * (COL / 3), ROW / 2);
		printf("GAME OVER");
		while (1) //询问玩家是否再来一局
		{
			char ch;
			CursorJump(2 * (COL / 3), ROW / 2 + 3);
			printf("再来一局?(y/n):");
			scanf("%c", &ch);
			if (ch == 'y' || ch == 'Y')
			{
				system("cls");
				main();
			}
			else if (ch == 'n' || ch == 'N')
			{
				CursorJump(2 * (COL / 3), ROW / 2 + 5);
				exit(0);
			}
			else
			{
				CursorJump(2 * (COL / 3), ROW / 2 + 4);
				printf("选择错误,请再次选择");
			}
		}
	}
}
//打印蛇与覆盖蛇
void DrawSnake(int flag)
{
	if (flag == 1) //打印蛇
	{
		color(10); //颜色设置为绿色
		CursorJump(2 * snake.x, snake.y);
		printf("■"); //打印蛇头
		//打印蛇身
		for (int i = 0; i < snake.len; i++)
		{
			CursorJump(2 * body[i].x, body[i].y);
			printf("■");
		}
	}
	else //覆盖蛇
	{
		if (body[snake.len - 1].x != 0) //防止len++后(0, 0)位置所显示的信息被覆盖
		{
			//将蛇尾覆盖为空格即可
			CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
			printf("  ");
		}
	}
}
//移动蛇
void MoveSnake(int x, int y)
{
	DrawSnake(0); //先覆盖当前所显示的蛇
	face[body[snake.len - 1].y][body[snake.len - 1].x] = KONG; //蛇移动后蛇尾重新标记为空
	face[snake.y][snake.x] = BODY; //蛇移动后蛇头的位置变为蛇身
	//蛇移动后各个蛇身位置坐标需要更新
	for (int i = snake.len - 1; i > 0; i--)
	{
		body[i].x = body[i - 1].x;
		body[i].y = body[i - 1].y;
	}
	//蛇移动后蛇头位置信息变为第0个蛇身的位置信息
	body[0].x = snake.x;
	body[0].y = snake.y;
	//蛇头的位置更改
	snake.x = snake.x + x;
	if (snake.x == COL - 1) //越过右界
		snake.x = 1;
	else if (snake.x == 0) //越过左界
		snake.x = COL - 2;
	snake.y = snake.y + y;
	if (snake.y == ROW - 1) //越过下界
		snake.y = 2;
	else if (snake.y == 1) //越过上界
		snake.y = ROW - 2;
	face[snake.y][snake.x] = HEAD; //对蛇头位置进行标记
	DrawSnake(1); //打印移动后的蛇
}
//执行按键
void run(int x, int y)
{
	int t = 0;
	while (1)
	{
		if (t == 0)
			t = 3000; //这里t越小,蛇移动速度越快(可以根据次设置游戏难度)
		while (--t)
		{
			if (kbhit() != 0) //若键盘被敲击,则退出循环
				break;
		}
		if (t == 0) //键盘未被敲击
		{
			JudgeFunc(x, y); //判断到达该位置后,是否得分与游戏结束
			MoveSnake(x, y); //移动蛇
		}
		else //键盘被敲击
		{
			break; //返回Game函数读取键值
		}
	}
}
//游戏主体逻辑函数
void Game()
{
	int n = RIGHT; //开始游戏时,默认向后移动
	int tmp = 0; //记录蛇的移动方向
	goto first; //第一次进入循环先向默认方向前进
	while (1)
	{
		n = getch(); //读取键值
		//在执行前,需要对所读取的按键进行调整
		switch (n)
		{
		case UP:
		case DOWN: //如果敲击的是“上”或“下”
			if (tmp != LEFT&&tmp != RIGHT) //并且上一次蛇的移动方向不是“左”或“右”
			{
				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
			}
			break;
		case LEFT:
		case RIGHT: //如果敲击的是“左”或“右”
			if (tmp != UP&&tmp != DOWN) //并且上一次蛇的移动方向不是“上”或“下”
			{
				n = tmp; //那么下一次蛇的移动方向设置为上一次蛇的移动方向
			}
		case SPACE:
		case ESC:
		case 'r':
		case 'R':
			break; //这四个无需调整
		default:
			n = tmp; //其他键无效,默认为上一次蛇移动的方向
			break;
		}
	first: //第一次进入循环先向默认方向前进
		switch (n)
		{
		case UP: //方向键:上
			if (snake.y - 1 != body[0].y) //改变的方向不能是第0个蛇身的方向
			{
				run(0, -1); //向上移动(横坐标偏移为0,纵坐标偏移为-1)
				tmp = UP; //记录当前蛇的移动方向
			}
			break;
		case DOWN: //方向键:下
			if (snake.y + 1 != body[0].y) //改变的方向不能是第0个蛇身的方向
			{
				run(0, 1); //向下移动(横坐标偏移为0,纵坐标偏移为1)
				tmp = DOWN; //记录当前蛇的移动方向
			}
			break;
		case LEFT: //方向键:左
			if (snake.x - 1 != body[0].x) //改变的方向不能是第0个蛇身的方向
			{
				run(-1, 0); //向左移动(横坐标偏移为-1,纵坐标偏移为0)
				tmp = LEFT; //记录当前蛇的移动方向
			}
			break;
		case RIGHT: //方向键:右
			if (snake.x + 1 != body[0].x) //改变的方向不能是第0个蛇身的方向
			{
				run(1, 0); //向右移动(横坐标偏移为1,纵坐标偏移为0)
				tmp = RIGHT; //记录当前蛇的移动方向
			}
			break;
		case SPACE: //暂停
			system("pause>nul"); //暂停后按任意键继续
			break;
		case ESC: //退出
			system("cls"); //清空屏幕
			color(7); //颜色设置为白色
			CursorJump(COL - 8, ROW / 2);
			printf("  游戏结束  ");
			CursorJump(COL - 8, ROW / 2 + 2);
			exit(0);
		case 'r':
		case 'R': //重新开始
			system("cls"); //清空屏幕
			main(); //重新执行主函数
		}
	}
}

3. Explication détaillée du code du jeu

Les similitudes et les différences mineures avec les précédents ne seront pas développées ici, et ceux qui sont intéressés le comprendront en les regardant.

Sept, un grand nombre d'acquisition de code source de jeu de projet en langage C

Vous pouvez suivre le compte public WeChat : "C et C plus" Réponse : "TC"

Je suppose que tu aimes

Origine blog.csdn.net/weixin_55305220/article/details/123222868
conseillé
Classement