Windows下基于EasyX的带存档的三种模式贪吃蛇游戏
程序员文章站
2022-04-07 17:22:45
...
一、游戏窗口
PS:此游戏为c语言开发,因EasyX库需要.cpp文件才能编译,所以工程文件后缀为.cpp,并未使用cpp的语法。
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 日