利用easyx写简单的贪吃蛇 带一点小小的API

开始先放代码

#include <stdio.h>
#include <graphics.h>
#include <conio.h>
#include <Windows.h>

#define BLONG 50
void InitData();
void JudgeAnd();
void Input();
void WithoutInput();
void execute();


int plat[BLONG][BLONG];
int SnakePosH;
int SnakePosL;
int direction;//上下左右 5 2 1 3
int SnakeLong;
int Grade;
int Foodi;
int Foodj;

void InitData()
{
	Grade = 0;
	plat[BLONG][BLONG] = { 0 };
	SnakePosH = BLONG / 2;
	SnakePosL = BLONG / 2;
	direction = 3;
	SnakeLong = 4;
	
	for (int i = 0; i < BLONG; i++)
	{
		for (int j = 0; j < BLONG; j++)
		{
			if (plat[i][j] > 0)//将蛇消失
				plat[i][j] = 0;
			else if (plat[i][j] == -2)//将食物消失
				plat[i][j] = 0;
		}
	}
	for (int i = 0; i < BLONG; i++)//定义边界
	{
		plat[i][0] = -1;
		plat[i][BLONG - 1] = -1;
	}
	for (int j = 0; j < BLONG; j++)
	{
		plat[0][j] = -1;
		plat[BLONG - 1][j] = -1;
	}

	for (int i = 0; i < SnakeLong; i++)//定义蛇
		plat[SnakePosH][SnakePosL - i] = 1 + i;

	Foodi = rand() % (BLONG - 2) + 2;//理论上 -2 +2可以是food出现的范围最大
	Foodj = rand() % (BLONG - 2) + 2;//因为随机表示从2到lie-2  因为1和lie-1都是边框 避免food出现在边框上
	plat[Foodi][Foodj] = -2;
	
}
void JudgeAnd()
{
	BeginBatchDraw();
	for (int i = 0; i < BLONG * 10; i+=10)
	{
		for (int j = 0; j < BLONG * 10; j+=10)
		{
		
			 if (plat[i / 10][j / 10] >= 1)
				solidrectangle(j, i, j + 10, i + 10);
			else if (plat[i / 10][j / 10] == -1)//墙
				solidrectangle(j, i, j + 10, i + 10);//这里用的是计算机坐标 和数组是相反的
			else if(plat[i / 10][j / 10] == -2)
				 solidrectangle(j, i, j + 10, i + 10);
			//FlushBatchDraw(j, i, j + 10, i + 10);
		}
		//printf("\n");
		
	}
	Sleep(25);
	EndBatchDraw();


}

void Input()
{
	char input;
	if (_kbhit())
	{
		input = _getch();
		if (input == '5' && direction != 2)//上
			direction = 5;
		else if (input == '2' && direction != 5)//下
			direction = 2;
		else if (input == '1' && direction != 3)//左
			direction = 1;
		else if (input == '3' && direction != 1)//右
			direction = 3;
	}
}

void WithoutInput()
{
	int OldHeadi, OldHeadj;
	int OldTaili, OldTailj;
	int NewHeadi, NewHeadj;
	int max = 0;
	for (int i = 0; i < BLONG; i++)
	{
		for (int j = 0; j < BLONG; j++)
		{
			if (plat[i][j] > 0)
			{
				plat[i][j]++;
				if (max < plat[i][j])
				{
					max = plat[i][j];//记录旧的蛇尾巴
					OldTaili = i;
					OldTailj = j;
				}
				if (plat[i][j] == 2)
				{
					OldHeadi = i;//记录旧的蛇头
					OldHeadj = j;
				}
			}
			

		}
	}
	if (direction == 5)//
	{
		
		NewHeadi = OldHeadi - 1;
		NewHeadj = OldHeadj;
		
	}
	else if (direction == 2)//
	{
		NewHeadi = OldHeadi + 1;
		NewHeadj = OldHeadj;
	}
	else if (direction == 1)
	{
		
		NewHeadi = OldHeadi;
		NewHeadj = OldHeadj - 1;
	}
	else if (direction == 3)
	{
		
		NewHeadi = OldHeadi;
		NewHeadj = OldHeadj + 1;
	}
	
	if (plat[NewHeadi][NewHeadj] == -2)//当前的food消失,产生新的食物
	{
		plat[Foodi][Foodj] = 0;
		Foodi = rand() % (BLONG - 5) + 2;
		Foodj = rand() % (BLONG - 5) + 2;
		plat[Foodi][Foodj] = -2;
		Grade++;

	}
	else
	{
		plat[OldTaili][OldTailj] = 0;//让尾巴为0,实现转向时不改变长度
	}
	
	if (plat[NewHeadi][NewHeadj] > 0 || plat[NewHeadi][NewHeadj] == -1)
	{            
		char str[100];
		sprintf_s(str, "您输了,是否继续。您的分数是:%d", Grade);
		if (IDYES == MessageBox(NULL, str, "贪吃蛇", MB_YESNO))
			execute();
		else
			exit(0);
	}
	else
	{
		plat[NewHeadi][NewHeadj] = 1;
	}
	
}

void execute()
{
	InitData();
	while (1)
	{

		JudgeAnd();
		WithoutInput();
		Input();
		cleardevice();

	}

}


int main()
{
	initgraph(BLONG * 10, BLONG * 10);
	if (IDNO == MessageBox(NULL, "是否开始", "贪吃蛇", MB_YESNO))
		exit(0);
	execute();
	_getch();
	closegraph();
	return 0;
}

代码也就两百多行

本次使用C语言

代码目标是利用easyx图形库做一个简单的贪吃蛇
先放一下最终代码图:
在这里插入图片描述

MessageBox函数

这个图片首先是初始化了窗口
定义的数组是长宽为50。做成画布大概是50*10的边长
这里第一个弹出的窗口就是最简单的API
利用了MessageBox函数
这个函数一共有四个形参
第一个是窗口句柄
第二个是窗口内信息
第三个是窗口名字
第四个是按钮类型

1.对话框的按钮类型
    MB_OK                          //"确定"
    MB_OKCANCEL                    //"确定" + "取消"
    MB_ABORTRETRYIGNORE            //"终止" + "重试" + "忽略"
    MB_YESNOCANCEL                 //"是"   + "否"   + "取消"
    MB_YESNO                       //"是"   + "否"
    MB_RETRYCANCEL                 //"重试" + "取消"

2.对话框的图标类型
    MB_ICONHAND                      //带有红X的错误/停止图标
    MB_ICONQUESTION                  //问号的询问图标
    MB_ICONEXCLAMATION               //黄色感叹号的警告图标
    MB_ICONASTERISK                  //带有蓝i的信息提示图标
这是资料整理来的

这是对贪吃蛇开始的和结束的准备工作

sprintf函数

然后就是sprintf函数 
这是一个类型转换函数
在vs2019的环境下
    char str[100];
		sprintf_s(str, "您输了,是否继续。您的分数是:%d", Grade);

分数是全局变量 在代码开头就已经定义了
利用sprintf函数 转换了分数的类型 把sprintf的函数中的第二个参数上的内容放到str[100]数组中

这是游戏结束时的图片在这里插入图片描述

准备工作基本完成

接下来就是代码:
头文件

#include <stdio.h>
#include <graphics.h>
#include <conio.h>
#include <Windows.h>

四个头文件
第一个在图形库中其实用不到
但是我写习惯了 便加上了
第二个是图形库头文件
第三个也用不到
但是在写代码的时候需要_getch()函数方便调试
第四个就是需要调用API 所以加上的

宏定义

#define BLONG 50

就是数组的边长
也便是画布的边长

函数申明

void InitData();
void JudgeAnd();
void Input();
void WithoutInput();
void execute();

这里是所有函数的申明
这里是避免函数在定义的时候顺序不当
因为在用到void execute();这个函数时需要在某些函数之前
但是我嫌麻烦就一次性把函数都申明了。

全局变量的申明

int plat[BLONG][BLONG];//地图
int SnakePosH;//蛇的位置
int SnakePosL;//蛇的位置
int direction;//上下左右 5 2 1 3
int SnakeLong;//蛇的长度
int Grade;//分数
int Foodi;//食物的位置
int Foodj;//食物的位置

这些变量必须是全局变量 因为在下面函数中需要改变变量的值 所以只有全局变量的作用域的范围广
全局变量是可以被本程序所有对象或函数引用。

代码中整数的代表

1便是蛇头
大于1的地方代码蛇身
-1代表地图的边界
-2代表食物

数据的初始化

void InitData()
{
	Grade = 0;
	plat[BLONG][BLONG] = { 0 };
	SnakePosH = BLONG / 2;
	SnakePosL = BLONG / 2;
	direction = 3;
	SnakeLong = 4;
	
	for (int i = 0; i < BLONG; i++)
	{
		for (int j = 0; j < BLONG; j++)
		{
			if (plat[i][j] > 0)//将蛇消失
				plat[i][j] = 0;
			else if (plat[i][j] == -2)//将食物消失
				plat[i][j] = 0;
		}
	}
	for (int i = 0; i < BLONG; i++)//定义边界
	{
		plat[i][0] = -1;
		plat[i][BLONG - 1] = -1;
	}
	for (int j = 0; j < BLONG; j++)
	{
		plat[0][j] = -1;
		plat[BLONG - 1][j] = -1;
	}

	for (int i = 0; i < SnakeLong; i++)//定义蛇
		plat[SnakePosH][SnakePosL - i] = 1 + i;

	Foodi = rand() % (BLONG - 2) + 2;//理论上 -2 +2可以是food出现的范围最大
	Foodj = rand() % (BLONG - 2) + 2;//因为随机表示从2到lie-2  因为1和lie-1都是边框 避免food出现在边框上
	plat[Foodi][Foodj] = -2;
	
}
  1. 函数第一把所有全局变量需要初始化的地方都初始化。
  2. 因为游戏结束后需要继续时。就要把所有的数据都都变成原来的数据。
	for (int i = 0; i < BLONG; i++)
   {
   	for (int j = 0; j < BLONG; j++)
   	{
   		if (plat[i][j] > 0)//将蛇消失
   			plat[i][j] = 0;
   		else if (plat[i][j] == -2)//将食物消失
   			plat[i][j] = 0;
   	}
   }

这里就是遍历数组 把数组不是0的地方都变成0,除了边界不处理 其实处理了也没有什么大不了的 因为代码下面都有对边界的初始化

  1. 接下来对地图的边界 食物刚开始的位置 蛇刚开始的长度和位置进行初始化
	for (int i = 0; i < BLONG; i++)//定义边界
   {
   	plat[i][0] = -1;
   	plat[i][BLONG - 1] = -1;
   }
   for (int j = 0; j < BLONG; j++)
   {
   	plat[0][j] = -1;
   	plat[BLONG - 1][j] = -1;
   }

   for (int i = 0; i < SnakeLong; i++)//定义蛇
   	plat[SnakePosH][SnakePosL - i] = 1 + i;

   Foodi = rand() % (BLONG - 2) + 2;//理论上 -2 +2可以是food出现的范围最大
   Foodj = rand() % (BLONG - 2) + 2;//因为随机表示从2到lie-2  因为1和lie-1都是边框 避免food出现在边框上
   plat[Foodi][Foodj] = -2;

随机函数

rand() rand()%(变量1) + 变量2
表示函数的返回值随机从变量1到变量2 包括变量1到变量2

easyx绘图函数(来自easyx帮助文档)

solidrectangle
这个函数用于画填充矩形(无边框)。

void solidrectangle(
    int left,
    int top,
    int right,
    int bottom
);参数:
left
矩形左部 x 坐标。
top
矩形上部 y 坐标。
right
矩形右部 x 坐标。
bottom
矩形下部 y 坐标。
返回值:
(无)
示例:
(无)

这个函数用于绘制边界 蛇头 蛇身 这样看起来比较单一 但是这个只是绘图 因为边界 蛇头 蛇身 的数值都不一样 所以想要改变样式只要更改绘图函数就行了。

批量绘制函数

BeginBatchDraw
这个函数用于开始批量绘图。执行后,任何绘图操作都将暂时不输出到屏幕上,直到执行 FlushBatchDraw 或 EndBatchDraw 才将之前的绘图输出。
void BeginBatchDraw();参数:
(无)
返回值:
(无)
示例:
以下代码实现一个圆从左向右移动,会有比较明显的闪烁。
请取消 main 函数中的三个注释,以实现批绘图功能,可以消除闪烁。
#include <graphics.h>
int main()
{
    initgraph(640,480);
    setlinecolor(WHITE);
    setfillcolor(RED);
    // BeginBatchDraw();
    for(int i=50; i<600; i++)
    {
        circle(i, 100, 40);
        floodfill(i, 100, WHITE);
        // FlushBatchDraw();
        Sleep(10);
        cleardevice();
    }
    // EndBatchDraw();
    closegraph();
}
EndBatchDraw
这个函数用于结束批量绘制,并执行未完成的绘制任务。

// 结束批量绘制,并执行未完成的绘制任务
void EndBatchDraw();// 结束批量绘制,并执行指定区域内未完成的绘制任务
void EndBatchDraw(
    int left,
    int top,
    int right,
    int bottom
);参数:
left
指定区域的左部 x 坐标。
top
指定区域的上部 y 坐标。
right
指定区域的右部 x 坐标。
bottom
指定区域的下部 y 坐标
返回值:
(无)
示例:
请参见 BeginBatchDraw 的示例。

这两个函数分别放在绘图函数的开头和结尾

FlushBatchDraw
这个函数用于执行未完成的绘制任务。

// 执行未完成的绘制任务
void FlushBatchDraw();// 执行指定区域内未完成的绘制任务
void FlushBatchDraw(
    int left,
    int top,
    int right,
    int bottom
); 参数:
left
指定区域的左部 x 坐标。
top
指定区域的上部 y 坐标。
right
指定区域的右部 x 坐标。
bottom
指定区域的下部 y 坐标。
返回值:
(无)
示例:
请参见 BeginBatchDraw 的示例。

这个函数可以画出没有画出的图
但是加上这个会使频闪更加严重
所以没有加上

输出地图

void JudgeAnd()
{
	BeginBatchDraw();
	for (int i = 0; i < BLONG * 10; i+=10)
	{
		for (int j = 0; j < BLONG * 10; j+=10)
		{
		
			 if (plat[i / 10][j / 10] >= 1)
				solidrectangle(j, i, j + 10, i + 10);
			else if (plat[i / 10][j / 10] == -1)//墙
				solidrectangle(j, i, j + 10, i + 10);//这里用的是计算机坐标 和数组是相反的
			else if(plat[i / 10][j / 10] == -2)
				 solidrectangle(j, i, j + 10, i + 10);
			//FlushBatchDraw(j, i, j + 10, i + 10);
		}
		//printf("\n");
		
	}
	Sleep(25);
	EndBatchDraw();

}

在画矩形的时候数据有些不一样 但是因为屏幕的坐标是从左上角开始的 但是数组和坐标是相反的 所以这个画矩形的函数的实参是和数组的参数相反的
因为地图是500*500 所以i 和 j 都是以10为一个单位增加的
所以在数组的时候 i和j都除以了10;
这就保证了地图的输出
刚开始这样只是输出了一个食物和蛇 但是它们是没有动的
那么就需要另外一个函数

输入函数

因为代码需要while(1)死循环 不断更新数据来输出地图
所以如果要输入 那么程序就必须暂停来获取用户输入
那么就不能达到我们的目的
所以我们需要一个非阻塞函数
_kbhit()
这个函数如果有输入就会返回真
所以代码如下:

void Input()
{
	char input;
	if (_kbhit())
	{
		input = _getch();
		if (input == '5' && direction != 2)//上
			direction = 5;
		else if (input == '2' && direction != 5)//下
			direction = 2;
		else if (input == '1' && direction != 3)//左
			direction = 1;
		else if (input == '3' && direction != 1)//右
			direction = 3;
	}
}

这个direction是表示方向
为了方便我把方向的 上下左右 定为小键盘里的 1 2 3 5
只要有输入就改变方向 当然保证方向不能与当前方向相反

与用户输入无关的函数(核心)

这个函数不断的更改数据
可以说是贪吃蛇里面最核心的函数

void WithoutInput()
{
   int OldHeadi, OldHeadj;
   int OldTaili, OldTailj;
   int NewHeadi, NewHeadj;
   int max = 0;
   for (int i = 0; i < BLONG; i++)
   {
   	for (int j = 0; j < BLONG; j++)
   	{
   		if (plat[i][j] > 0)
   		{
   			plat[i][j]++;
   			if (max < plat[i][j])
   			{
   				max = plat[i][j];//记录旧的蛇尾巴
   				OldTaili = i;
   				OldTailj = j;
   			}
   			if (plat[i][j] == 2)
   			{
   				OldHeadi = i;//记录旧的蛇头
   				OldHeadj = j;
   			}
   		}
   		

   	}
   }
   if (direction == 5)//
   {
   	
   	NewHeadi = OldHeadi - 1;
   	NewHeadj = OldHeadj;
   	
   }
   else if (direction == 2)//
   {
   	NewHeadi = OldHeadi + 1;
   	NewHeadj = OldHeadj;
   }
   else if (direction == 1)
   {
   	
   	NewHeadi = OldHeadi;
   	NewHeadj = OldHeadj - 1;
   }
   else if (direction == 3)
   {
   	
   	NewHeadi = OldHeadi;
   	NewHeadj = OldHeadj + 1;
   }
   
   if (plat[NewHeadi][NewHeadj] == -2)//当前的food消失,产生新的食物
   {
   	plat[Foodi][Foodj] = 0;
   	Foodi = rand() % (BLONG - 5) + 2;
   	Foodj = rand() % (BLONG - 5) + 2;
   	plat[Foodi][Foodj] = -2;
   	Grade++;

   }
   else
   {
   	plat[OldTaili][OldTailj] = 0;//让尾巴为0,实现转向时不改变长度
   }
   
   if (plat[NewHeadi][NewHeadj] > 0 || plat[NewHeadi][NewHeadj] == -1)
   {            
   	char str[100];
   	sprintf_s(str, "您输了,是否继续。您的分数是:%d", Grade);
   	if (IDYES == MessageBox(NULL, str, "贪吃蛇", MB_YESNO))
   		execute();
   	else
   		exit(0);
   }
   else
   {
   	plat[NewHeadi][NewHeadj] = 1;
   }
   
}
int OldHeadi, OldHeadj;
   int OldTaili, OldTailj;
   int NewHeadi, NewHeadj;
   int max = 0;

定义 旧的蛇头 旧的蛇尾 新的蛇头
这个蛇移动的原理是这样的:
在这里插入图片描述
图中没有画出边界
1 2 3 表示蛇头 蛇身
刚开始遍历数组 大于0的地方就是蛇 所以我们把大于0的地方都+1
于是就变成了这样:
在这里插入图片描述
然后遍历数组中 如果数组值为2 就记录这个数组的i和j 这个i
和j就放到旧的蛇头地方
新的蛇头需要输入来改变
其次的是蛇尾 因为移动的时候 蛇头会在2的周围增加
那么蛇的长度就增加了 那么蛇尾就需要消除
因此就需要记录蛇尾的位置 放到旧的蛇尾这个变量中
但是蛇尾其实是最大的数 那么遍历中 用max
如果max小于这个数组值 那么就让这个max变成这个数组值
同时 我们把数组值的i j都记录下来 那么并且这个循环只有在max最大的时候回停止 那么结束后max就是最大值 记录数组的i j
那么这个地方就记录好了

if (direction == 5)//
	{
		
		NewHeadi = OldHeadi - 1;
		NewHeadj = OldHeadj;
		
	}
	else if (direction == 2)//
	{
		NewHeadi = OldHeadi + 1;
		NewHeadj = OldHeadj;
	}
	else if (direction == 1)
	{
		
		NewHeadi = OldHeadi;
		NewHeadj = OldHeadj - 1;
	}
	else if (direction == 3)
	{
		
		NewHeadi = OldHeadi;
		NewHeadj = OldHeadj + 1;
	}

这是输入 判断上下左右
并把旧的蛇头修改 如果上 那么新的蛇头的i就-1;
以此类推
这样就记录了新的蛇头
到这个地方为止 都只是记录

if (plat[NewHeadi][NewHeadj] == -2)//当前的food消失,产生新的食物
	{
		plat[Foodi][Foodj] = 0;
		Foodi = rand() % (BLONG - 5) + 2;
		Foodj = rand() % (BLONG - 5) + 2;
		plat[Foodi][Foodj] = -2;
		Grade++;

	}
	else
	{
		plat[OldTaili][OldTailj] = 0;//让尾巴为0,实现转向时不改变长度
	}

判断新的蛇头的位置是不是在-2的地方 就是表示蛇头遇见了食物 那么蛇尾就没有再消失的必要 所以不把蛇尾消失 但是如果没有遇见食物 所以就让蛇尾消失 就保证了蛇的长度不变
最后如果食物吃到了 那么食物消失 就随机产生新的食物

接下在我们新的蛇头还没有产生
但是我们先判断有没有遇见边界 如果遇见了 游戏就输了
如果没有遇见 那么就让新的蛇头产生 便是赋值为1
那么整个代码基本上都完成了。
接下来就只是需要用另一个函数把这些函数都封装起来
那么就是我们的execute()函数

void execute()
{
	InitData();
	while (1)
	{

		JudgeAnd();
		WithoutInput();
		Input();
		cleardevice();

	}

}

那么回到上一个函数
在判断蛇遇见边界的时候 我们就利用API转换char类型值 输入分数 以及 告诉你输了 而且要不要继续
那么如果继续 我们就把函数execute()放上去
这个API的函数的返回值很特殊

Windows.h头文件自带一些宏定义
IDABORT:Abort 按钮被选中。
IDCANCEL:Cancel按钮被选中。
IDIGNORE:Ignore按钮被选中。
IDNO:NO按钮被选中。
IDOK:OK按钮被选中。
IDRETRY:RETRY按钮被选中。
IDYES:YES按钮被选中。
我们用IDYES代表选中了yes

那么我们就继续游戏
因为画图已经初始化了 不用再画 所以这个execute()函数中没有初始化画布的函数 把这个函数放到了主函数
如果取消 没那么exit(0)直接结束程序
这就是为什么全局变量没有直接初始化的原因
而且数据的初始化只需要一次就够了 所以没有放在while循环里面。

主函数部分

int main()
{
	initgraph(BLONG * 10, BLONG * 10);
	if (IDNO == MessageBox(NULL, "是否开始", "贪吃蛇", MB_YESNO))
		exit(0);
	execute();
	_getch();
	closegraph();
	return 0;
}

利用简单的API告诉我们是不是要开始
然后把execute();放上
就关闭画布
结束…

最后我们小店的镇店之图 :
在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值