C语言用vs2017写一个简单的贪吃蛇游戏

本文介绍了一个使用C语言编写的经典贪吃蛇游戏。游戏包括难度选择、用户注册、控制台界面绘制、蛇的移动逻辑、食物生成、碰撞检测等功能。玩家可以通过键盘控制蛇的移动方向,吃掉食物来增长蛇身并获取分数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 源代码
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<windows.h>
#include<time.h>
#include<conio.h>
#define High 20
#define Wide 60
int speed = 0;  //定义一个全局变量控制小蛇蛇的速度
char name[100] = "游客选手"; //用户名称
void Snake_Menu()
{
	COORD pos;
	pos.X = 50;
	pos.Y = 10;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
	printf("Dxs版贪吃蛇\n\n\n");
	printf("\t\t\t\t\t         请选择游戏难度:\n\n");
	printf("\t\t\t\t\t       简单模式请按键盘'1'\n\n");
	printf("\t\t\t\t\t       困难模式请按键盘'2'\n\n");
	printf("\t\t\t\t\t       噩梦模式请按键盘'3'\n\n");
	printf("\t\t\t\t\t       注册用户请按键盘'4'\n\n");
	printf("\t\t\t\t\t       退出游戏请按ESC键!\n");
}
void Register()
{
	system("cls");
	COORD pos;
	pos.X = 50;
	pos.Y = 10;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
	printf("Dxs版贪吃蛇\n\n\n");
	printf("\t\t\t\t\t         请输入用户名称:");
	scanf("%s", name);
	printf("注册成功!请按任意键返回游戏!\n");
	char ch = _getch();
	system("cls");
}
void Draw_map()
{
	printf("\t\t\t贪吃蛇小游戏\n");
	for (int i = 0; i <= High; i++)
	{
		for (int j = 0; j <= Wide; j++)
		{
			if (i==0||j==0||i== High || j == Wide)
			{
				printf("&");
			}
			else {
				printf(" ");
			}		
		}
		if (i == 5)
		{
			printf("   作者:Dxs 日期:2019-8-3");
		}
		if (i == 6)
		{
			printf("   游戏按键提示如下:");
		}
		if (i == 7)
		{
			printf("   上:w W ↑ 8");
		}
		if (i == 8)
		{
			printf("   下:s S ↓ 2");
		}
		if (i == 9)
		{
			printf("   左:a A ← 4");
		}
		if (i == 10)
		{
			printf("   右:d D → 6");
		}
		if (i == 11)
		{
			printf("   暂停:space(空格键)");
		}
		printf("\n");
	}
}
typedef struct body
{
	int x;
	int y;
}BODY;
typedef struct Snake
{
	BODY list[Wide*High];
	int size;
	BODY food;
	int dx;
	int dy;
	int score;
	COORD coord;//光标信息
	BODY tail;
}Snake;
void init_food(Snake *snake)    //食物初始化
{
	srand((unsigned int)time(NULL));
	snake->food.x = rand() % (Wide-2)+1;
	snake->food.y = rand() % (High-2)+2;
}
void init_snake(Snake *snake)
{
	//蛇头初始化
	snake->list[0].x = Wide / 2;
	snake->list[0].y = High / 2;
	//蛇尾初始化
	snake->list[1].x = Wide / 2-1;
	snake->list[1].y = High / 2;

	//初始化蛇的大小
	snake->size = 2;
	snake->score = 0;
	snake->dx = 1;
	snake->dy = 0;
	init_food(snake);
}
//移动光标
void test()
{
	COORD pos;
	pos.X = 20;
	pos.Y = 30;
	//设置光标的位置
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

//隐藏光标
void hide_cur()
{
	//隐藏控制台的光标
	CONSOLE_CURSOR_INFO cci;
	cci.dwSize = sizeof(cci);
	cci.bVisible = FALSE;
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
}

//4.打印蛇和食物
void Show_ui(Snake *snake)
{
	for (int i = 0; i < snake->size; i++)
	{
		//把蛇的位置坐标给光标的位置
		snake->coord.X = snake->list[i].x;
		snake->coord.Y = snake->list[i].y;
		//移动光标
		SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
		if (i == 0)
		{
			printf("@");
		}
		else
		{
			printf("*");
		}
	}
	//显示食物
	//把食物的坐标给光标
	snake->coord.X = snake->food.x;
	snake->coord.Y = snake->food.y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
	printf("$");
	//将原来的蛇尾打印为空。
	snake->coord.X = snake->tail.x;
	snake->coord.Y = snake->tail.y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
	printf(" ");
}

//移动蛇
void move_snake(Snake *snake)
{

	//记录蛇尾的坐标
	snake->tail.x = snake->list[snake->size - 1].x;
	snake->tail.y = snake->list[snake->size - 1].y;
	//更新蛇的身体坐标,除蛇头外
	for (int i = snake->size - 1; i > 0; i--)
	{
		//原来蛇头的位置赋值给蛇身,前面的蛇身位置赋值给后面的蛇身
		snake->list[i] = snake->list[i - 1];
	}
	//更新蛇头的位置
	snake->list[0].x += snake->dx;
	snake->list[0].y += snake->dy;
}

void Eat_food(Snake * snake)
{
	if (snake->list[0].x == snake->food.x&&snake->list[0].y == snake->food.y)
	{
		init_food(snake);
		snake->size += 1;
		//分数增加
		snake->score += 100;
	}
}
char key2 = 0;   //存储上一个按键值,通过两次按键比较防止小蛇可以反身撞击自己
void control_snake(Snake * snake)
{
	char key = 0;
	COORD pos;
	if (_kbhit())
	{
		key = _getch();
		
		switch (key)
		{
		case 'w':
		case 'W':
		case '8':
		case 72:
			if (key2 == 's' || key2 == 'S' || key2 == 80||key2=='2')
			{
				break;
			}
			else
			{
				snake->dx = 0;
				snake->dy = -1;
				key2 = key;
			}
			
			break;
		case 's':
		case 'S':
		case '2':
		case 80:
			if (key2 == 'w' || key2 == 'W' || key2 == 72||key2=='8')
			{
				break;
			}
			else
			{
				snake->dx = 0;
				snake->dy = 1;
				key2 = key;
			}
			
			break;
		case 'a':
		case 'A':
		case '4':
		case 75:
			if (key2 == 'd' || key2 == 'D' || key2 == 77||key2=='6')
			{
				break;
			}
			else
			{
				snake->dx = -1;
				snake->dy = 0;
				key2 = key;
			}
		
			break;
		case 'd':
		case 'D':
		case '6':
		case 77:
			if (key2 == 'a' || key2 == 'A' || key2 == 75||key2=='4')
			{
				break;
			}
			else
			{
				snake->dx = 1;
				snake->dy = 0;
				key2 = key;
			}
			
			break;
		case 32:
			pos.X = Wide/2-10;
			pos.Y = High/2;
			//设置光标的位置
			SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
			printf("游戏暂停!任意键继续!\n");
			_getch();
			system("cls");
			Draw_map();
			break;
		default:
			break;
		}
	}
	
}
//游戏结束
void game_over(Snake * snake)
{
	snake->coord.X = 0;
	snake->coord.Y = 10;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
	printf("            ####         #       ##     ##   ######         #####    #       #   ######   #######     ##\n"
		   "          ##            # #      ##     ##   #             #     #    #     #    #        #     ##    ##\n"
           "          ##  ####     #####     # #   # #   ######        #     #     #   #     ######   # ##        ##\n"
		   "          ##    ##    #     #    #  # #  #   #             #     #      # #      #        #   ##        \n"
           "           ######    #       #   #   #   #   ######         #####        #       ######   #     ##    ##\n");
	
	
	printf("\n\n\t\t您的分数为:%d分", snake->score);

	Sleep(10000);
	
	exit(0);
}
//蛇是否碰到自己
void snake_eat_body(Snake *snake)
{
	//如果蛇头碰到蛇身的任何一个,都算碰到自己,游戏结束
	for (int i = 1; i < snake->size; i++)
	{
		if (snake->list[0].x == snake->list[i].x&&snake->list[0].y == snake->list[i].y)
		{
			//游戏结束
			system("cls");
			game_over(snake);
		}
	}
}
void CurrentScoreAndUser(Snake* snake)
{
	COORD pos;
	pos.X = 64;
	pos.Y = 2;
	//设置光标的位置
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
	printf("当前得分:%d\n", snake->score);
	pos.X = 64;
	pos.Y = 3;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
	printf("当前用户:%s\n", name);
}

//5.游戏业务逻辑
void start_game(Snake *snake)
{
	init_snake(snake);
	move_snake(snake);
	Show_ui(snake);
	while (snake->list[0].x < Wide&&snake->list[0].y < High&&snake->list[0].x>=1&&snake->list[0].y>=2)
	{
		//1.控制蛇的方向
		control_snake(snake);
		//2。蛇移动,是不断的改变蛇的坐标和打印蛇
		move_snake(snake);
		Show_ui(snake);
		//更新蛇的坐标
		//打印蛇
		//3.蛇是否碰到自己
		snake_eat_body(snake);
		//4.蛇是否碰到食物(食物消失,产生新的事物)
		Eat_food(snake);
		CurrentScoreAndUser(snake);
		Sleep(speed);//控制蛇的速度
	}
	system("cls");
	game_over(snake);
}
int main(void)
{
    Loop:
	Loop2:
	hide_cur();
	Snake_Menu();
	char ss = _getch();
	switch (ss)
	{
	case '1':
		speed = 800;
		system("cls");
		break;
	case '2':
		speed = 400;
		system("cls");
		break;
	case '3':
		speed = 80;
		system("cls");
		break;
	case '4':
		Register();
		goto Loop2;
		system("cls");
		break;
	case 27:
		exit(0);
		break;
	default:
		printf("输入错误,请重新输入!");
		Sleep(2000);
		goto Loop;
		break;
	}
	Draw_map();
	//2.申请蛇的结构体空间
	Snake *snake = (Snake *)malloc(sizeof(Snake));
	start_game(snake);
	printf("\n");
	system("pause");
	return 0;
}

  1. 结果展示
    菜单
    游戏界面
<think>我们根据用户要求,使用C语言一个完整的贪吃蛇游戏代码。我们将参考上述引用中的结构,并实现一个控制台版本的贪吃蛇游戏游戏的基本要素包括: 1. 蛇:由多个节点组成,每个节点有坐标。蛇可以移动,并且可以改变方向(上、下、左、右)。 2. 食物:随机出现在屏幕上,被蛇吃掉后,蛇的长度增加,并在新的位置生成食物。 3. 游戏区域:一个固定的矩形区域,蛇头碰到边界或自己的身体则游戏结束。 我们将使用Windows API中的控制台函数来设置光标位置、颜色等。注意:这个程序在Windows环境下运行。 代码结构: 1. 定义必要的常量和结构体(蛇的方向、蛇身节点、蛇的结构体、食物结构体等)。 2. 初始化游戏:绘制边框、初始化蛇和食物的位置、设置游戏状态。 3. 游戏主循环:处理输入、更新蛇的位置、判断是否吃到食物、判断游戏结束条件、绘制游戏界面。 4. 游戏结束:显示得分,并询问是否再来一局。 注意:我们将使用控制台函数来移动光标,避免频繁清屏导致的闪烁。 我们将使用以下函数(需要包含Windows.h): - SetConsoleCursorPosition: 设置控制台光标位置。 - GetStdHandle: 获取标准输出句柄。 - 其他控制台相关函数。 由于控制台默认是行缓冲,我们将使用_kbhit和_getch(在conio.h中)来检测键盘输入。 参考引用中的一些结构,我们设计如下: 步骤: 1. 定义蛇的节点(坐标)。 2. 定义蛇的方向(枚举)。 3. 定义蛇的结构(包括蛇的节点链表、当前方向、分数、速度等)。 4. 定义食物。 5. 初始化蛇:开始时蛇长度为3,水平放置,方向向右。 6. 生成食物:随机位置,且不能与蛇身重叠。 7. 主循环:输入处理、蛇的移动、吃食物判断、碰撞检测、绘制。 注意:由于控制台没有图形界面,我们用字符表示蛇和食物(例如:蛇身用'o',蛇头用'@',食物用'$')。 但是,引用[1]中使用了链表来管理蛇的节点,这样蛇可以动态增长。我们也可以使用数组,但为了动态增长,我们使用链表(动态分配节点)会更灵活。 然而,考虑到简单性,我们也可以使用数组(固定最大长度)来实现,因为贪吃蛇的长度不会无限增长(受限于屏幕大小)。这里我们使用链表,但注意内存管理。 根据引用[1]的结构,我们定义: 节点结构体: typedef struct SnakeNode { int x; int y; struct SnakeNode* next; } SnakeNode, *pSnakeNode; 蛇的结构体: typedef struct Snake { pSnakeNode pHead; // 指向蛇头 pSnakeNode pFood; // 指向食物节点(这里我们可能不需要用链表节点表示食物,因为只有一个,所以我们可以用单独的两个变量表示食物坐标) int Score; int FoodWeight; // 每个食物的分数 int SleepTime; // 休眠时间,控制速度 enum DIR Dir; // 当前方向 enum GAME_STATUS Status; // 游戏状态 } Snake, *pSnake; 但为了简化,我们也可以不将食物作为链表节点,而是单独存储。所以,我们重新设计: typedef struct Snake { pSnakeNode pHead; // 蛇头 pSnakeNode pTail; // 蛇尾(可选,用于在尾部添加节点) int Score; int FoodWeight; int SleepTime; enum DIR Dir; enum GAME_STATUS Status; // 食物的位置 int food_x; int food_y; } Snake, *pSnake; 不过,引用[1]中食物也是用一个节点表示,但我们在绘制时单独绘制。所以,我们这里采用: typedef struct Snake { pSnakeNode pHead; pSnakeNode pFood; // 这里pFood实际上是一个节点,但我们生成食物时,只需要一个节点,并且这个节点在蛇身之外。 ... } 但这样,我们每次生成新食物时,需要释放之前的食物节点,并新建一个节点。为了避免频繁的内存分配,我们可以只用一个节点表示食物,并重新设置其位置。 考虑到简单性,我们不用链表节点表示食物,而是用两个整数(x,y)表示食物坐标。 因此,我们调整如下: typedef struct Snake { pSnakeNode pHead; // 蛇头(也是链表头) int Score; int FoodWeight; int SleepTime; enum DIR Dir; enum GAME_STATUS Status; int food_x; int food_y; } Snake, *pSnake; 方向枚举: enum DIR { UP = 1, DOWN, LEFT, RIGHT }; 游戏状态枚举: enum GAME_STATUS { OK, // 正常运行 KILL_BY_WALL, // 撞墙 KILL_BY_SELF, // 撞到自己 END_NORMAL // 正常结束(比如按ESC退出) }; 另外,我们需要一些辅助函数: - 设置光标位置:void SetPos(int x, int y); - 绘制蛇身和食物 - 创建蛇的初始身体(比如3个节点) - 生成食物:确保食物不在蛇的身体内 由于控制台坐标:左上角为原点(0,0),x轴向右,y轴向下。 移动蛇: 1. 根据当前方向,计算新的蛇头位置。 2. 创建新的蛇头节点,并插入链表头部。 3. 判断新的蛇头位置是否与食物重合: 如果重合,则吃掉食物(不删除尾部节点,分数增加,并生成新的食物) 如果不重合,则删除链表尾部节点(蛇尾向前移动,长度不变) 4. 检查碰撞:撞墙或撞到自身,则游戏结束。 注意:蛇不能反向移动(例如当前向右,不能直接按左键反向)。 我们开始编代码: 注意:由于控制台输入输出,我们需要设置控制台窗口大小,并隐藏光标。 由于代码较长,我们将分多个函数实现。 为了避免代码过长,我们只实现一个简单的版本,但包含基本功能。 我们将代码分为几个部分: 1. 头文件包含和常量定义 2. 结构体定义 3. 函数声明 4. 函数实现 由于用户要求完整代码,我们将在一个文件中实现(但为了清晰,我们会分多个函数)。 注意:我们使用Windows控制台API,因此只能在Windows下编译运行。 开始编:</think>以下是一个使用C语言实现的完整贪吃蛇游戏代码示例,基于Windows控制台环境开发: ```c #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <conio.h> #include <time.h> #include <stdbool.h> // 游戏区域尺寸 #define WIDTH 60 #define HEIGHT 20 // 方向控制 #define UP 72 #define DOWN 80 #define LEFT 75 #define RIGHT 77 // 蛇身节点结构 typedef struct SnakeNode { int x; int y; struct SnakeNode* next; } SnakeNode; // 游戏状态 typedef struct Game { SnakeNode* snake; // 蛇头指针 SnakeNode* food; // 食物位置 int direction; // 当前移动方向 int score; // 游戏得分 bool gameOver; // 游戏结束标志 int speed; // 游戏速度(毫秒) } Game; // 设置光标位置 void gotoxy(int x, int y) { COORD coord; coord.X = x; coord.Y = y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); } // 初始化游戏 void initGame(Game* game) { // 创建初始蛇身(3个节点) SnakeNode* head = (SnakeNode*)malloc(sizeof(SnakeNode)); head->x = WIDTH / 2; head->y = HEIGHT / 2; head->next = NULL; SnakeNode* body1 = (SnakeNode*)malloc(sizeof(SnakeNode)); body1->x = head->x - 1; body1->y = head->y; body1->next = head; SnakeNode* body2 = (SnakeNode*)malloc(sizeof(SnakeNode)); body2->x = body1->x - 1; body2->y = body1->y; body2->next = body1; game->snake = body2; game->direction = RIGHT; game->score = 0; game->gameOver = false; game->speed = 150; // 生成第一个食物 game->food = (SnakeNode*)malloc(sizeof(SnakeNode)); game->food->x = rand() % (WIDTH - 4) + 2; game->food->y = rand() % (HEIGHT - 4) + 2; game->food->next = NULL; } // 绘制游戏界面 void draw(Game* game) { system("cls"); // 绘制上边界 for (int i = 0; i < WIDTH + 2; i++) printf("#"); printf("\n"); // 绘制游戏区域 for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH + 2; x++) { if (x == 0 || x == WIDTH + 1) printf("#"); // 左右边界 else { bool drawn = false; // 绘制蛇身 SnakeNode* current = game->snake; while (current != NULL) { if (current->x == x && current->y == y) { printf(current == game->snake ? "@" : "o"); drawn = true; break; } current = current->next; } // 绘制食物 if (!drawn && game->food->x == x && game->food->y == y) { printf("*"); drawn = true; } // 绘制空白 if (!drawn) printf(" "); } } printf("\n"); } // 绘制下边界 for (int i = 0; i < WIDTH + 2; i++) printf("#"); printf("\n"); // 显示分数 printf("Score: %d\n", game->score); } // 生成新食物(确保不在蛇身上) void generateFood(Game* game) { bool valid; do { valid = true; game->food->x = rand() % (WIDTH - 4) + 2; game->food->y = rand() % (HEIGHT - 4) + 2; // 检查食物是否在蛇身上 SnakeNode* current = game->snake; while (current != NULL) { if (current->x == game->food->x && current->y == game->food->y) { valid = false; break; } current = current->next; } } while (!valid); } // 移动蛇 void moveSnake(Game* game) { // 创建新蛇头 SnakeNode* newHead = (SnakeNode*)malloc(sizeof(SnakeNode)); newHead->x = game->snake->x; newHead->y = game->snake->y; // 根据方向移动蛇头 switch (game->direction) { case UP: newHead->y--; break; case DOWN: newHead->y++; break; case LEFT: newHead->x--; break; case RIGHT: newHead->x++; break; } // 检查是否撞墙 if (newHead->x <= 0 || newHead->x >= WIDTH + 1 || newHead->y < 0 || newHead->y >= HEIGHT) { game->gameOver = true; free(newHead); return; } // 检查是否吃到食物 if (newHead->x == game->food->x && newHead->y == game->food->y) { // 增加分数 game->score += 10; // 增加游戏速度 if (game->speed > 50) game->speed -= 5; // 生成新食物 generateFood(game); } else { // 如果没有吃到食物,移除蛇尾 SnakeNode* current = game->snake; SnakeNode* prev = NULL; while (current->next != NULL) { prev = current; current = current->next; } if (prev != NULL) { prev->next = NULL; free(current); } } // 检查是否撞到自己 SnakeNode* current = game->snake; while (current != NULL) { if (current->x == newHead->x && current->y == newHead->y) { game->gameOver = true; free(newHead); return; } current = current->next; } // 添加新蛇头 newHead->next = game->snake; game->snake = newHead; } // 处理键盘输入 void processInput(Game* game) { if (_kbhit()) { int key = _getch(); // 方向键需要读取两次 if (key == 0xE0) { key = _getch(); // 防止180度转向 switch (key) { case UP: if (game->direction != DOWN) game->direction = UP; break; case DOWN: if (game->direction != UP) game->direction = DOWN; break; case LEFT: if (game->direction != RIGHT) game->direction = LEFT; break; case RIGHT: if (game->direction != LEFT) game->direction = RIGHT; break; } } // ESC键退出 else if (key == 27) { game->gameOver = true; } } } // 释放内存 void cleanup(Game* game) { // 释放蛇身节点 SnakeNode* current = game->snake; while (current != NULL) { SnakeNode* next = current->next; free(current); current = next; } // 释放食物节点 free(game->food); } int main() { srand((unsigned int)time(NULL)); // 初始化随机数种子 Game game; initGame(&game); while (!game.gameOver) { draw(&game); processInput(&game); moveSnake(&game); Sleep(game.speed); // 控制游戏速度 } cleanup(&game); system("cls"); printf("Game Over! Final Score: %d\n", game.score); printf("Press any key to exit..."); _getch(); return 0; } ``` ### 使用说明 1. **编译运行**:在Windows系统下使用C编译器(如GCC)编译运行 2. **控制方式**: - 方向键:控制蛇的移动方向 - ESC键:退出游戏 3. **游戏规则**: - 蛇吃到食物(`*`)会增长并得分 - 撞到墙壁或自身会导致游戏结束 - 随着分数增加,蛇的移动速度会加快 ### 功能特点 1. **动态内存管理**:使用链表结构实现蛇身,支持动态增长[^1] 2. **碰撞检测**:墙壁和自身碰撞检测[^1] 3. **速度控制**:随分数增加自动加速[^3] 4. **随机食物生成**:确保食物不会出现在蛇身上[^2] 5. **方向控制限制**:防止180度转向导致立即死亡[^5] ### 编译方法 ```bash gcc snake_game.c -o snake_game.exe snake_game.exe ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值