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 日

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