C语言贪吃蛇——Windows下基于EasyX带存档三种模式(无屏障、有屏障、迷宫)

一、游戏窗口

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

VS2013工程百度云链接:

链接:https://pan.baidu.com/s/1TNyaiF_9NHBfdObEsDLH3Q
提取码:es7z

1.1选择游戏模式窗口图形

在这里插入图片描述

1.2有屏障模式窗口图形

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

二、游戏源码

将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 日

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章