C++ 图形界面的贪吃蛇。

发布时间:2023年12月25日

#include <graphics.h>
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <conio.h>
#include <cstdio>
#include <time.h>

#define	Width 40		//宽40格
#define Height 30		//高30格
#define Size 20			//格子大小:20

int blocks[Width][Height] = { 0 };		//用以表示所有方格
char moveDir;			//表示运动方向
char oldmove;			//旧的移动方向
int fx, fy;				//food食物坐标
int addspeed = 15;		//控制初始速度
int failure = 0;		//用以判断游戏是否失败,失败赋值为 1
int score = 0;			//记录分数
int max = 0;			//记录最高分
int k = 1;				//奖励机制初始值
char num[20];			//输出分数数组
char best[20];			//最高分数组	

void initGame();
void show();			//图像绘制更新显示函数
void WithInput();		//与输入有关的更新
void WithoutInput();	//与输入无关的更新
void movesnake();		//蛇的移动函数
void readscore();		//读取分数
void writescore();		//写入分数
void ShowWord();		//界面文字
void drawmenu();		//主菜单
void gamestart();		//开始游戏
void seerule();			//查看规则
void back();			//退出游戏

int main() {
	drawmenu();
	return 0;
}

void initGame() {
	int i;
	initgraph(1000, 600);		//①初始化绘图窗口,格子个数乘格子大小即为长度
	setbkcolor(CYAN);		//设置背景颜色
	BeginBatchDraw();		//easyx中实现批绘图功能的函数,可以消除图像闪烁
	cleardevice();			//使用当前背景色清空绘图设备
	setlinecolor(WHITE);	//划线颜色为白色

	for (i = Size; i < Width * Size + 1; i += Size) {		//用for循环绘制多条线
		line(i, 0, i, Height * Size);				//竖线坐标从x1(i,0)-> y1(i,Height * Size)
	}
	for (i = Size; i < Height * Size; i += Size) {
		line(0, i, Width * Size, i);				//横线坐标从x2(0,i) -> y2(Width * Size, i)
	}

	moveDir = oldmove = 'd';			//初始化默认蛇行进方向朝右

	blocks[Width / 2][Height / 2] = 1;				/*正中间画蛇头,蛇为赋值的方格
													0则为空白区域*/
	for (i = 1; i < 4; i++) {
		blocks[Width / 2 - i][Height / 2] = i + 1;	//y坐标不变,更改x坐标,在蛇头后方画蛇身
	}

	srand(time(0));									//srand函数产生一个时间种子,从而达到真正的随机效果
	fx = rand() % (Width - 1) + 1;					//随机数除39取余并+1,则为食物所生成的格子x坐标
	fy = rand() % (Height - 1) + 1;					//随机数除取余并+1,则为食物所生成的格子y坐标
}

void show() {
	cleardevice();
	for (int i = 0; i < Width; i++) {				//遍历所有方格,更新蛇的颜色
		for (int j = 1; j < Height; j++) {
			if (blocks[i][j] != 0) {
				setfillcolor(HSVtoRGB(blocks[i][j] * 10, 0.9, 1));	//HSV转RGB颜色,色调H 0°->360°,此处设置每长一个蛇身,提升10°色调,饱和度0.9,明度1
			}
			else {
				setfillcolor(CYAN);					//否则更新为正常背景颜色
			}
			fillrectangle(i * Size, j * Size, (i + 1) * Size, (j + 1) * Size);		//填充,(参数为左上角和右下角坐标)
		}
	}
	setfillcolor(LIGHTGREEN);						//食物颜色
	fillrectangle(fx * Size, fy * Size, (fx + 1) * Size, (fy + 1) * Size);

	ShowWord();

	FlushBatchDraw();				/*BeginBatchDraw执行后,任何绘图操作都将暂时不输出到绘图窗口上
								直到执行 FlushBatchDraw 或 EndBatchDraw 才将之前的绘图输出。*/
}

void ShowWord() {
	if (failure) {
		settextcolor(LIGHTRED);						//设置字体颜色
		settextstyle(80, 0, _T("宋体"));				//设置字体样式
		setbkmode(TRANSPARENT);
		outtextxy(240, 220, "游戏失败");
		settextstyle(40, 0, _T("宋体"));
		sprintf(num, "%d", score);						//将int转换为char并存入num中
		outtextxy(260, 360, "分数:");					//显示的字体及坐标
		outtextxy(380, 360, num);
	}
	else {												//②
		settextcolor(BLACK);							//设置分数颜色
		settextstyle(25, 0, _T("隶书"));				//设置字体样式
		setbkmode(TRANSPARENT);							//设置当前设备图案填充和文字输出时的背景模式,THANSPARENT为透明背景
		sprintf(num, "%d", score);						//将int转换为char并存入num中
		outtextxy(860, 40, "分数:");					//显示的字体及坐标
		outtextxy(940, 42, num);
		outtextxy(845, 73, "最高分:");
		sprintf(best, "%d", max);
		outtextxy(940, 75, best);
	}
}
void readscore() {										//③
	FILE* fpread = fopen("score.txt", "r");				//以只读的方式打开文件
	if (fpread == NULL) {
		return;
	}
	fscanf(fpread, "%d", &max);
	fclose(fpread);
}

void writescore() {
	FILE* fpWrite = fopen("score.txt", "w");			//"w"打开并写入文本文件.(若不存在则新建,反之,则从头写,覆盖原内容)
	if (fpWrite == NULL) {
		return;
	}
	fprintf(fpWrite, "%d", score);
	fclose(fpWrite);
}

void WithInput() {
	if (_kbhit() && failure == 0) {
		//键盘输入控制蛇的方向
		char input = _getch();

		if (input == 'a' || input == 's' || input == 'd' || input == 'w') {
			moveDir = input;
			//设定规则:当向某一方向行动时,按下反向键不起作用(防止误触导致判断为咬到蛇身游戏失败),强制将按下的反向键变为当前运动方向
			if (moveDir == 'a' && oldmove == 'd') {
				moveDir = 'd';
			}
			else if (moveDir == 's' && oldmove == 'w') {
				moveDir = 'w';
			}
			else if (moveDir == 'd' && oldmove == 'a') {
				moveDir = 'a';
			}
			else if (moveDir == 'w' && oldmove == 's') {
				moveDir = 's';
			}
			else {
				oldmove = input;
			}
			movesnake();
		}
	}
}

void WithoutInput() {

	if (failure) {				//先判断是否失败,失败则返回
		return;
	}
	//Sleep(100);				//更新速度控制蛇的移动速度,但使用sleep会产生键盘控制与画面的延迟,所以不使用
	//movesnake();
	static int speed = 1;		//设置一个静态变量speed,利用循环特定次数来触发movesnake函数
	speed++;
	if (speed == addspeed) {	//每15帧移动一次,同时实现长按方向键加快蛇的移动速度
		movesnake();
		speed = 1;
	}
}

void movesnake() {
	int i, j;
	//对蛇进行更新,蛇每个格数值都+1,以便后面预备一个数值为1的新蛇头,当吃到食物时,新蛇头改变颜色加入蛇身中
	for (i = 0; i < Width; i++) {
		for (j = 0; j < Height; j++) {
			if (blocks[i][j] != 0) {
				blocks[i][j]++;
			}
		}
	}

	int oldhx, oldhy, oldtx, oldty;			//旧蛇头和旧蛇尾的坐标
	int tailblock = 0;
	for (i = 0; i < Width; i++) {
		for (j = 0; j < Height; j++) {
			if (tailblock < blocks[i][j]) {
				tailblock = blocks[i][j];	//遍历找出最大值即为蛇尾
				oldtx = i;
				oldty = j;
			}
			if (blocks[i][j] == 2) {			//寻找旧蛇头
				oldhx = i;
				oldhy = j;
			}
		}
	}
	//根据蛇的运动方向来赋值新蛇头的xy坐标
	int newhx = oldhx, newhy = oldhy;		//定义新蛇头
	switch (moveDir) {
	case'a':
		newhx -= 1;
		break;
	case's':
		newhy += 1;
		break;
	case'd':
		newhx += 1;
		break;
	case'w':
		newhy -= 1;
		break;
	}
	//判断游戏是否失败
	if (newhx >= Width || newhx < 0 || newhy >= Height || newhy < 0 || blocks[newhx][newhy] != 0) {
		failure = 1;
		return;
	}
	//令蛇头方格值为1
	blocks[newhx][newhy] = 1;
	//对吃到食物以及蛇尾的处理				⑤
	if (newhx == fx && newhy == fy) {		//吃到食物,重新随机数生成新食物坐标
		score = score + k;					//吃到食物分数+k
		if (score % 2 == 0) {				//通过判断吃食物的数量来叠加奖励分
			k = k + 1;
		}

		if (score > max) {					//与最高分判断,条件成立才能覆盖最高分
			writescore();					//写入最高分
		}
		//④
		if (score) {						//“加速”判断,通过判断分数增大的取值,加快蛇的运动速度,score不为0时进入该判断语句
			if (score % 2 == 0) {			//便于测试结果明显,设置每吃2个食物,蛇速度加快1帧
				addspeed--;
			}
		}
		fx = rand() % (Width - 1) + 1;
		fy = rand() % (Height - 1) + 1;
	}
	else {									//未吃到食物
		blocks[oldtx][oldty] = 0;
	}


}

void drawmenu() {
	MOUSEMSG m;					//定义结构体鼠标m
	initgraph(600, 600);		//定义画布大小
	setbkcolor(CYAN);			//设置背景颜色
	cleardevice();				//使用当前背景色清空绘图设备,类似system(cls)的刷新
	setfillcolor(WHITE);	//方框颜色

	fillrectangle(140, 50, 460, 170);			//矩形左上角xy坐标和右下角xy坐标
	fillrectangle(120, 220, 480, 350);			//矩形左上角xy坐标和右下角xy坐标
	fillrectangle(100, 400, 500, 540);			//矩形左上角xy坐标和右下角xy坐标

	settextcolor(BLACK);						//设置字体颜色
	settextstyle(50, 0, _T("楷体"));			//设置字体格式
	setbkmode(TRANSPARENT);						//去除文字背景

	outtextxy(200, 85, "开始游戏");				//文字输出位置的xy坐标
	outtextxy(200, 260, "查看规则");
	outtextxy(200, 445, "退出游戏");

	while (1) {
		m = GetMouseMsg();						//获取一个鼠标消息
		if (m.x >= 140 && m.x <= 460 && m.y >= 50 && m.y <= 170) {		//判断鼠标点击的位置
			if (m.uMsg == WM_LBUTTONDOWN) {		//当鼠标按下左键
				gamestart();					//开始游戏
			}
		}
		else if (m.x >= 120 && m.x <= 480 && m.y >= 220 && m.y <= 350) {
			if (m.uMsg == WM_LBUTTONDOWN) {		//当鼠标按下左键
				seerule();					//查看规则
			}
		}
		else if (m.x >= 100 && m.x <= 500 && m.y >= 400 && m.y <= 540) {
			if (m.uMsg == WM_LBUTTONDOWN) {		//当鼠标按下左键
				back();					//关闭菜单退出游戏
			}
		}
	}
}

void gamestart() {
	readscore();		//读取最高分
	initGame();			//游戏初始化
	while (1) {			//循环进行界面实时刷新,直至游戏判定失败
		show();			//界面显示
		WithoutInput();	//先调用与输入无关的更新
		WithInput();
	}
}

void seerule() {
	MOUSEMSG m;					//定义结构体鼠标m

	readscore();				//读取历史最高分

	initgraph(600, 600);		//定义画布大小
	setbkcolor(CYAN);			//设置背景颜色
	cleardevice();				//使用当前背景色清空绘图设备,类似system(cls)的刷新
	setfillcolor(WHITE);	//方框颜色

	fillrectangle(100, 400, 500, 540);			//矩形左上角xy坐标和右下角xy坐标

	settextcolor(BLACK);						//设置字体颜色
	settextstyle(50, 0, _T("楷体"));			//设置字体格式
	setbkmode(TRANSPARENT);						//去除文字背景
	outtextxy(200, 445, "返回菜单");

	settextcolor(BLACK);						//设置字体颜色
	settextstyle(30, 0, _T("楷体"));			//设置字体格式
	setbkmode(TRANSPARENT);						//去除文字背景
	outtextxy(45, 30, "WASD控制蛇移动,长按方向键可加速移动");
	outtextxy(45, 80, "碰到墙壁游戏失败");
	outtextxy(45, 130, "吃的食物越多,分数奖励越高");
	outtextxy(45, 180, "历史最高分:");
	sprintf(best, "%d", max);
	outtextxy(220, 180, best);



	while (1) {
		m = GetMouseMsg();						//获取一个鼠标消息
		if (m.x >= 100 && m.x <= 500 && m.y >= 400 && m.y <= 540) {
			if (m.uMsg == WM_LBUTTONDOWN) {		//当鼠标按下左键
				drawmenu();					//返回主菜单
			}
		}
	}
}

void back() {
	closegraph();		//关闭画面
	exit(0);			//退出程序
}

文章来源:https://blog.csdn.net/laocooon/article/details/135200240
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。