欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Windows下基于EasyX的带存档的三种模式贪吃蛇游戏

程序员文章站 2022-04-07 17:22:45
...

一、游戏窗口

PS:此游戏为c语言开发,因EasyX库需要.cpp文件才能编译,所以工程文件后缀为.cpp,并未使用cpp的语法。

1.1选择游戏模式窗口图形

Windows下基于EasyX的带存档的三种模式贪吃蛇游戏

1.2有屏障模式窗口图形

Windows下基于EasyX的带存档的三种模式贪吃蛇游戏

1.3无屏障模式游戏难度选择窗口图形

Windows下基于EasyX的带存档的三种模式贪吃蛇游戏

1.4无屏障经典模式游戏窗口图形

Windows下基于EasyX的带存档的三种模式贪吃蛇游戏

1.5无屏障迷宫模式游戏窗口图形

Windows下基于EasyX的带存档的三种模式贪吃蛇游戏

二、游戏源码

将tcs.cpp、tcs.h、main.cpp文件添加到工程里

2.1 tcs.cpp

#include "tcs.h"

#define  L  20 //地图的长  
#define  H  20  //地图的高  
int GameMap[L + 2][H + 2] = { 0 };   //游戏地图  
int  key;  //按键保存  
int  sum = 1;  //蛇的长度, 游戏结束(自吃或碰墙)  
int  dx[4] = { -1, 1, 0, 0 };  //左、右、上、下的方向  
int  dy[4] = { 0, 0, -1, 1 };
int res;//模式选择结果
int score = 0;//总分数
int pauseflag = 0;//暂停标志  pauseflag为1则暂停
int resist_time = -1;//加分食物剩余时间
int parfoodx, parfoody;//随机加分食物位置坐标

#define R     10   //显示的圆的半径
#define Wall  4   //墙
#define Shead 3  //蛇头  
#define Sbody 2  //蛇身  
#define Sfood 1  //食物  
#define Sfood_par -1//特殊加分食物
#define Snode 0  //在地图中为空
#define GameLevel 5//GameLevel越小速度越快   每吃到GameLevel个食物游戏速度增加

struct Snake   //蛇的每个节点的数据类型  
{
	int x, y;  //蛇的每个节点在地图中的位置  
	int now;   //保存当前节点的方向, 0,1,2,3分别为左右上下  
}Snake[L * H];


LRESULT CALLBACK CBTHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	HWND hwnd = (HWND)wParam;
	if (nCode == HCBT_ACTIVATE)
	{
		if (nCode == HCBT_ACTIVATE)
		{
			SetDlgItemText((HWND)wParam, IDYES, "&继续");
			SetDlgItemText((HWND)wParam, IDNO, "&存档并退出");
			SetDlgItemText((HWND)wParam, IDOK, "&经典");
			SetDlgItemText((HWND)wParam, IDCANCEL, "&迷宫");
			SetDlgItemText((HWND)wParam, IDABORT, "&有屏障");
			SetDlgItemText((HWND)wParam, IDRETRY, "&无屏障");
			SetDlgItemText((HWND)wParam, IDIGNORE, "&读档并开始");
		}
	}
	return 0;
}
int Msg(HWND hwnd, TCHAR *szText, TCHAR *szCaption, UINT uType)
{
	int ret;
	HHOOK hHook = SetWindowsHookEx(WH_CBT, CBTHookProc, NULL, GetCurrentThreadId());
	ret = MessageBox(hwnd, szText, szCaption, uType);
	UnhookWindowsHookEx(hHook);
	return ret;
}


void randfood_resisttime(void *)
{
	while (1)
	{	
		Sleep(1000);
		if (pauseflag == 0)
		{
			if (resist_time == 0)
			{
				GameMap[parfoodx][parfoody] = Snode;
				resist_time = -1;
				Show_Map();
			}
			if (resist_time > 0)
				resist_time--;
		}		
	}
}
void Create_Food(int mod)
{
	srand((unsigned int)time(NULL));
	if (mod == 0)
	{
		int foodx, foody;		
		while (1)
		{
			foodx = rand() % (L + 2);
			foody = rand() % (H + 2);
			if (GameMap[foodx][foody] == Snode)  //在地图中为空
			{
				GameMap[foodx][foody] = Sfood;
				break;
			}
		}
	}
	else if(mod == 1)
	{
		while (1)
		{
			parfoodx = rand() % (L + 2);
			parfoody = rand() % (H + 2);
			if (GameMap[parfoodx][parfoody] == Snode)  //在地图中为空
			{
				GameMap[parfoodx][parfoody] = Sfood_par;
				break;
			}
		}
		resist_time = 7 - getscorestep()/2;
	}
	Show_Map();
}
void Show_Map()
{
	int i, j;
	for (i = 0; i < L + 2; i++)   //地图显示  
	{
		for (j = 0; j < H + 2; j++)
		{
			setcolor(getbkcolor());			//取消圆的边缘
			if (GameMap[i][j] == Wall)
				setfillcolor(RED);
			else if (GameMap[i][j] == Shead)
				setfillcolor(YELLOW);
			else if (GameMap[i][j] == Sbody)
				setfillcolor(BLUE);
			else if (GameMap[i][j] == Sfood)
				setfillcolor(GREEN);
			else if (GameMap[i][j] == Sfood_par)
				setfillcolor(CYAN);
			else if (GameMap[i][j] == Snode)
				setfillcolor(BLACK);

			if (GameMap[i][j] == Wall)
				fillrectangle(i * 2 * R, j * 2 * R, (i*2+2)*R,(j*2+2) * R);//墙为方块
			else
				fillcircle(i * 2 * R + R, j * 2 * R + R,R);//其余显示为圆
			
		}
	}
	Show_Score();
}
void Show_Score()//显示分数
{
	setcolor(WHITE);
	char string[60];
	if (resist_time <= 0)	
		sprintf(string, "得分:%d 食物:%d/100", score, sum - 1);	
	else
		sprintf(string, "%ds后消失 得分:%d 食物:%d/100", resist_time, score, sum - 1);
	settextstyle(24, 0, _T("宋体"));//设置字体类型
	outtextxy(0, (H + 2) * 2 * R + 10, _T("                                                                                                                    "));
	outtextxy(0, (H + 2) * 2 * R + 10, string);
}
void Button()  //取出按键,并判断方向  
{
	if (_kbhit() != 0) //检查当前是否有键盘输入,若有则返回一个非0值,否则返回0  
	{
		if (GetAsyncKeyState(VK_SPACE) & 0x8000)//按空格键暂停	
			pauseflag = 1 - pauseflag;
		
		if (GetAsyncKeyState(VK_ESCAPE) & 0x8000)//按ESC存档	
		{
			pauseflag = 1 - pauseflag;
			int saveres = MessageBoxA(0, "请选择是否存档", "贪吃蛇", MB_OKCANCEL|| MB_SYSTEMMODAL);
			if (saveres == IDOK)//选择确定
				Save_game();			
			else if (saveres == IDCANCEL)
				pauseflag = 1 - pauseflag;
		}
			
		while (_kbhit() != 0)  //可能存在多个按键,要全部取完,以最后一个为主  
			key = _getch(); //将按键从控制台中取出并保存到key中

		if (sum == 1)//没有蛇身时随意改变方向
		{
			switch (key)
			{
			case 75: //左	
				Snake[0].now = 0;
				break;
			case 77: //右 
				Snake[0].now = 1;
				break;
			case 72: //上
				Snake[0].now = 2;
				break;
			case 80: //下  
				Snake[0].now = 3;
				break;
			}
		}
		else
		{
			switch (key)//有蛇身时
			{
			case 75: //左	
				if (Snake[0].now == 1);//原来方向为右 则按下左键无效
				else
					Snake[0].now = 0;
				break;
			case 77: //右 
				if (Snake[0].now == 0);//原来方向向左 则按下右键无效
				else
					Snake[0].now = 1;
				break;
			case 72: //上
				if (Snake[0].now == 3);//原来方向向下 则按上键无效
				else
					Snake[0].now = 2;
				break;
			case 80: //下  
				if (Snake[0].now == 2);//原来方向向上 则按下下键无效	
				else
					Snake[0].now = 3;
				break;
			}
		}
	}
}
void Check_Head(int x, int y)  //检查蛇头移动后的位置情况  
{
	if (GameMap[Snake[0].x][Snake[0].y] == Snode)  //地图位置为空  
		GameMap[Snake[0].x][Snake[0].y] = Shead;//设置为蛇头
	else if (GameMap[Snake[0].x][Snake[0].y] == Sfood_par)
	{
		GameMap[Snake[0].x][Snake[0].y] = Shead;//设置为蛇头
		Snake[sum].x = x;   //新增加的蛇身为蛇头后面的那个  
		Snake[sum].y = y;
		Snake[sum].now = Snake[0].now;
		GameMap[x][y] = Sbody;
		sum++;//蛇身增长
		score += resist_time * getscorestep();//吃到食物总分增加	
		resist_time = -1;
	}
	else if (GameMap[Snake[0].x][Snake[0].y] == Sfood )  //地图位置为食物  
	{
		GameMap[Snake[0].x][Snake[0].y] = Shead;//设置为蛇头
		Snake[sum].x = x;   //新增加的蛇身为蛇头后面的那个  
		Snake[sum].y = y;
		Snake[sum].now = Snake[0].now;
		GameMap[x][y] = Sbody;
		sum++;//蛇身增长
		if ((sum - 1) % 6 == 0) 
		{
			Create_Food(1);	
		} 
		Create_Food(0);  //食物吃完了马上再产生一个食物
		score += getscorestep();//吃到食物总分增加	
		if ((sum - 1) == 100)//判断是否已达到最高分
		{
			Show_Map();
			char str[50] = { 0 };
			sprintf(str, "已达到最高分,最终得分:%d。是否重新开始?", score);
			int res = MessageBoxA(0, str, "贪吃蛇", MB_YESNO);
			closegraph();          // 关闭绘图窗口
			if (res == IDYES) 	StartGame();
			else  exit(0);
		}
	}
	else//否则游戏结束
	{
		char str[50] = { 0 };
		sprintf(str, "游戏结束,最终得分:%d。是否重新开始?", score);
		//mciSendString(_T("close mymusic"), NULL, 0, NULL);
		int res = MessageBoxA(0, str, "贪吃蛇", MB_YESNO);
		closegraph();          // 关闭绘图窗口
		if (res == IDYES) 	StartGame();
		else  exit(0);
	}
}
void Move()   //蛇的移动  
{
	int i, x, y;
	int t = sum;  //保存当前蛇的长度  
	//记录当前蛇头的位置,并设置为空,蛇头先移动  
	x = Snake[0].x;
	y = Snake[0].y;
	GameMap[x][y] = Snode;
	Snake[0].x = Snake[0].x + dx[Snake[0].now];
	Snake[0].y = Snake[0].y + dy[Snake[0].now];

	if (res == IDRETRY)//无屏障模式
	{
		if (Snake[0].x < 0)
			Snake[0].x = L + 1;
		if (Snake[0].x > L + 1)
			Snake[0].x = 0;
		if (Snake[0].y < 0)
			Snake[0].y = H + 1;
		if (Snake[0].y > H + 1)
			Snake[0].y = 0;
	}

	Check_Head(x, y);  //蛇头移动后的位置情况, 参数为之前蛇头的位置 
	if (sum == t)  //未吃到食物  蛇身移动 
	{
		for (i = 1; i < sum; i++)  //蛇尾节点向前移动,前一个节点作为参照  
		{
			if (i == 1)
				GameMap[Snake[i].x][Snake[i].y] = Snode; //蛇尾为空   

			if (i == sum - 1)  //蛇头后面的蛇身节点  特殊处理  
			{
				Snake[i].x = x;
				Snake[i].y = y;
				Snake[i].now = Snake[0].now;
			}
			else   //其他蛇身即走到前一个蛇身位置  
			{
				Snake[i].x = Snake[i + 1].x;
				Snake[i].y = Snake[i + 1].y;
				Snake[i].now = Snake[i + 1].now;
			}
			GameMap[Snake[i].x][Snake[i].y] = Sbody; //移动后要置为蛇身   
		}
	}
}
int gamespeed()
{
	if (sum >= GameLevel * 1 && sum < GameLevel * 2)
		return  GameLevel * 52;
	else if (sum >= GameLevel * 2 && sum < GameLevel * 3)
		return GameLevel * 44;
	else if (sum >= GameLevel * 3 && sum < GameLevel * 4)
		return GameLevel * 36;
	else if (sum >= GameLevel * 4 && sum < GameLevel * 5)
		return GameLevel * 28;
	else if (sum >= GameLevel * 5 && sum < GameLevel * 6)
		return GameLevel * 20;
	else if (sum >= GameLevel * 6)
		return GameLevel * 14;

	else return GameLevel * 60;
}
int getscorestep()
{
	if (sum >= GameLevel * 1 && sum < GameLevel * 2)
		return  2;
	else if (sum >= GameLevel * 2 && sum < GameLevel * 3)
		return 3;
	else if (sum >= GameLevel * 3 && sum < GameLevel * 4)
		return 4;
	else if (sum >= GameLevel * 4 && sum < GameLevel * 5)
		return 5;
	else if (sum >= GameLevel * 5 && sum < GameLevel * 6)
		return 6;
	else if (sum >= GameLevel * 6)
		return 7;

	else return 1;
}
void OnInit()//初始化所有参数
{
	for (int i = 0; i < L + 2; i++)//游戏地图  
	{
		for (int j = 0; j < H + 2; j++)
		{
			GameMap[i][j] = 0;
			if (i * j <= L * H - 1)
			{
				Snake[i * j].x = 0;
				Snake[i * j].y = 0;
				Snake[i * j].now = 0;
			}
		}
	}
	sum = 1;//蛇的长度  
	res = -1;//BGM选择
	score = 0;//总分清零
}
void Cre_Hard_wallpos()
{
	int tempnum = rand() % 8;//留一个屏障入口
	for (int i = 0; i <= H / 3 + 1; i++)
	{
		if (tempnum != 0) GameMap[L / 3 + 1][i] = Wall;
		if (tempnum != 1) GameMap[L - L / 3][i] = Wall;
	}
	for (int i = H + 1; i >= H - (H / 3); i--)
	{
		if (tempnum != 2) GameMap[L / 3 + 1][i] = Wall;
		if (tempnum != 3) GameMap[L - L / 3][i] = Wall;
	}
	for (int i = 0; i <= L / 3 + 1; i++)
	{
		if (tempnum != 4) GameMap[i][H / 3 + 1] = Wall;
		if (tempnum != 5) GameMap[i][H - H / 3] = Wall;
	}
	for (int i = L + 1; i >= L - (L / 3); i--)
	{
		if (tempnum != 6) GameMap[i][H / 3 + 1] = Wall;
		if (tempnum != 7) GameMap[i][H - H / 3] = Wall;
	}
}
void Creat_game()
{
	int hx, hy;
	while (1)
	{
		int flag = 1;
		hx = rand() % (L / 2) + L / 4;   //产生蛇头  
		hy = rand() % (H / 2) + H / 4;
		for (int i = 0; i < 3; i++)
		{
			if (GameMap[hx + i][hy] != Snode)//连续两个位置在地图中不都为空			
				flag = 0;
		}
		if (flag)
		{
			GameMap[hx][hy] = Shead;
			break;
		}
	}
	Snake[0].x = hx;
	Snake[0].y = hy;
	Snake[0].now = 1;//初始状态向右运动
	Create_Food(0);   //随机产生食物 
}
void Save_game()//存档
{
	char str[20];
	sprintf(str, "del memory.txt");
	system(str);//删除已有存档
	FILE  *mem;
	if ((mem = fopen("memory.txt", "w+")) == NULL)//创建失败则继续游戏
	{
		MessageBoxA(0, "存档失败", "贪吃蛇", MB_OK || MB_SYSTEMMODAL);
		pauseflag = 1 - pauseflag;//游戏继续
	}
	else
	{
		//保存蛇身长度
		fwrite(&sum, sizeof(sum), 1, mem);
		//保存模式
		fwrite(&res, sizeof(res), 1, mem);
		//保存分数
		fwrite(&score, sizeof(score), 1, mem);
		//保存加分食物剩余时间
		fwrite(&resist_time, sizeof(resist_time), 1, mem);
		//保存加分食物坐标
		fwrite(&parfoodx, sizeof(parfoodx), 1, mem);
		fwrite(&parfoody, sizeof(parfoody), 1, mem);
		
		//保存地图
		for (int i = 0; i < L + 2; i++)
			for (int j = 0; j < H + 2; j++)
				fwrite(&GameMap[i][j], sizeof(GameMap[i][j]), 1, mem);
		//保存蛇坐标
		for (int i = 0; i < L * H; i++)
		{
			fwrite(&Snake[i].x, sizeof(Snake[i].x), 1, mem);
			fwrite(&Snake[i].y, sizeof(Snake[i].y), 1, mem);
			fwrite(&Snake[i].now, sizeof(Snake[i].now), 1, mem);
		}
		fclose(mem);

		int memres = MessageBoxA(0, "存档完成,是否退出", "贪吃蛇", MB_OKCANCEL || MB_SYSTEMMODAL);
		if (memres == IDOK)//选择确定
		{
			closegraph();
			exit(0);
		}		
		else if (memres == IDCANCEL)
			pauseflag = 1 - pauseflag;
	}
}
void Read_game()//读取游戏存档
{
	FILE  *mem;
	if ((mem = fopen("memory.txt", "r")) == NULL)
	{
		MessageBoxA(0, "没有存档,请重新选择", "贪吃蛇", MB_OK || MB_SYSTEMMODAL);
		StartGame();
	}
	else
	{
		//保存蛇身长度
		fread(&sum, sizeof(sum), 1, mem);
		//保存模式
		fread(&res, sizeof(res), 1, mem);
		//保存分数
		fread(&score, sizeof(score), 1, mem);
		//保存加分食物剩余时间
		fread(&resist_time, sizeof(resist_time), 1, mem);
		//保存加分食物坐标
		fread(&parfoodx, sizeof(parfoodx), 1, mem);
		fread(&parfoody, sizeof(parfoody), 1, mem);

		//保存地图
		for (int i = 0; i < L + 2; i++)
			for (int j = 0; j < H + 2; j++)
				fread(&GameMap[i][j], sizeof(GameMap[i][j]), 1, mem);
		//保存蛇坐标
		for (int i = 0; i < L * H; i++)
		{
			fread(&Snake[i].x, sizeof(Snake[i].x), 1, mem);
			fread(&Snake[i].y, sizeof(Snake[i].y), 1, mem);
			fread(&Snake[i].now, sizeof(Snake[i].now), 1, mem);
		}
		fclose(mem);
	}
}
void StartGame()//开始游戏
{
	OnInit();//初始化
	srand((unsigned int)time(NULL));
	initgraph((L + 2) * 2 * R, (H + 2) * 2 * R + 40);		//初始绘图窗口
	line(0, (H + 2) * 2 * R, (L + 2) * 2 * R, (H + 2) * 2 * R);//画线
	res = Msg(0, "请选择模式", "贪吃蛇", MB_ABORTRETRYIGNORE | MB_SYSTEMMODAL);

	if (res == IDABORT)//有屏障模式
		for (int i = 0; i < L + 2; i++)   //设置墙的位置
			for (int j = 0; j < H + 2; j++)
				if (i == 0 || i == L + 1 || j == 0 || j == H + 1)
					GameMap[i][j] = Wall;

	if (res == IDRETRY)//无屏障模式
	{
		int msgres = Msg(0, "请选择游戏难度", "贪吃蛇", MB_OKCANCEL | MB_SYSTEMMODAL);
		if (msgres == IDCANCEL)
		{
			Cre_Hard_wallpos();//产生墙壁
		}
	}	
	if (res == IDIGNORE)//读档开始
	{
		Read_game();//读档
	}
	else
	{
		Creat_game();//产生游戏要素
	}	
	Show_Map();//刷新显示地图
	while (1)
	{
		Button();
		if (pauseflag == 0)
		{		
			Move();
			Show_Map();//刷新显示地图		
		}
		Sleep(gamespeed());
	}
}

2.2 tcs.h

#pragma once
#define  _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include <conio.h>  
#include <string.h>  
#include <time.h> 
#include <process.h>
#include <graphics.h>

LRESULT CALLBACK CBTHookProc(int nCode, WPARAM wParam, LPARAM lParam);
int Msg(HWND hwnd, TCHAR *szText, TCHAR *szCaption, UINT uType);

void randfood_resisttime(void *);
void Create_Food(int mod);
void Show_Map();
void Show_Score();
void Button();
void Check_Head(int x, int y);
void Move();
void StartGame();
void OnInit();
int gamespeed(); 
int getscorestep();//获得每次吃食物后的加分
void Cre_Hard_wallpos();//无屏障困难模式 产生wall
void Creat_game();//产生游戏要素
void Save_game();//存档
void Read_game();//读档

2.3 main.cpp

#include "tcs.h"

int main()
{
	_beginthread(&randfood_resisttime, 0, 0);
	StartGame();

	return 0;
}

由 LiangJian 写于 2019 年 10 月 11 日