前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言基于控制台实现简单的贪吃蛇游戏的步骤和核心机制

C语言基于控制台实现简单的贪吃蛇游戏的步骤和核心机制

原创
作者头像
晨星成焰
发布2024-05-05 21:07:17
2280
发布2024-05-05 21:07:17
举报

为什么写一个贪吃蛇游戏

设计贪吃蛇游戏的主要目的是

夯实自己的C语言基础,训练编程思维,培养解决问题,学习游戏开发基础的思路。

总之就是巩固基础😋

游戏设计


游戏规则

  1. 游戏开始时,蛇只有一个头部,并位于游戏界面的随机位置。
  2. 蛇每吃到一个食物,身体就会增长一个单位并得分加1。
  3. 蛇可以通过键盘wasd控制上、下、左、右四个方向的移动。
  4. 如果蛇头撞到自己的身体或撞墙,则游戏结束。

需求分析

  1. 初始化游戏界面和蛇的位置。
  2. 在游戏循环中,不断接收玩家输入,并根据输入移动蛇的位置。
  3. 判断蛇是否吃到了食物,如果是则增长身体并生成新的食物。
  4. 检测蛇头是否碰到自己的身体或墙壁,如果是则游戏结束。
  5. 更新游戏界面
  6. 重复步骤2至5,直到游戏结束。

实现步骤

初始化

地图和存储

用一个二维数组存储地图,一个二维数组存储地图上的数字对应的值,方便用中文替换,或者以后用贴图或者加上颜色渲染

代码语言:cpp
复制
#define mapX 16
#define mapY 16


int map[mapX][mapY]{
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

char CN[5][1024] = {
	{"  "},
	{"墙"},
	{"果"},
	{"头"},
	{"身"},
};

蛇的身体

蛇的身子是一节一节的,此时最容易联想到的数据结构就是顺序表,链表,二维数组,因为我对于顺序表和链表的使用还很一般,这里就用一个二维数组来维护蛇的身体,随机生成蛇头的位置,并保证不会生成在食物的位置

代码语言:cpp
复制
	int snake[mapX * mapY][2] = { {1,1} };
	while (1) {
		snake[0][0] = rand() % 14 + 1;
		snake[0][1] = rand() % 14 + 1;
		if (snake[0][0] != fruitPosX && snake[0][1] != fruitPosY) {
			break;
		}
	}

食物的产生

食物的产生,随机的在地图中产生一个节点,在蛇的头坐标和食物的坐标重复的时候,食物消失,蛇的身子加长

代码语言:cpp
复制
	//果子位置初始化
	srand(time(NULL));
	int fruitPosX = rand() % 14 + 1;
	int fruitPosY = rand() % 14 + 1;
	map[fruitPosX][fruitPosY] = 2;

		//检测吃到果子
		if (snake[0][0] == fruitPosX && snake[0][1] == fruitPosY) {
			snakeLength++;
			while (1) {
				//果子位置初始化
				//再次初始化随机值的话会固定fruitPosX和fruitPosY
				srand(time(NULL));
				fruitPosX = rand() % 14 + 1;
				fruitPosY = rand() % 14 + 1;
				int fruitFlag = 1;
				for (int i = 0; i < snakeLength; i++) {
					if (snake[i][0] == fruitPosX && snake[i][1] == fruitPosY) {
						fruitFlag = 0;
						break;
					}
				}
				if (fruitFlag) {
					map[fruitPosX][fruitPosY] = 2;
					break;
				}
			}
		}

失败判断

蛇触碰到身子或者触碰到墙死亡

代码语言:cpp
复制
		// 检测蛇头是否撞到自己的身体
		for (int i = 1; i < snakeLength; i++) {
			if (snake[0][0] == snake[i][0] && snake[0][1] == snake[i][1]) {
				gameOver = 1;
			}
		}

		// 检测蛇头是否撞墙
		if (snake[0][0] <= 0 || snake[0][0] >= mapX-1 || snake[0][1] <= 0 || snake[0][1] >= mapY-1) {
			gameOver = 1;
		}
		//gameOver
		if (gameOver) {
			map[fruitPosX][fruitPosY] = 0;
			//清除屏幕
			printf("\033[H\033[2J");
			printf("\n 哇,你输掉了游戏ヾ( ̄ー ̄)X(^▽^)ゞ");
			break;
		}

加入初始化函数

代码语言:cpp
复制
void snakeGame_Init() {
	//gameInit

	//果子位置初始化
	srand(time(NULL));
	int fruitPosX = rand() % 14 + 1;
	int fruitPosY = rand() % 14 + 1;
	map[fruitPosX][fruitPosY] = 2;

	//蛇头位置初始化
	int snake[mapX * mapY][2] = { {1,1} };
	while (1) {
		snake[0][0] = rand() % 14 + 1;
		snake[0][1] = rand() % 14 + 1;
		if (snake[0][0] != fruitPosX && snake[0][1] != fruitPosY) {
			break;
		}
	}
	map[snake[0][0]][snake[0][1]] = 3;
	int snakeLength = 3;
	int gameOver = 0;

	while (1)
	{
		//检测吃到果子
		if (snake[0][0] == fruitPosX && snake[0][1] == fruitPosY) {
			snakeLength++;
			while (1) {
				//果子位置初始化
				//再次初始化随机值的话会固定fruitPosX和fruitPosY
				srand(time(NULL));
				fruitPosX = rand() % 14 + 1;
				fruitPosY = rand() % 14 + 1;
				int fruitFlag = 1;
				for (int i = 0; i < snakeLength; i++) {
					if (snake[i][0] == fruitPosX && snake[i][1] == fruitPosY) {
						fruitFlag = 0;
						break;
					}
				}
				if (fruitFlag) {
					map[fruitPosX][fruitPosY] = 2;
					break;
				}
			}
		}
		//清除屏幕
		printf("\033[H\033[2J");
		//地图
		for (int i = 0; i < mapX; i++) {
			for (int j = 0; j < mapY; j++) {
				printf("%s", CN[map[i][j]]);
			}
			printf("\n");
		}
		printf("\n");
		//移动
		moveS(snake, &snakeLength);


		// 检测蛇头是否撞到自己的身体
		for (int i = 1; i < snakeLength; i++) {
			if (snake[0][0] == snake[i][0] && snake[0][1] == snake[i][1]) {
				gameOver = 1;
			}
		}

		// 检测蛇头是否撞墙
		if (snake[0][0] <= 0 || snake[0][0] >= mapX-1 || snake[0][1] <= 0 || snake[0][1] >= mapY-1) {
			gameOver = 1;
		}
		//gameOver
		if (gameOver) {
			map[fruitPosX][fruitPosY] = 0;
			//清除屏幕
			printf("\033[H\033[2J");
			printf("\n 哇,你输掉了游戏ヾ( ̄ー ̄)X(^▽^)ゞ");
			break;
		}
	}
}

蛇的移动

首先定义一个全局变量direction用来判断每次自动移动时蛇头向那个方向移动

主体逻辑

获取玩家输入:通过 _kbhit() 和 _getch() 函数检查是否有键盘输入。如果有,根据按下的键改变 direction 的值,从而决定蛇的移动方向。

处理自动移动:如果没有键盘输入,direction 的值保持不变,表示蛇将按照之前的方向自动移动。

计算新位置:根据当前的 direction 值,计算出蛇头的新位置。这里通过 dirX 和 dirY 来表示移动的方向。

更新蛇头位置:首先将蛇头当前位置的值设置为0(表示空白),然后更新蛇头的位置,最后将新位置设置为3(表示蛇头)。

更新蛇身位置:通过遍历蛇身的每个部分,将它们按照蛇头的新位置移动。每个部分的新位置也被设置为4(表示蛇身)。

更新地图:在移动蛇的过程中,需要更新地图上的相应位置,以反映蛇的移动情况。

注意事项

_kbhit() 和 _getch() 是Windows特有的函数,用于检测键盘输入。

不加Sleep(会一直进入未检测到输入的状态)

所以需要Sleep(1000) 用于暂停一秒,检测按键输入,另外可以通过调整sleep数值来改变难度

代码语言:cpp
复制
//蛇头的移动方向
int direction = 1;

void moveS(int (*snake)[2],int* snakeLength) {
	int dirX = 0;
	int dirY = 0;
	// 获取玩家输入
	//char keydown = _getch();
	//反应时间
	Sleep(1000);
	if (_kbhit() != 0)
	{
		switch (_getch())
		{
		case 'w':
			direction = 1;
			break;
		case 's':
			direction = 2;
			break;
		case 'a':
			direction = 3;
			break;
		case 'd':
			direction = 4;
			break;
		}
		// 手动移动蛇的位置
		switch (direction)
		{
		case 1:  // 上
			dirX = -1;
			break;
		case 2:  // 下
			dirX = 1;
			break;
		case 3:  // 左
			dirY = -1;
			break;
		case 4:  // 右
			dirY = 1;
			break;
		}
	}
	else {
		// 自动移动蛇的位置
		switch (direction)
		{
		case 1:  // 上
			dirX = -1;
			break;
		case 2:  // 下
			dirX = 1;
			break;
		case 3:  // 左
			dirY = -1;
			break;
		case 4:  // 右
			dirY = 1;
			break;
		}
	}
		// 头的移动
		int posX = snake[0][0];
		int posY = snake[0][1];
		int nextX = posX + dirX;
		int nextY = posY + dirY;
		map[posX][posY] = 0;
		snake[0][0] = nextX;
		snake[0][1] = nextY;
		map[nextX][nextY] = 3;

		// 身子跟着头移动
		for (int i = 1;i<*snakeLength; i++) 
		{
			nextX = posX;
			nextY = posY;
			posX = snake[i][0];
			posY = snake[i][1];
			snake[i][0] = nextX;
			snake[i][1] = nextY;
			map[posX][posY] = 0;
			map[nextX][nextY] = 4;
		}
}

所有代码

代码语言:cpp
复制
#include <stdio.h>
#include <stdlib.h>
#include <ctime>
#include<conio.h>
#include<Windows.h>

#define mapX 16
#define mapY 16

int map[mapX][mapY]{
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

char CN[5][1024] = {
	{"  "},
	{"墙"},
	{"果"},
	{"头"},
	{"身"},
};

//蛇头的移动方向
int direction = 1;

void moveS(int (*snake)[2],int* snakeLength) {
	int dirX = 0;
	int dirY = 0;
	// 获取玩家输入
	//char keydown = _getch();
	//反应时间
	Sleep(1000);
	if (_kbhit() != 0)
	{
		switch (_getch())
		{
		case 'w':
			direction = 1;
			break;
		case 's':
			direction = 2;
			break;
		case 'a':
			direction = 3;
			break;
		case 'd':
			direction = 4;
			break;
		}
		// 手动移动蛇的位置
		switch (direction)
		{
		case 1:  // 上
			dirX = -1;
			break;
		case 2:  // 下
			dirX = 1;
			break;
		case 3:  // 左
			dirY = -1;
			break;
		case 4:  // 右
			dirY = 1;
			break;
		}
	}
	else {
		// 自动移动蛇的位置
		switch (direction)
		{
		case 1:  // 上
			dirX = -1;
			break;
		case 2:  // 下
			dirX = 1;
			break;
		case 3:  // 左
			dirY = -1;
			break;
		case 4:  // 右
			dirY = 1;
			break;
		}
	}
		// 头的移动
		int posX = snake[0][0];
		int posY = snake[0][1];
		int nextX = posX + dirX;
		int nextY = posY + dirY;
		map[posX][posY] = 0;
		snake[0][0] = nextX;
		snake[0][1] = nextY;
		map[nextX][nextY] = 3;

		// 身子跟着头移动
		for (int i = 1;i<*snakeLength; i++) 
		{
			nextX = posX;
			nextY = posY;
			posX = snake[i][0];
			posY = snake[i][1];
			snake[i][0] = nextX;
			snake[i][1] = nextY;
			map[posX][posY] = 0;
			map[nextX][nextY] = 4;
		}
}

void snakeGame_Init() {
	//gameInit

	//果子位置初始化
	srand(time(NULL));
	int fruitPosX = rand() % 14 + 1;
	int fruitPosY = rand() % 14 + 1;
	map[fruitPosX][fruitPosY] = 2;

	//蛇头位置初始化
	int snake[mapX * mapY][2] = { {1,1} };
	while (1) {
		snake[0][0] = rand() % 14 + 1;
		snake[0][1] = rand() % 14 + 1;
		if (snake[0][0] != fruitPosX && snake[0][1] != fruitPosY) {
			break;
		}
	}
	map[snake[0][0]][snake[0][1]] = 3;
	int snakeLength = 3;
	int gameOver = 0;

	while (1)
	{
		//检测吃到果子
		if (snake[0][0] == fruitPosX && snake[0][1] == fruitPosY) {
			snakeLength++;
			while (1) {
				//果子位置初始化
				//再次初始化随机值的话会固定fruitPosX和fruitPosY
				srand(time(NULL));
				fruitPosX = rand() % 14 + 1;
				fruitPosY = rand() % 14 + 1;
				int fruitFlag = 1;
				for (int i = 0; i < snakeLength; i++) {
					if (snake[i][0] == fruitPosX && snake[i][1] == fruitPosY) {
						fruitFlag = 0;
						break;
					}
				}
				if (fruitFlag) {
					map[fruitPosX][fruitPosY] = 2;
					break;
				}
			}
		}
		//清除屏幕
		printf("\033[H\033[2J");
		//地图
		for (int i = 0; i < mapX; i++) {
			for (int j = 0; j < mapY; j++) {
				printf("%s", CN[map[i][j]]);
			}
			printf("\n");
		}
		printf("\n");
		//移动
		moveS(snake, &snakeLength);


		// 检测蛇头是否撞到自己的身体
		for (int i = 1; i < snakeLength; i++) {
			if (snake[0][0] == snake[i][0] && snake[0][1] == snake[i][1]) {
				gameOver = 1;
			}
		}

		// 检测蛇头是否撞墙
		if (snake[0][0] <= 0 || snake[0][0] >= mapX-1 || snake[0][1] <= 0 || snake[0][1] >= mapY-1) {
			gameOver = 1;
		}
		//gameOver
		if (gameOver) {
			map[fruitPosX][fruitPosY] = 0;
			//清除屏幕
			printf("\033[H\033[2J");
			printf("\n 哇,你输掉了游戏ヾ( ̄ー ̄)X(^▽^)ゞ");
			break;
		}
	}
}

int main() {
	snakeGame_Init();
}

运行截图


未来展望

实现贴图渲染/颜色渲染

做一个游戏开始界面,计算得分

打包成.exe文件

存储在数据库中,可以查看历史最高分

我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么写一个贪吃蛇游戏
  • 游戏设计
    • 游戏规则
      • 需求分析
      • 实现步骤
        • 初始化
          • 地图和存储
          • 蛇的身体
          • 食物的产生
          • 失败判断
          • 加入初始化函数
        • 蛇的移动
          • 主体逻辑
          • 注意事项
        • 所有代码
          • 运行截图
          • 未来展望
      相关产品与服务
      文件存储
      文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档