一、遊戲窗口
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 日