C语言实现经典游戏——《贪吃蛇》

一、游戏设计与分析

这个游戏代码我们分成三个文件来完成。

  • Snake.h —— 游戏的头文件
     这个文件中包含游戏代码实现所需要的结构体定义以及函数声明。

  • Snake.c —— 游戏的源文件
     这个文件中是实现游戏操作的代码。

  • test.c —— 游戏的测试文件
     这个文件是用来检验代码能否正常运行。

1、地图

我们最终的贪吃蛇⼤纲要是这个样⼦,那我们的地图如何布置呢?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  这⾥不得不讲⼀下控制台窗⼝的⼀些知识,如果想在控制台的窗⼝中指定位置输出信息,我们得知道该位置的坐标,所以⾸先介绍⼀下控制台窗⼝的坐标知识。
  控制台窗⼝的坐标如下所⽰,横向的是X轴,从左向右依次增⻓,纵向是Y轴,从上到下依次增⻓。
在这里插入图片描述
  在游戏地图上,我们打印墙体使⽤宽字符:□,打印蛇使⽤宽字符●,打印⻝物使⽤宽字符★普通的字符是占⼀个字节的,这类宽字符是占⽤2个字节。

(1)、 <locale.h>本地化

  <locale.h>提供的函数⽤于控制C标准库中对于不同的地区会产⽣不⼀样⾏为的部分。
在标准中,依赖地区的部分有以下⼏项:
• 数字量的格式
• 货币量的格式
• 字符集
• ⽇期和时间的表⽰形式

类型

通过修改地区,程序可以改变它的⾏为来适应世界的不同区域。但地区的改变可能会影响库的许多部
分,其中⼀部分可能是我们不希望修改的。所以C语⾔⽀持针对不同的类项进⾏修改,下⾯的⼀个宏,
指定⼀个类项:
• LC_COLLATE:影响字符串⽐较函数strcoll() 和strxfrm() 。
• LC_CTYPE:影响字符处理函数的⾏为。
• LC_MONETARY:影响货币格式。
• LC_NUMERIC:影响printf() 的数字格式。
• LC_TIME:影响时间格式strftime() 和wcsftime() 。
• LC_ALL-针对所有类项修改,将以上所有类别设置为给定的语⾔环境。

(2)、 setlocale函数

1  char* setlocale (int category, const char* locale)

  setlocale 函数⽤于修改当前地区,可以针对⼀个类项修改,也可以针对所有类项。
  setlocale 的第⼀个参数可以是前⾯说明的类项中的⼀个,那么每次只会影响⼀个类项,如果第⼀个参数是LC_ALL,就会影响所有的类项。
  C标准给第⼆个参数仅定义了2种可能取值:“C”(正常模式)和""(本地模式)。

 1 setlocale(LC_ALL, "C");

  当地区设置为"C"时,库函数按正常⽅式执⾏,⼩数点是⼀个点。
当程序运⾏起来后想改变地区,就只能显⽰调⽤setlocale函数。⽤""作为第2个参数,调⽤setlocale函数就可以切换到本地模式,这种模式下程序会适应本地环境。
  ⽐如:切换到我们的本地模式后就⽀持宽字符(汉字)的输出等。


 1. setlocale(LC_ALL, " ");//切换到本地环境

(3)、宽字符的打印

  那如果想在屏幕上打印宽字符,怎么打印呢?

  宽字符的字⾯量必须加上前缀“L”,否则C语⾔会把字⾯量当作窄字符类型处理。前缀“L”在单引号前⾯,表⽰宽字符,对应wprintf() 的占位符为 %lc 。在双引号前⾯,表⽰宽字符串,对应wprintf() 的占位符为%ls 。

 #include <stdio.h>
 #include<locale.h>
 
 int main() 
 {
   
   
 	setlocale(LC_ALL, "");
    wchar_t ch1 = L'●'; 
	wchar_t ch2 = L'⽐';
 	wchar_t ch3 = L'特';
 	wchar_t ch4 = L'★';
 	printf("%c%c\n", 'a', 'b');
 	
	wprintf(L"%lc\n", ch1);
 	wprintf(L"%lc\n", ch2);
 	wprintf(L"%lc\n", ch3);
 	wprintf(L"%lc\n", ch4);
 	return 0;
 }

在这里插入图片描述
  两个普通字符的大小等于一个宽字符的大小
在这里插入图片描述

2、游戏分析

(1)、Snake.h

  首先,在Snake.h中创建两个结构体:一个蛇的结构体;一个蛇的节点的结构体。

  • 蛇的节点的结构体
      整个蛇身可以看作是一个单链表,所有节点一次连接起来的。
      蛇的一个节点 要包括该点在控制台坐标系上的X轴值和Y轴值,以及该节点的下一个节点

如下:

//蛇身节点类型
typedef struct SnakeNode
{
   
   
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode, * PSnakeNode;
//PSnakeNode是指向该结构体的指针
//eg: PSnakeNode n 等同于 SnakeNode* n
  • 蛇身的结构体
      我们可以想象一下,将舍生看作是一个链表,游戏过程中要将蛇运动起来,要想完成贪吃蛇的运动,我们需要再蛇身中添加那些元素呢?

  整个蛇身要包含 指向蛇头的指针、指向食物节点的指针、蛇的移动方向、游戏状态、应该食物的分数、总成绩、休息时间。

//贪吃蛇
typedef struct Snake
{
   
   
	PSnakeNode pSnake; //指向蛇头的指针
	PSnakeNode pFood; //指向食物节点的指针
	enum DIRECTTON dir; //蛇移动的方向
	enum GAME_STATUS status; //游戏状态
	int food_weight; //一个食物的分数
	int score; //总成绩
	int sleep_time; //休息时间,时间越短,速度越快,时间越长,速度越慢
}Snake, * PSnake;

完成结构体后,我们还要继续分析结构体中的元素。

  • 蛇的移动方向
//蛇移动的方向
enum DIRECTTON
{
   
   
	up = 1,
	down,
	left,
	right
};
  • 游戏状态
//游戏状态
//正常,撞墙,撞自己,正常退出,
enum GAME_STATUS
{
   
   
	ok,//正常
	KILL_BY_WALL,//撞墙
	KILL_BY_SELF,//撞自己
	END_NORMAL//正常退出
};

(2)、test.c

  在测试文件中,想要打印游戏地图,那要先适应本地化模式,所以我们在main函数中使用setlocale函数适配本地环境。
  为了方便之后的代码测试,我们在main函数中调用一个自定义函数test01。

void test01()
{
   
   
	//创建贪吃蛇
	Snake snake = {
   
    0 };
	//一、初始化游戏
	GameStart(&snake);
	//二、运行游戏
	GameRun(&snake);
	//三、结束游戏——善后工作
	GameEnd(&snake);
}
int main()
{
   
   
	//先适配本地环境
	setlocale(LC_ALL, "");
	test01();
	return 0;
}

(3)、Snake.c

  接下来,我们简单的将代码的实现分为三部分:

  • 初始化游戏GameStart();

     1.打印环境界面和功能介绍
     2.绘制地图
     3.创建蛇
     4.创建食物
    
  • 运行游戏GameRun();

    1、键盘上的按键情况
    2、蛇每走一步的状态
    3、蛇上下左右走
    4、判断下一个节点是不是食物
    5、判断蛇有没有撞墙或者撞到自己
    
  • 结束游戏——善后工作GameEnd();

二、初始化游戏

在这里插入图片描述

1、打印环境界面和功能介绍

  我们要先获取控制板上光标的信息,对光标进行相应的操作。
这里所要用到的函数我在上一篇文章中已经详细解释过了,所以我们这里直接使用。
  详情请参考上一篇文章:link

	//隐藏光标,将光标设置到指定位置
	//设置面板
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");
	//获取标准输出设备的句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	//定义一个光标信息结构体
	CONSOLE_CURSOR_INFO cursor_info = {
   
    0 };
	//获取光标信息
	GetConsoleCursorInfo(houtput, &cursor_info);
	//设置光标可见度
	cursor_info.bVisible = false;
	//设置光标信息
	SetConsoleCursorInfo(houtput, &cursor_info);

  我们要定位光标,在相应位置打印相应的文字。
这里可以创建一个函数SetPos,来定位光标

//获取光标位置
v
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值