贪吃蛇游戏(C语言实现)

这篇文章详细介绍了如何基于Easyx图形库和VisualStudio环境,通过编写一系列函数(如绘制网格、绘制蛇、移动蛇、改变方向等)来实现经典的贪吃蛇游戏。程序使用C++编写,包括键盘输入控制蛇的方向,生成食物,判断游戏结束等功能,并提供了完整的代码示例。
摘要由CSDN通过智能技术生成

 前言

本篇基于Esayx图形库的基础上,在Visual Studio编译器上运行,主要通过构建各个不同功能的函数(面向过程)来实现这款经典的小游戏———贪吃蛇

 一 主要函数

//该函数用于绘制出一个网格出来
void paintGrid();

//该函数用于绘制出一个蛇
void paintSnake(node* snake, int n);

//该函数将依次移动蛇节头并根据前进方向设置蛇头
//以此来控制蛇的移动
node snakeMove(node* snake, int length, int direction);

//该函数通过键盘输入的方式来调整蛇移动的方向
void changDirection(enum direction* p);

//该函数用于在画布中创建出一个食物类型
node creatFood(node* snake, int length);

//该函数用于在画布中生成一个食物
void paintFood(node* food);

//该函数用于判断游戏是否结束
bool isGameover(node* snake, int length);

//该函数用于将游戏数据初始化
void reset(node* snake, int* plength, enum direction* pd);

 二 对各个函数进行实现

1.定义结构体以及一些枚举变量、常量

//我们创建的画布大小是600*800大小的,将其分成宽为40的小矩形
#define NOOD_WIDTH 40 

//该枚举类型分别对应四个方向,用键盘上的wsad来表示上下左右
enum direction
{
	eUp,
	eDown,
	eLeft,
	eRight
};

//该结构node用于记录蛇的每一个节点
typedef struct
{
	int x;
	int y;
}node;

 2.//该函数用于绘制出一个网格出来
void paintGrid();

void paintGrid()
{
	setlinecolor(WHITE);
	for (int x = 0; x < 800; x += NOOD_WIDTH)//以这个宽度为间距进行画竖线
	{
		line(x, 0, x, 600);//画竖线
	}
	for (int y = 0; y < 600; y += NOOD_WIDTH)//以这个宽度为间距进行画横线

	{
		line(0, y, 800, y);//画横线
	}
}

3.//该函数用于绘制出一个蛇
void paintSnake(node* snake, int n);

void paintSnake(node* snake, int n)
{
	int left, top, right, bottom;
	for (int i = 0; i < n; i++)
	{
		left = snake[i].x * NOOD_WIDTH;//*(snake + i).x
		top = snake[i].y * NOOD_WIDTH;
		right = (snake[i].x + 1) * NOOD_WIDTH;
		bottom = (snake[i].y + 1) * NOOD_WIDTH;
		setlinecolor(BLACK);
		fillrectangle(left, top, right, bottom);
	}
}

 4.//该函数将依次移动蛇节头并根据前进方向设置蛇头
//以此来控制蛇的移动
node snakeMove(node* snake, int length, int direction);

node snakeMove(node* snake, int length, int direction)
{
	node tail = snake[length - 1];
	//用一个循环将数组中的元素从后往前依次进行覆盖
	for (int i = length - 1; i > 0; i--)
	{
		snake[i] = snake[i - 1];//将前一个节点覆盖后一个节点
	}
	node newhead = snake[0];
	if (direction == eUp)
	{
		newhead.y--;//在原蛇头的基础上进行修改
	}
	if (direction == eDown)
	{
		newhead.y++;
	}
	if (direction == eLeft)
	{
		newhead.x--;
	}
	if (direction == eRight)
	{
		newhead.x++;
	}
	snake[0] = newhead;//最后在把修改后的蛇头数据赋值给原数组的首元素
	return tail;
}

5. //该函数通过键盘输入的方式来调整蛇移动的方向
void changDirection(enum direction* p);

 

void changDirection(enum direction* p)
{
	if (_kbhit() != 0)
	{
		char c = _getch();
		switch (c)
		{
		case 'a':
			if(*p != eRight)
			    *p = eLeft;
			break;
		case 'd':
			if (*p != eLeft)
			    *p = eRight;
			break;
		case 'w':
			if (*p != eDown)
			    *p = eUp;
			break;
		case's':
			if (*p != eUp)
			    *p = eDown;
			break;
		}
	}
}

 6.//该函数用于在画布中创建出一个食物类型
node creatFood(node* snake, int length);

node creatFood(node* snake, int length)
{
	//先在画布圈定的范围之内随机生成一个食物点
	node food;
	//然后用一个死循环来对食物点进行检验(不能与蛇的身体进行重合)
	while (1)
	{
		food.x = rand() % (800 / NOOD_WIDTH);
		food.y = rand() % (600 / NOOD_WIDTH);
		int i = 0;
		for (i = 0; i < length; i++)
		{
			if (food.x == snake[i].x && food.y == snake[i].y)
				break;
		}
		if (i < length)
			continue;
		else
			break;
	}
	return food;
}

7.//该函数用于在画布中生成一个食物
void paintFood(node* food);

void paintFood(node* food)
{
	int left, top, right, bottom;
		left = food->x * NOOD_WIDTH;//*(snake + i).x
		top = food->y* NOOD_WIDTH;
		right = (food->x + 1) * NOOD_WIDTH;
		bottom = (food->y + 1) * NOOD_WIDTH;
		setfillcolor(YELLOW);
		solidrectangle(left, top, right, bottom);
		setfillcolor(WHITE);
}

 8.//该函数用于判断游戏是否结束
bool isGameover(node* snake, int length);

 

bool isGameover(node* snake, int length)
{
	if (snake[0].x < 0 || snake[0].x>800 / NOOD_WIDTH)
	{
		return true;
	}
	if (snake[0].y < 0 || snake[0].y>600 / NOOD_WIDTH)
	{
		return true;
	}
	for (int i = 1; i < length; i++)
	{
		if (snake[0].x == snake[i].x && snake[0].y == snake[i].y)
			return true;
	}
	return false;
}

9. //该函数用于将游戏数据初始化
void reset(node* snake, int* plength, enum direction* pd);

void reset(node* snake, int* plength, enum direction* pd)
{
	for (int i = 0; i < 5; i++)
	{
		for (int j = 5; j > 0; j--)
		{
			snake[i].x = j;
		}
		snake[i].y = 7;
	}
	*plength = 5;
	*pd = eRight;
}

三 主函数部分 

 

int main()
{
	initgraph(800, 600);
	setbkcolor(RGB(164, 225, 202));
	cleardevice();
	node snake[100] = { {5,7},{4,7},{3,7},{2,7},{1,7} };
	int length = 5;
	enum direction d = eRight;
	srand(unsigned int(time(NULL)));//用当前时间作为随机数种子
	node food = creatFood(snake,length);
	while (1)
	{
		cleardevice();
		paintGrid();
		paintSnake(snake, length);
		paintFood(&food);
		Sleep(500);

		changDirection(&d);
		node last_tail = snakeMove(snake, length, d);
		if (snake[0].x == food.x && snake[0].y == food.y)
		{
			if (length < 100)
			{
				snake[length] = last_tail;
				length++;
			}
			food = creatFood(snake, length);
		}
		if (isGameover(snake, length) == true)
		{
			reset(snake, &length, &d);
			food = creatFood(snake, length);
		}
	}

	getchar();
	closegraph();
	return 0;
}

 四 完整代码

完整代码如下: 

#include <easyx.h>
#include <stdio.h>
#include <conio.h>
#include <time.h>
#define NODE_WIDTH 40
// 节点
typedef struct {
int x;
int y;
}node;
enum direction
{
	eUp,
	eDown,
	eLeft,
	eRight
};
void paintGrid()
{
	setlinecolor(WHITE);
	for (int x = 0; x < 800; x += NOOD_WIDTH)
	{
		line(x, 0, x, 600);//画竖直线段
	}
	for (int y = 0; y < 600; y += NOOD_WIDTH)
	{
		line(0, y, 800, y);//画竖直线段
	}
}

void paintSnake(node* snake, int n)
{
	int left, top, right, bottom;
	for (int i = 0; i < n; i++)
	{
		left = snake[i].x * NOOD_WIDTH;//*(snake + i).x
		top = snake[i].y * NOOD_WIDTH;
		right = (snake[i].x + 1) * NOOD_WIDTH;
		bottom = (snake[i].y + 1) * NOOD_WIDTH;
		setlinecolor(BLACK);
		fillrectangle(left, top, right, bottom);
	}
}

node snakeMove(node* snake, int length, int direction)
{
	node tail = snake[length - 1];
	//用一个循环将数组中的元素从后往前依次进行覆盖
	for (int i = length - 1; i > 0; i--)
	{
		snake[i] = snake[i - 1];//将前一个节点覆盖后一个节点
	}
	node newhead = snake[0];
	if (direction == eUp)
	{
		newhead.y--;//在原蛇头的基础上进行修改
	}
	if (direction == eDown)
	{
		newhead.y++;
	}
	if (direction == eLeft)
	{
		newhead.x--;
	}
	if (direction == eRight)
	{
		newhead.x++;
	}
	snake[0] = newhead;//最后在把修改后的蛇头数据赋值给原数组的首元素
	return tail;
}

void changDirection(enum direction* p)
{
	if (_kbhit() != 0)
	{
		char c = _getch();
		switch (c)
		{
		case 'a':
			if(*p != eRight)
			    *p = eLeft;
			break;
		case 'd':
			if (*p != eLeft)
			    *p = eRight;
			break;
		case 'w':
			if (*p != eDown)
			    *p = eUp;
			break;
		case's':
			if (*p != eUp)
			    *p = eDown;
			break;
		}
	}
}

node creatFood(node* snake, int length)
{
	//先在画布圈定的范围之内随机生成一个食物点
	node food;
	//然后用一个死循环来对食物点进行检验(不能与蛇的身体进行重合)
	while (1)
	{
		food.x = rand() % (800 / NOOD_WIDTH);
		food.y = rand() % (600 / NOOD_WIDTH);
		int i = 0;
		for (i = 0; i < length; i++)
		{
			if (food.x == snake[i].x && food.y == snake[i].y)
				break;
		}
		if (i < length)
			continue;
		else
			break;
	}
	return food;
}

void paintFood(node* food)
{
	int left, top, right, bottom;
		left = food->x * NOOD_WIDTH;//*(snake + i).x
		top = food->y* NOOD_WIDTH;
		right = (food->x + 1) * NOOD_WIDTH;
		bottom = (food->y + 1) * NOOD_WIDTH;
		setfillcolor(YELLOW);
		solidrectangle(left, top, right, bottom);
		setfillcolor(WHITE);
}

bool isGameover(node* snake, int length)
{
	if (snake[0].x < 0 || snake[0].x>800 / NOOD_WIDTH)
	{
		return true;
	}
	if (snake[0].y < 0 || snake[0].y>600 / NOOD_WIDTH)
	{
		return true;
	}
	for (int i = 1; i < length; i++)
	{
		if (snake[0].x == snake[i].x && snake[0].y == snake[i].y)
			return true;
	}
	return false;
}

void reset(node* snake, int* plength, enum direction* pd)
{
	for (int i = 0; i < 5; i++)
	{
		for (int j = 5; j > 0; j--)
		{
			snake[i].x = j;
		}
		snake[i].y = 7;
	}
	*plength = 5;
	*pd = eRight;
}
int main()
{
	initgraph(800, 600);
	setbkcolor(RGB(164, 225, 202));
	cleardevice();
	node snake[100] = { {5,7},{4,7},{3,7},{2,7},{1,7} };
	int length = 5;
	enum direction d = eRight;
	srand(unsigned int(time(NULL)));//用当前时间作为随机数种子
	node food = creatFood(snake,length);
	while (1)
	{
		cleardevice();
		paintGrid();
		paintSnake(snake, length);
		paintFood(&food);
		Sleep(500);

		changDirection(&d);
		node last_tail = snakeMove(snake, length, d);
		if (snake[0].x == food.x && snake[0].y == food.y)
		{
			if (length < 100)
			{
				snake[length] = last_tail;
				length++;
			}
			food = creatFood(snake, length);
		}
		if (isGameover(snake, length) == true)
		{
			reset(snake, &length, &d);
			food = creatFood(snake, length);
		}
	}

	getchar();
	closegraph();
	return 0;
}

 

 

用windows api 做的贪吃蛇 #include #include"resource.h" #include"Node.h" #include #include TCHAR szAppname[] = TEXT("Snack_eat"); #define SIDE (x_Client/80) #define x_Client 800 #define y_Client 800 #define X_MAX 800-20-SIDE //点x的范围 #define Y_MAX 800-60-SIDE //点y的范围 #define TIME_ID 1 #define SECOND 100 #define NUM_POINT 10 //点的总个数 #define ADD_SCORE 10 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HWND hwnd; //窗口句柄 MSG msg; //消息 WNDCLASS wndclass; //窗口类 HACCEL hAccel;//加速键句柄 wndclass.style = CS_HREDRAW | CS_VREDRAW; //窗口的水平和垂直尺寸被改变时,窗口被重绘 wndclass.lpfnWndProc = WndProc; //窗口过程为WndProc函数 wndclass.cbClsExtra = 0; //预留额外空间 wndclass.cbWndExtra = 0; //预留额外空间 wndclass.hInstance = hInstance; //应用程序的实例句柄,WinMain的第一个参数 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); //设置图标 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); //载入预定义的鼠标指针 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //设置画刷 wndclass.lpszMenuName = szAppname; //设置菜单 wndclass.lpszClassName = szAppname; //设置窗口类的名字 if (!RegisterClass(&wndclass))//注册窗口类 { MessageBox(NULL, TEXT("这个程序需要windows NT!"), szAppname, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppname, TEXT("Snack_eat"),//CreateWindow函数调用时,WndProc将受到WM_CREATE WS_OVERLAPPEDWINDOW&~WS_THICKFRAME& ~WS_MAXIMIZEBOX,//普通的层叠窗口&禁止改变大小&禁止最大化 CW_USEDEFAULT, //初始x坐标(默认) CW_USEDEFAULT, //初始y坐标 x_Client, //初始x方向尺寸 770 y_Client, //初始y方向尺寸 750 NULL, //父窗口句柄 NULL, //窗口菜单句柄 hInstance, //程序实例句柄 WinMain函数中第二个参数 NULL); //创建参数 ShowWindow(hwnd, iCmdShow);//显示窗口,iCmdShow是WinMain的第四个参数,决定窗口在屏幕中的初始化显示形式,例:SW_SHOWNORMAL表示正常显示 UpdateWindow(hwnd);//使窗口客户区重绘,通过向WndProc发送一条WM_PAINT消息而完成的 hAccel = LoadAccelerators(hInstance, szAppname);//加载加速键 while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(hwnd, hAccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }/* while (GetMessage(&msg, NULL, 0, 0))//GetMessage函数从消息队列中得到消息,填充msg。如果msg.message等于WM_QUIT,返回0,否则返回非0 { TranslateMessage(&msg);//将msg返回给windows已进行某些键盘消息的转换 DispatchMessage(&msg);//将msg再次返回给windows }*/ return msg.wParam;//msg.wParam是PostQuitMessage函数的参数值,通常是0 } ...
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值