Cobra gananciosa da linguagem C, cobra gananciosa Realização do código da linguagem C Daquan

1. Prefácio à implementação do código snake em linguagem C

O objetivo principal de projetar o Snake Game é permitir que todos consolidem a base da linguagem C, treinem o pensamento de programação, cultivem ideias de resolução de problemas e apreciem a colorida linguagem C.

Snake é um jogo muito clássico. Desta vez simulamos a implementação do jogo Snake no console, que é realizar as funções básicas do Snake. Por exemplo, no mapa, use "↑↓←→" para controlar o movimento do cobra. direção, depois de comer a comida, o corpo da cobra crescerá e assim por diante. . . .

Antes de tudo, temos que analisar algumas situações que encontraremos no jogo.

①A parte da cobra, o corpo da cobra é seção por seção, a estrutura de dados mais facilmente associada neste momento é a lista de sequências, a lista vinculada, se você comparar a cobra com a lista de sequências ou a lista vinculada, quando você comer comida depois, o corpo definitivamente vai ficar mais longo, o que envolve a operação de inserção, então para maior eficiência, usamos uma lista encadeada para implementar nossa parte da cobra. Inicialmente, imprimimos o corpo da cobra na tela de acordo com quatro nós.

②O movimento da cobra O movimento da cobra na tela parece que todo o corpo é movido para frente por uma unidade, mas o princípio é que reimprimimos a cobra em outro lugar na tela e removemos o corpo da cobra anterior.

③Para a geração de alimentos, um nó é gerado aleatoriamente no mapa. Quando as coordenadas da cabeça da cobra e as coordenadas do alimento são repetidas, o alimento desaparece e o corpo da cobra é alongado, ou seja, o número de nós da cobra aumenta em um.

④ Vários estados da cobra nele, estado normal: as coordenadas do nó da cabeça da cobra não coincidem com as coordenadas da parede e as coordenadas de seu próprio corpo,

Morto por si mesmo: as coordenadas da cabeça da cobra coincidem com as coordenadas do corpo da cobra e batem na parede: as coordenadas da cabeça da cobra coincidem com as coordenadas da parede. Através das Paredes: As cobras não colidirão com as paredes e morrerão.

Em segundo lugar, o código snake da linguagem C para realizar a descrição do jogo

1. Um grande número de códigos-fonte de jogos de projeto em linguagem C pode ser obtido seguindo a conta pública do WeChat: "C e C plus plus" Resposta: "TC" é suficiente

2. Não há descrições de botões relacionadas impressas na interface do jogo Snake em linguagem C. Aqui as listamos uma a uma . As descrições de botões do jogo Snake em linguagem C :

  • Pressione as setas do teclado para cima, para baixo, para a esquerda e para a direita para mudar a direção de movimento da cobra.

  • Pressione e segure uma das teclas de seta para cima, para baixo, para a esquerda e para a direita por um curto período de tempo para acelerar o movimento da cobra naquela direção por um curto período de tempo.

  • Pressione a barra de espaço para pausar e, em seguida, pressione qualquer tecla para retomar o jogo.

  • Pressione a tecla Esc para sair do jogo diretamente.

  • Pressione a tecla R para reiniciar o jogo.

Além disso, o jogo também conta com um sistema de pontuação, que pode salvar o maior recorde do jogador na história.

Três, exibição de efeito de jogo de código de cobra de linguagem C

A velocidade de movimento da cobra no jogo Snake pode ser ajustada. Na animação, a velocidade é ajustada mais lentamente (a velocidade é muito rápida e o corpo da cobra não é totalmente exibido na animação). O código abaixo ajusta a velocidade de a cobra a uma velocidade adequada. local, você pode tentar

Quarto, o código do jogo snake da linguagem C é o seguinte

Você pode copiar o seguinte código em seu próprio compilador para executar:

#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, explicação detalhada do código do jogo snake em linguagem C

1. A linguagem Snake C implementa a construção do framework do jogo

Primeiro defina o tamanho da interface do jogo, defina o número de linhas e colunas na área do jogo.

 

Aqui, a área onde a cobra está ativa é chamada de área de jogo, e a área onde a pontuação é indicada é chamada de área de dica (a área de dica ocupa uma linha).

 

Além disso, precisamos de duas estruturas para representar a cabeça da cobra e o corpo da cobra. A estrutura da cabeça da cobra armazena o comprimento do corpo da cobra atual e as coordenadas de posição da cabeça da cobra.

 

As coordenadas de posição do corpo da cobra são armazenadas na estrutura do corpo da cobra.

 

Ao mesmo tempo, precisamos de um array bidimensional para marcar o status de cada posição na área de jogo (vazio, parede, comida, cabeça de cobra e corpo de cobra).

 

Para aumentar a legibilidade do código, é melhor usar macros para definir o estado de cada posição, em vez de alternar o estado de cada posição com números secos no código.

 

Claro que, para a legibilidade do código, é melhor definirmos os valores de chave das chaves que precisamos usar com macros.

2. Oculte o cursor

Ocultar o cursor é relativamente simples. Defina uma variável de estrutura das informações do cursor, depois atribua as informações do cursor e, finalmente, use a variável de estrutura das informações do cursor para definir as informações do cursor.

3. Salto do cursor

Salto do cursor, ou seja, deixe o cursor pular para a posição especificada para saída. Semelhante às etapas da operação de ocultar o cursor, primeiro defina uma variável de estrutura da posição do cursor, depois defina as coordenadas horizontal e vertical do cursor e, finalmente, use a variável de estrutura da posição do cursor para definir a posição do cursor.

4. Interface de inicialização

A interface de inicialização completa a impressão da "parede" da área de jogo e a impressão da área de prompt.

Há dois pontos a serem observados durante o processo de impressão:

Na janela cmd, um pequeno quadrado ocupa duas unidades de abcissa e uma unidade de ordenada.

A função de salto do cursor CursorJump recebe as coordenadas horizontais e verticais da posição onde o cursor irá saltar.

Por exemplo, usar a função CursorJump para pular para a linha i e a coluna j (com um pequeno quadrado como unidade) é equivalente a pular o cursor para a 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:  Ao inicializar a interface, lembre-se de marcar o estado da posição correspondente na área do jogo.

5. Configuração de cores do jogo Snake em linguagem C

A função da função de configuração de cor é alterar a cor do conteúdo de saída para a cor especificada. O parâmetro recebido c é o código de cor. A tabela de código de cor decimal é a seguinte:

 

A declaração da função set color em seu arquivo de cabeçalho é a seguinte:

 

6. Inicialize a cobra

Ao inicializar a cobra, o comprimento do corpo da cobra é inicializado em 2, a posição inicial da cabeça da cobra está no centro da área de jogo e a cabeça da cobra é o 0º corpo da cobra e o primeiro corpo da cobra à direita.

 Depois de inicializar as informações da cobra, lembre-se de marcar o estado do local na área de jogo.

7. Gere comida aleatoriamente

Para gerar comida aleatoriamente na área do jogo, é necessário julgar as coordenadas geradas, somente quando o local estiver vazio, a comida pode ser gerada aqui, caso contrário as coordenadas precisam ser regeneradas. Depois que as coordenadas dos alimentos são determinadas, o estado do local na área de jogo precisa ser marcado.

8. Imprimir Snake vs Cover Snake

A impressão de snakes e a cobertura de snakes são implementadas diretamente usando uma função.Se o sinalizador de parâmetro de entrada for 1, o snake será impresso, se o parâmetro de entrada for 0, o snake será substituído por espaços.

cobra de impressão:

Primeiro obtenha as coordenadas da cabeça da cobra de acordo com a variável de estrutura snake e imprima a cabeça da cobra na posição correspondente.

Em seguida, obtenha as coordenadas do corpo da cobra de acordo com o corpo da matriz de estrutura e imprima na posição correspondente.

Cubra a cobra:

Apenas cubra o último corpo de cobra com um espaço.

No entanto, é necessário prestar atenção para determinar se a posição coberta é a posição (0,0) antes de cobrir, pois quando o comprimento do corpo da cobra aumenta após a pontuação, é necessário cobrir a cobra atual (e depois imprimir a cobra com o comprimento aumentado), e neste momento, o novo corpo da cobra é adicionado. Ele ainda não foi atribuído (o compilador geralmente é inicializado em 0 por padrão), as coordenadas que obtivemos de acordo com o último corpo da cobra são ( 0,0), e a parede na posição (0,0) será coberta com um espaço.Após a implementação da seguinte função mobile snake, iremos entendê-la. (Você também pode remover esse julgamento primeiro, observar a mudança da parede na posição (0,0) após a cobra comer a comida e depois analisá-la)

//打印蛇与覆盖蛇
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. Serpente em Movimento

A função da função de cobra em movimento é cobrir primeiro a cobra exibida no momento e depois imprimir a cobra movida.

Descrição do parâmetro:

x: A mudança da abcissa depois que a cobra se move em relação à abcissa da cobra atual.

y: A mudança da ordenada após a serpente se mover em relação à ordenada da serpente atual.

Depois que a cobra se move, várias informações precisam mudar:

O último corpo de cobra na área de jogo precisa ser marcado novamente como vazio.

A posição da cabeça da cobra precisa ser remarcada como o corpo da cobra na área de jogo.

No corpo da matriz de estrutura que armazena as informações de coordenadas do corpo da cobra, as informações das coordenadas do corpo da cobra do segmento i-ésimo precisam ser atualizadas para as informações das coordenadas do corpo da cobra do segmento i-1, e o 0º ou seja, as informações de coordenadas do primeiro segmento do corpo da cobra precisam ser atualizadas.Atualize as informações de coordenadas da cabeça da cobra atual.

As informações de coordenadas da cabeça da cobra precisam ser recalculadas de acordo com os parâmetros de entrada x e y.

10. Função lógica principal do jogo

Lógica do corpo principal:

Primeiro, insira a função pela primeira vez, a cobra padrão se move para a direita e, em seguida, executa a função de execução.

Até que o teclado seja tocado, retorne da função de execução para a função de jogo para leitura de teclas.

Depois de ler o valor da chave, você precisa ajustar a chave de leitura (isso é necessário).

Após o ajuste, execute a execução da chave e, em seguida, a leitura da chave e assim por diante.

Mecanismo de ajuste do botão:

Se você pressionar a tecla "Para cima" ou "Para baixo" e a última vez que a direção de movimento da cobra não foi "Esquerda" ou "Direita", defina a direção de movimento da próxima cobra para a direção de movimento da última cobra, ou seja, a direção de movimento constante.

Se a tecla "esquerda" ou "direita" for pressionada e a direção de movimento da última cobra não for "para cima" ou "para baixo", a direção de movimento da próxima cobra será definida para a direção de movimento da última cobra, isto é, a constante de direção do movimento.

Se a tecla pressionada for Espaço, Esc, r ou R, nenhum ajuste é feito.

As demais teclas são inválidas e a direção de movimento da próxima cobra é definida para a direção de movimento da última cobra, ou seja, a direção de movimento permanece inalterada.

//游戏主体逻辑函数
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ão Executar

Descrição do parâmetro:

x: A mudança da abcissa depois que a cobra se move em relação à abcissa da cobra atual.

y: A mudança da ordenada após a serpente se mover em relação à ordenada da serpente atual.

Dado um determinado intervalo de tempo, se o teclado for tocado dentro desse intervalo de tempo, a função de execução será encerrada e a função de jogo será retornada para ler as teclas. Se não for virado, primeiro julgue se a cobra atinge a posição movida para pontuar ou se o jogo acabou e, em seguida, mova a posição da cobra.

Se o teclado não foi tocado, a função while na função run sempre será executada, e a cobra continuará se movendo em uma direção até que o jogo termine.

//执行按键
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. Pontuação do julgamento e final

Pontuação do Julgamento:

Os pontos são concedidos se o local que a cabeça da cobra estiver prestes a alcançar for comida. Após a pontuação, o corpo da cobra precisa ser alongado e a pontuação atual precisa ser atualizada, além disso, o alimento precisa ser regenerado.

Acabou o julgamento:

Se a posição que a cabeça da cobra está prestes a alcançar for uma parede ou um corpo de cobra, o jogo termina. Depois que o jogo terminar, compare a pontuação deste jogo com a maior pontuação da história, dê a sentença correspondente e pergunte ao jogador se quer jogar outro jogo, e você pode jogar livremente.

//判断得分与结束
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: Se a pontuação deste jogo for maior que a pontuação mais alta da história, você precisa atualizar a pontuação mais alta no arquivo.

13. Leia a pontuação mais alta do arquivo

Primeiro, você precisa usar a função fopen para abrir o arquivo "Snake's high score record.txt". Se você executar o código pela primeira vez, o arquivo será criado automaticamente e o registro histórico mais alto será definido como 0, e então leia o histórico no arquivo. O registro mais alto é armazenado na variável max, e o arquivo pode ser fechado.

 

14. Atualize a pontuação mais alta para o arquivo

Primeiro, use a função fopen para abrir o "registro de pontuação mais alta da cobra.txt" e, em seguida, escreva a pontuação do jogo no arquivo (sobrescrever).

15. Função principal

Com o suporte das funções acima, escrever a função main é bem simples, mas você precisa prestar atenção nos três pontos a seguir:

A variável global grade precisa ser inicializada com 0 dentro da função main e não pode ser inicializada com 0 globalmente, porque precisamos redefinir a nota atual para 0 quando o jogador pressiona a tecla R para reproduzir.

Recomenda-se que o ponto inicial da geração de números aleatórios seja definido na função principal.

A instrução #pragma na função main é usada para eliminar avisos como:

 

Seis, através da versão de parede do código da linguagem C snake

1. Exibição do efeito do jogo

Pessoalmente, acho que a versão de parede da cobra é mais divertida que a versão normal, o que você acha?

2. Código do jogo de linguagem Snake C

O código a seguir pode ser executado diretamente, bem-vindo para tentar:

#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. Explicação detalhada do código do jogo

As semelhanças e pequenas diferenças das anteriores não serão aqui elaboradas, e quem estiver interessado compreenderá olhando para elas.

Sete, um grande número de aquisição de código-fonte do jogo do projeto de linguagem C

Você pode seguir a conta pública do WeChat: "C e C plus" Responder: "TC"

Acho que você gosta

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