Serpiente codiciosa del lenguaje C, serpiente codiciosa Realización del código del lenguaje C Daquan

1. Prefacio a la implementación del código serpiente en lenguaje C

El propósito principal de diseñar Snake Game es permitir que todos consoliden la base del lenguaje C, entrenen el pensamiento de programación, cultiven ideas para resolver problemas y aprecien el colorido lenguaje C.

Snake es un juego muy clásico. Esta vez simulamos la implementación del juego Snake en la consola, que consiste en realizar las funciones básicas de Snake. Por ejemplo, en el mapa, use "↑↓←→" para controlar el movimiento del Serpiente dirección, después de comer la comida, el cuerpo de la serpiente crecerá y así sucesivamente. . . .

En primer lugar, tenemos que analizar algunas situaciones que nos encontraremos en el juego.

①La parte de la serpiente, el cuerpo de la serpiente es sección por sección, la estructura de datos más fácil de asociar en este momento es la lista de secuencias, la lista enlazada, si compara la serpiente con la lista de secuencias o la lista enlazada, cuando comer alimentos más tarde, el cuerpo definitivamente será más largo, lo que implica la operación de inserción, por lo que para una mayor eficiencia, usamos una lista vinculada para implementar nuestra parte de serpiente Inicialmente, imprimimos el cuerpo de serpiente en la pantalla de acuerdo con cuatro nodos.

②El movimiento de la serpiente. El movimiento de la serpiente en la pantalla parece que todo el cuerpo se mueve hacia adelante una unidad, pero el principio es que reimprimimos la serpiente en otro lugar de la pantalla y eliminamos el cuerpo de serpiente anterior.

③Para la generación de comida se genera aleatoriamente un nodo en el mapa, cuando se repiten las coordenadas de la cabeza de la serpiente y las coordenadas de la comida, la comida desaparece y se alarga el cuerpo de la serpiente, es decir, el número de nodos de la serpiente aumenta en uno.

④ Varios estados de la serpiente en él, estado normal: las coordenadas del nodo de cabeza de serpiente no coinciden con las coordenadas de la pared y las coordenadas de su propio cuerpo,

Asesinado por ti mismo: las coordenadas de la cabeza de serpiente coinciden con las coordenadas del cuerpo de serpiente y golpean la pared: las coordenadas de la cabeza de serpiente coinciden con las coordenadas de la pared. A través de las paredes: las serpientes no chocarán contra las paredes y morirán.

En segundo lugar, el código de serpiente en lenguaje C para realizar la descripción del juego.

1. Se puede obtener una gran cantidad de códigos fuente de juegos de proyectos en lenguaje C siguiendo la cuenta pública de WeChat: "C and C plus plus" Respuesta: "TC" es suficiente

2. No hay descripciones de botones relacionadas impresas en la interfaz del juego Snake en lenguaje C. Aquí los enumeramos uno por uno . Las descripciones de los botones del juego Snake en lenguaje C :

  • Presione las teclas de flecha arriba, abajo, izquierda y derecha para cambiar la dirección de movimiento de la serpiente.

  • Mantenga presionada una de las teclas de flecha arriba, abajo, izquierda y derecha durante un breve período de tiempo para acelerar el movimiento de la serpiente en esa dirección durante un breve período de tiempo.

  • Presione la barra espaciadora para hacer una pausa y luego presione cualquier tecla para reanudar el juego.

  • Presiona la tecla Esc para salir directamente del juego.

  • Presiona la tecla R para reiniciar el juego.

Además, el juego también cuenta con un sistema de puntuación, que puede salvar el récord más alto de la historia del jugador.

Tres, pantalla de efecto de juego de código de serpiente en lenguaje C

La velocidad de movimiento de la serpiente en el juego Snake se puede ajustar. En la animación, la velocidad se ajusta más lentamente (la velocidad es demasiado rápida y el cuerpo de la serpiente no se muestra completamente en la animación). El código que se proporciona a continuación ajusta la velocidad de la serpiente a una ubicación adecuada, puede intentar

En cuarto lugar, el código del juego de serpientes en lenguaje C es el siguiente

Puede copiar el siguiente código en su propio compilador para ejecutarlo:

#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(); //重新执行主函数
		}
	}
}

Cinco, código de juego de serpiente en lenguaje C explicación detallada

1. El lenguaje Snake C implementa la construcción del marco del juego

Primero defina el tamaño de la interfaz del juego, defina el número de filas y columnas en el área del juego.

 

Aquí, el área donde la serpiente está activa se denomina área de juego, y el área donde se indica la puntuación se denomina área de pistas (el área de pistas ocupa una línea).

 

Además, necesitamos dos estructuras para representar la cabeza de serpiente y el cuerpo de serpiente. La estructura de cabeza de serpiente almacena la longitud del cuerpo de serpiente actual y las coordenadas de posición de la cabeza de serpiente.

 

Las coordenadas de posición del cuerpo de la serpiente se almacenan en la estructura del cuerpo de la serpiente.

 

Al mismo tiempo, necesitamos una matriz bidimensional para marcar el estado de cada posición en el área de juego (vacío, pared, comida, cabeza de serpiente y cuerpo de serpiente).

 

Para aumentar la legibilidad del código, es mejor usar macros para definir el estado de cada posición, en lugar de cambiar el estado de cada posición con números secos en el código.

 

Por supuesto, para la legibilidad del código, será mejor que definamos los valores clave de las claves que necesitamos usar con las macros.

2. Ocultar el cursor

Ocultar el cursor es relativamente simple. Defina una variable de estructura de la información del cursor, luego asigne la información del cursor y finalmente use la variable de estructura de la información del cursor para configurar la información del cursor.

3. Salto de cursor

Salto del cursor, es decir, dejar que el cursor salte a la posición especificada para la salida. De manera similar a los pasos de operación para ocultar el cursor, primero defina una variable de estructura de la posición del cursor, luego configure las coordenadas horizontales y verticales del cursor y finalmente use la variable de estructura de la posición del cursor para configurar la posición del cursor.

4. Interfaz de inicialización

La interfaz de inicialización completa la impresión del "muro" del área de juego y la impresión del área de solicitud.

Hay dos puntos a los que hay que prestar atención durante el proceso de impresión:

En la ventana cmd, un pequeño cuadrado ocupa dos unidades de abscisa y una unidad de ordenada.

La función de salto del cursor CursorJump recibe las coordenadas horizontales y verticales de la posición donde saltará el cursor.

Por ejemplo, usar la función CursorJump para saltar a la fila i ya la columna j (con un cuadrado pequeño como unidad) es equivalente a saltar el cursor a la coordenada (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);
}

Nota:  Al inicializar la interfaz, recuerda marcar el estado de la posición correspondiente en el área de juego.

5. Configuración de color del juego Snake en lenguaje C

La función de la función de configuración de color es cambiar el color del contenido de salida al color especificado. El parámetro recibido c es el código de color. La tabla de código de color decimal es la siguiente:

 

La declaración de la función set color en su archivo de encabezado es la siguiente:

 

6. Inicializar la serpiente

Al inicializar la serpiente, la longitud del cuerpo de la serpiente se inicializa en 2, la posición inicial de la cabeza de la serpiente está en el centro del área de juego, y la cabeza de la serpiente es el cuerpo de la serpiente 0 y el primer cuerpo de la serpiente a la derecha.

 Después de inicializar la información de la serpiente, recuerde marcar el estado de la ubicación en el área de juego.

7. Genera comida al azar

Para generar comida aleatoriamente en el área de juego, es necesario juzgar las coordenadas generadas. Solo cuando la ubicación está vacía, se puede generar comida aquí, de lo contrario, las coordenadas deben regenerarse. Después de determinar las coordenadas de los alimentos, se debe marcar el estado de la ubicación en el área de juego.

8. Imprimir Serpiente vs Cubrir Serpiente

La impresión de serpientes y la cobertura de serpientes se implementan directamente mediante una función. Si el indicador de parámetro entrante es 1, la serpiente se imprimirá; si el parámetro entrante es 0, la serpiente se sobrescribirá con espacios.

serpiente de impresión:

Primero obtenga las coordenadas de la cabeza de serpiente de acuerdo con la variable de estructura serpiente e imprima la cabeza de serpiente en la posición correspondiente.

Luego obtenga las coordenadas del cuerpo de la serpiente a su vez de acuerdo con el cuerpo de la matriz de estructura e imprima en la posición correspondiente.

Cubrir la serpiente:

Solo cubre el último cuerpo de serpiente con un espacio.

Sin embargo, es necesario prestar atención para determinar si la posición cubierta es la posición (0,0) antes de cubrir, porque cuando la longitud del cuerpo de la serpiente aumenta después de la puntuación, es necesario cubrir la serpiente actual (y luego imprimir la serpiente con la longitud aumentada), y en este momento, se agrega el nuevo cuerpo de serpiente. Aún no ha sido asignado (el compilador generalmente se inicializa a 0 por defecto), las coordenadas que obtuvimos de acuerdo con el último cuerpo de serpiente son ( 0,0), y la pared en la posición (0,0) se cubrirá con un espacio. Después de la implementación de la siguiente función de serpiente móvil, lo entenderemos. (También puede eliminar este juicio primero, observar el cambio de la pared en la posición (0,0) después de que la serpiente come la comida y luego analizarlo)

//打印蛇与覆盖蛇
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. Serpiente en movimiento

La función de la función de serpiente en movimiento es cubrir primero la serpiente que se muestra actualmente y luego imprimir la serpiente movida.

Descripción de parámetros:

x: El cambio de la abscisa después de que la serpiente se mueva en relación con la abscisa de la serpiente actual.

y: El cambio de la ordenada después de que la serpiente se mueva en relación con la ordenada de la serpiente actual.

Después de que la serpiente se mueve, es necesario cambiar cierta información:

El último cuerpo de serpiente en el área de juego debe volver a marcarse como vacío.

La posición de la cabeza de la serpiente debe volver a marcarse como el cuerpo de la serpiente en el área de juego.

En el cuerpo de matriz de estructura que almacena la información de coordenadas del cuerpo de serpiente, la información de coordenadas del cuerpo de serpiente del i-ésimo segmento debe actualizarse a la información de coordenadas del cuerpo de serpiente del segmento i-1 y el 0. segmento, es decir, la información de coordenadas del primer segmento del cuerpo de la serpiente debe actualizarse.Actualice la información de coordenadas de la cabeza de serpiente actual.

La información de coordenadas de la cabeza de serpiente debe volver a calcularse de acuerdo con los parámetros entrantes x e y.

10. Función lógica principal del juego.

Lógica del cuerpo principal:

Primero ingrese la función por primera vez, la serpiente predeterminada se mueve hacia la derecha y luego ejecuta la función de ejecución.

Hasta que se toque el teclado, regrese de la función de ejecución a la función de juego para lectura de teclas.

Después de leer el valor de la clave, debe ajustar la clave de lectura (esto es necesario).

Después del ajuste, realice la ejecución de la tecla y luego la lectura de la tecla, y así sucesivamente.

Mecanismo de ajuste del botón:

Si presiona la tecla "Arriba" o "Abajo" y la última vez que la dirección de movimiento de la serpiente no fue "Izquierda" o "Derecha", establezca la dirección de movimiento de la siguiente serpiente en la dirección de movimiento de la última serpiente, es decir, la dirección de movimiento constante.

Si se toca la tecla "izquierda" o "derecha" y la dirección de movimiento de la última serpiente no es "arriba" o "abajo", entonces la dirección de movimiento de la próxima serpiente se establece en la dirección de movimiento de la última serpiente, es decir, la constante de dirección del movimiento.

Si la tecla presionada es Espacio, Esc, r o R, no se realiza ningún ajuste.

El resto de las teclas no son válidas y la dirección de movimiento de la siguiente serpiente se establece en la dirección de movimiento de la última serpiente, es decir, la dirección de movimiento permanece sin cambios.

//游戏主体逻辑函数
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. Botón Ejecutar

Descripción de parámetros:

x: El cambio de la abscisa después de que la serpiente se mueva en relación con la abscisa de la serpiente actual.

y: El cambio de la ordenada después de que la serpiente se mueva en relación con la ordenada de la serpiente actual.

Dado un cierto intervalo de tiempo, si se toca el teclado dentro de este intervalo de tiempo, se saldrá de la función de ejecución y se volverá a la función de Juego para leer las teclas. Si no está tocado, primero juzgue si la serpiente alcanza la posición movida para anotar o si el juego ha terminado, y luego mueva la posición de la serpiente.

Si no se ha tocado el teclado, la función mientras en la función de ejecución siempre se ejecutará y la serpiente seguirá moviéndose en una dirección hasta que finalice el juego.

//执行按键
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. Puntuación y final del juicio

Puntuación del juicio:

Se otorgan puntos si el lugar al que la cabeza de serpiente está a punto de llegar es comida. Después de la puntuación, el cuerpo de la serpiente debe alargarse y la puntuación actual debe actualizarse. Además, la comida debe regenerarse.

El juicio ha terminado:

Si la posición a la que está a punto de llegar la cabeza de serpiente es una pared o un cuerpo de serpiente, el juego termina. Después de que termine el juego, compare el puntaje de este juego con el puntaje más alto en la historia, dé la oración correspondiente y pregúntele al jugador si quiere jugar otro juego, y puede jugar libremente.

//判断得分与结束
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("选择错误,请再次选择");
			}
		}
	}
}

Nota: si el puntaje de este juego es mayor que el puntaje más alto en la historia, debe actualizar el puntaje más alto en el archivo.

13. Lea la puntuación más alta del archivo

Primero, debe usar la función fopen para abrir el archivo "Registro de puntaje más alto de Snake.txt". Si ejecuta el código por primera vez, el archivo se creará automáticamente y el registro histórico más alto se establecerá en 0, y luego lea el historial en el archivo.El registro más alto se almacena en la variable max, y el archivo se puede cerrar.

 

14. Actualizar la puntuación más alta al archivo

Primero, use la función fopen para abrir el "registro de puntaje más alto de Snake.txt" y luego escriba el puntaje del juego en el archivo (sobrescribir).

15 Función principal

Con el apoyo de las funciones anteriores, escribir la función principal es bastante simple, pero debe prestar atención a los siguientes tres puntos:

La calificación de la variable global debe inicializarse a 0 dentro de la función principal y no puede inicializarse a 0 globalmente, porque necesitamos restablecer la calificación actual a 0 cuando el jugador presiona la tecla R para reproducir.

Se recomienda establecer el punto de inicio de la generación de números aleatorios en la función principal.

La declaración #pragma en la función principal se usa para eliminar advertencias como:

 

Seis, a través de la versión de pared del código de lenguaje C de serpiente

1. Pantalla de efectos de juego

Personalmente, creo que la versión de la serpiente montada en la pared es más divertida que la versión normal, ¿qué os parece?

2. Código del juego de lenguaje Snake C

El siguiente código se puede ejecutar directamente, bienvenido a probar:

#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. Explicación detallada del código del juego.

Las similitudes y pequeñas diferencias con los anteriores no se desarrollarán aquí, y aquellos que estén interesados ​​​​lo entenderán al mirarlos.

Siete, una gran cantidad de adquisición de código fuente del juego del proyecto de lenguaje C

Puede seguir la cuenta pública de WeChat: "C y C plus" Responder: "TC"

Supongo que te gusta

Origin blog.csdn.net/weixin_55305220/article/details/123222868
Recomendado
Clasificación