每天一個編程小項目,提升你的編程能力! 這個是用C++語法和鏈表知識實現的哦!
游戲說明
這是一個傳統的貪吃蛇游戲,基于鏈表實現
按鍵說明
方向控制:↑↓←→ 或者 Q(逆時針),R(順時針)
速度:按下 space 加速,‘[’ 減速,‘]’ 加速
食物:小鍵盤 + 增加食物,小鍵盤 - 減少食物
其他:非小鍵盤數字鍵 9、0 可以調整幀率,小鍵盤 * 可以切換貪吃蛇模式,F1 幫助,F11 截屏
狀態欄說明
生命狀態:貪吃蛇是否存活,由于沒有設置死亡,所以只有存活和瀕死兩種狀態
等級:每吃 10 個食物升一級
分數:每個食物 10 分
速度:默認速度 0 ,可以調節,最快 10,最慢 -10
長度:貪吃蛇的節數(包括頭)
食物數量:界面中的食物個數,最大 99 ,可以手動調整
蛇體模式:貪吃蛇的模式,分為正常、穿墻和無敵(穿墻的基礎上可以穿過自己)
效果展示
簡單了解游戲后我們就來試試吧!(直接上源碼,大家可以看注釋)
GluSnake.h
#include#include //using namespace std; #include #include #include #include #include #include "mmsystem.h" #pragma comment(lib,"winmm.lib") namespace gSnake { enum class position { right, down, left, up }; enum class Snake_condition { survive, die, ate }; }; using namespace gSnake; //畫布 #define Map_wide 800 #define Map_height 600 //單個繪制單元 #define Segment_wide 10 #define Segment_height 10 #define Segment_sum 100 //有效坐標地圖 #define PatternElement_wide 80 #define PatternElement_height 60 #define PatternElement_sum (80 * 60) struct Point //坐標點 { int x; int y; }; /* * 名稱:蛇體 * 數據結構:雙向鏈表 * 作用:用于存儲蛇身體坐標信息 */ class SnakeBody { struct _SnakeBody_Link //蛇身數據類型 { Point bodyCoord; //身體坐標 _SnakeBody_Link* next = NULL; //指向下一節蛇身 _SnakeBody_Link* last = NULL; //指向上一節蛇身 }; struct SnakeBody_inf //蛇體信息 { _SnakeBody_Link* head = NULL; _SnakeBody_Link* end = NULL; int len = 0; }; //Point SnakeSiteBuff[PatternElement_sum]; public: COLORREF SnakeColor[80 * 60]; int colorcur = Snake_Body.len; public: //創建蛇體并傳入蛇頭信息 bool Creat_SnakeBody(Point site); //從頭添加一節蛇體 bool Add_SnakeBody(Point site); //從尾部刪除一節蛇體 bool Del_SnakeBody(); //銷毀整個蛇體 void destroy_SnakeBody(); //找到某節蛇體 Point Find_SnakeBody(int len); ~SnakeBody(){ destroy_SnakeBody(); } public: SnakeBody_inf Snake_Body; //創建蛇體信息 }; struct Food { Point fdxy; // 坐標 COLORREF color = RED; // 食物顏色 }; /* * 食物 * 虛繼承 蛇體 * 同時具有蛇體數據和食物數據 */ class SnakeFood :virtual public SnakeBody { public: void Food_init(); int Supple_Food(); public: Food food[100] = { 0 }; int foodSum = 1; }; class music { public: void playmusicEat(int cmd) { switch (cmd) { case 0: PlaySound(MAKEINTRESOURCE(102), NULL, SND_ASYNC | SND_NODEFAULT | SND_RESOURCE); break; case 1: PlaySound(MAKEINTRESOURCE(101), NULL, SND_ASYNC | SND_NODEFAULT | SND_RESOURCE); break; } } void playmusicHit(int cmd) { switch (cmd) { case 0: PlaySound(MAKEINTRESOURCE(103), NULL, SND_ASYNC | SND_NODEFAULT | SND_RESOURCE); break; } } }; /* * 對象一條蛇 * 虛繼承 食物,音效 * 具有蛇體數據,食物數據,蛇動作及狀態 */ class Snake : virtual public SnakeFood, virtual public music { public: void Snake_Init() { Creat_SnakeBody({ 6, 29 }); Add_SnakeBody({ 7,29 }); Add_SnakeBody({ 8,29 }); Food_init(); Supple_Food(); } Snake_condition Smove(int mode) // 移動 { Point head; Sveer(); head = Find_SnakeBody(0); switch (move_direction) { case position: { if (head.x + 1 >= PatternElement_wide) { if (mode == 0)return SnakeCondition = Snake_condition::die; else head.x = 0; } else head.x++; }break; case position: { if (head.y + 1 >= PatternElement_height) { if (mode == 0)return SnakeCondition = Snake_condition::die; else head.y = 0; } else head.y++; }break; case position: { if (head.x - 1 < 0) { if (mode == 0)return SnakeCondition = Snake_condition::die; else head.x = PatternElement_wide - 1; } else head.x--; }break; case position: { if (head.y - 1 < 0) { if (mode == 0)return SnakeCondition = Snake_condition::die; else head.y = PatternElement_height - 1; } else head.y--; }break; default:break; } if (mode != 2) for (int i = 0; i < Snake_Body.len; i++) if (head.x == Find_SnakeBody(i).x)if (head.y == Find_SnakeBody(i).y) { return SnakeCondition = Snake_condition::die; } //移動一格 Add_SnakeBody(head); //添加一節 for (int i = 0; i < foodSum; i++)if (head.x == food[i].fdxy.x && head.y == food[i].fdxy.y) { if (score / 10 % 10 == 9)playmusicEat(0); else playmusicEat(1); food[i] = { 0xff,0xff }; return Snake_condition::ate; } //無操作 Del_SnakeBody(); //刪除一節 len = Snake_Body.len; refreshSnakeinf(); return SnakeCondition = Snake_condition::survive; } void refreshSnakeinf() { len = Snake_Body.len; score = 10 * (len - 3); grade = (len - 3) / 10; } int Sveer() { int state = (int)move_direction - target_direction; if ((state == 2) || (state == -2))return 1; else move_direction = (position)target_direction; return 0; } public: int target_direction = 10; Snake_condition SnakeCondition = Snake_condition::survive; // 狀態 int len = 0; //長度 // position target_direction = position::right; //朝向 int Speed = 0; //速度 int score = 0; //分數 int grade = 0; //等級 int snakeBodyMod = 0; private: position move_direction = position::right; //朝向 }; /* * 按鍵交互 * 虛繼承 蛇 * 具有蛇體數據,食物數據,蛇動作及狀態,控制器 */ class Key :virtual public Snake { int remem = 0; #define KEY_DOWN(VK_NONAME) (((GetAsyncKeyState(VK_NONAME)) ) ? 1:0) public: static const int keySum = 15; //指令數 struct _KEY { int reset = 0; short keyState = 0; }; _KEY key[keySum]; int help_sign = 0; int max_fps = 120; int keyDown() { key[0].keyState = KEY_DOWN(VK_RIGHT); key[1].keyState = KEY_DOWN(VK_DOWN); key[2].keyState = KEY_DOWN(VK_LEFT); key[3].keyState = KEY_DOWN(VK_UP); key[4].keyState = KEY_DOWN(0x51);//q key[5].keyState = KEY_DOWN(0x45);//e key[6].keyState = KEY_DOWN(0x6B);//+ key[7].keyState = KEY_DOWN(0x6D);//- key[8].keyState = KEY_DOWN(0x6A);//* key[9].keyState = KEY_DOWN(0xDB);//[ key[10].keyState = KEY_DOWN(0xDD);//] key[11].keyState = KEY_DOWN(0x20);//space key[12].keyState = KEY_DOWN(0x70);//F11 key[13].keyState = KEY_DOWN(0x30);//) key[14].keyState = KEY_DOWN(0x39);//( for (int i = 0; i < keySum; i++) { if (i < 4) { if (key[i].keyState == 0)key[i].reset = 1; else if (key[i].reset == 1) { target_direction = i; key[i].reset = 0; } } if (i == 4) { if (key[i].keyState == 0)key[i].reset = 1; else { if (key[i].reset == 1) { if ((int)target_direction > 0)target_direction = ((int)target_direction - 1); else target_direction = 3; key[i].reset = 0; } } } if (i == 5) { if (key[i].keyState == 0)key[i].reset = 1; else { if (key[i].reset == 1) { if ((int)target_direction < 3)target_direction = ((int)target_direction + 1); else target_direction = 0; key[i].reset = 0; } } } if (i == 6) { if (key[i].keyState == 0)key[i].reset = 1; else { if (key[i].reset == 1) { if ((int)foodSum < 99)foodSum = ((int)foodSum + 1); else foodSum = 0; key[i].reset = 0; } } } if (i == 7) { if (key[i].keyState == 0)key[i].reset = 1; else { if (key[i].reset == 1) { if ((int)foodSum > 0)foodSum = ((int)foodSum - 1); else foodSum = 99; key[i].reset = 0; } } } if (i == 8) { if (key[i].keyState == 0)key[i].reset = 1; else { if (key[i].reset == 1) { if ((int)snakeBodyMod < 2)snakeBodyMod = ((int)snakeBodyMod + 1); else snakeBodyMod = 0; key[i].reset = 0; } } } if (i == 9) { if (key[i].keyState == 0)key[i].reset = 1; else { if (key[i].reset == 1) { if ((int)Speed < 10)Speed = ((int)Speed + 1); else Speed = 0; key[i].reset = 0; } } } if (i == 10) { if (key[i].keyState == 0)key[i].reset = 1; else { if (key[i].reset == 1) { if ((int)Speed > -10)Speed = ((int)Speed - 1); else Speed = 0; key[i].reset = 0; } } } if (i == 11) { if (key[i].keyState == 0) { if (key[i].reset == 0) Speed = remem; key[i].reset = 1; } else { if (key[i].reset == 1) { remem = Speed; Speed = 10; key[i].reset = 0; } } } if (i == 12) { if (key[i].keyState == 0) { help_sign = 0; } else { help_sign = 1; } } if (i == 13) { if (key[i].keyState == 0) { //if (key[i].reset == 0); key[i].reset = 1; } else { if (key[i].reset == 1) { if (max_fps < 500)max_fps++; else max_fps = 10; key[i].reset = 0; } } } if (i == 14) { if (key[i].keyState == 0) { if (key[i].reset == 0) Speed = remem; key[i].reset = 1; } else { if (key[i].reset == 1) { if (max_fps > 10)max_fps--; else max_fps = 500; key[i].reset = 0; } } } } return 0; } }; /* * 幀檢測 * 通過GetTickCount()的調用時間差 */ class FPS { public: int Get_Fps() { DWORD _TimeMs = GetTickCount(); if (_TimeMs - timePoint > 1000) { fps = count; timePoint = _TimeMs; count = 0; } else { count++; } return fps; } int fps = 0; int count = 0; DWORD timePoint = GetTickCount(); }; /* * 圖像內容繪制 * 虛繼承 按鍵交互 * 具有蛇體數據,食物數據,蛇動作及狀態,控制器,圖像內容繪制 */ class Draw : virtual public Key, virtual public FPS { public: void drawSnake(); void drawFood(int kep); wchar_t* trstring2wchar(char* str) { int mystringsize = (int)(strlen(str) + 1); WCHAR* wchart = new wchar_t[mystringsize]; MultiByteToWideChar(CP_ACP, 0, str, -1, wchart, mystringsize); return wchart; } WCHAR* numtostr(int num, WCHAR* wbuf) { //WCHAR* buf = new wchar_t[100]; char buf[100]; _itoa_s(num, buf, 50, 10); int mystringsize = (int)(strlen(buf) + 1); MultiByteToWideChar(CP_ACP, 0, buf, -1, wbuf, mystringsize); return wbuf; } #define _Site(x) (x*16) void help(int x, int y) { int hang = 0; outtextxy(_Site(x), _Site(y + hang), _T("按鍵說明:----------------------------------")); hang++; outtextxy(_Site(x), _Site(y + hang), _T("--方向控制:↑↓←→ 或者 Q(逆時針),R(順時針)-")); hang++; outtextxy(_Site(x), _Site(y + hang), _T("--速度:按下space加速,‘[’ 減速,‘]’加速 -")); hang++; outtextxy(_Site(x), _Site(y + hang), _T("--食物:小鍵盤+ 增加食物,小鍵盤- 減少食物 -")); hang++; outtextxy(_Site(x), _Site(y + hang), _T("--其他要素請自行探索! -")); hang++; outtextxy(_Site(x), _Site(y + hang), _T("--------------------------------------------")); hang++; outtextxy(_Site(x), _Site(y + hang), _T("[Version: 1.0 -----by RorySpt in 2020/7/28]")); } void drawtext(Point site) { int x = site.x; int y = site.y; int hang = 0; WCHAR buf[100] = { 0 }; //outtextxy(_Site(x), _Site(y + hang), _T("貪吃蛇:Snake0")); hang = 0; outtextxy(_Site(x), _Site(y + hang), _T("生命狀態:")); if (SnakeCondition == Snake_condition::survive) outtextxy(_Site(x + 5), _Site(y + hang), _T("存活")); if (SnakeCondition == Snake_condition::die) outtextxy(_Site(x + 5), _Site(y + hang), _T("瀕死")); if (SnakeCondition == Snake_condition::ate) outtextxy(_Site(x + 5), _Site(y + hang), _T("吃了")); hang ++; outtextxy(_Site(x), _Site(y + hang), _T("等級:")); outtextxy(_Site(x + 3), _Site(y + hang), numtostr(grade, buf)); hang ++; outtextxy(_Site(x), _Site(y + hang), _T("分數:")); outtextxy(_Site(x + 3), _Site(y + hang), numtostr(score, buf)); hang ++; outtextxy(_Site(x), _Site(y + hang), _T("速度:")); outtextxy(_Site(x + 3), _Site(y + hang), numtostr(Speed, buf)); hang ++; outtextxy(_Site(x), _Site(y + hang), _T("長度:")); outtextxy(_Site(x + 3), _Site(y + hang), numtostr(len, buf)); hang ++; outtextxy(_Site(x), _Site(y + hang), _T("食物數量:")); outtextxy(_Site(x + 5), _Site(y + hang), numtostr(foodSum, buf)); hang ++; outtextxy(_Site(x), _Site(y + hang), _T("蛇體模式:")); if (snakeBodyMod == 0) outtextxy(_Site(x + 5), _Site(y + hang), _T("正常")); if (snakeBodyMod == 1) outtextxy(_Site(x + 5), _Site(y + hang), _T("穿墻")); if (snakeBodyMod == 2) outtextxy(_Site(x + 5), _Site(y + hang), _T("無敵")); hang ++; outtextxy(_Site(x), _Site(y + hang), _T("FPS:")); outtextxy((int)_Site(x + 2.5), _Site(y + hang), numtostr(Get_Fps(), buf)); hang ++; outtextxy(_Site(x), _Site(y + hang), _T("說明:(F1)")); if (help_sign == 1) { help(10, 2); } } }; /* * 貪吃蛇游戲 * 具有蛇體數據,食物數據,蛇動作及狀態,控制器,圖像內容繪制,圖形初始化 * */ class GluSnakeGame :virtual public Draw { GluSnakeGame() { drawInit(); }; public: static GluSnakeGame* gethInstance() { static GluSnakeGame Instance; return &Instance; } int drawInit() { initgraph(Map_wide, Map_height); setbkcolor(RGB(95, 183, 72)); setlinecolor(0xf4d690); settextcolor(0x0); settextstyle(16, 0, _T("Consolas")); srand((unsigned)time(NULL)); //settextcolor(BLUE); setbkmode(TRANSPARENT); // 設置文字輸出模式為透明 return 0; } }; /* * 幀控制器 */ class FRACTRL { WCHAR* numtostr(int num) { //WCHAR* buf = new wchar_t[100]; char buf[100]; static WCHAR wbuf[100]; _itoa_s(num, buf, 50, 10); int mystringsize = (int)(strlen(buf) + 1); MultiByteToWideChar(CP_ACP, 0, buf, -1, wbuf, mystringsize); return wbuf; } LARGE_INTEGER QueryCounter() { LARGE_INTEGER count; QueryPerformanceCounter(&count); return count; } LARGE_INTEGER QueryFrequency() { LARGE_INTEGER count; QueryPerformanceFrequency(&count); return count; } FRACTRL() {}; public: static FRACTRL* gethInstance() { return &farctrl; } int triggerIndicator(int targetFps) { LARGE_INTEGER nowTime = QueryCounter(); if ((nowTime.QuadPart - timePoint.QuadPart) * 1.0 >= (tc.QuadPart / targetFps)) { timePoint = nowTime; return 1; } else return 0; } void timeController(int targetFps) { while (!triggerIndicator(targetFps))Sleep(0); } void DrawQuery(int x, int y) { outtextxy(x, y, _T("QueryFrequency:")); outtextxy(x + 8 * 15, y, numtostr((int)QueryFrequency().QuadPart)); } public: LARGE_INTEGER timePoint = QueryCounter(); LARGE_INTEGER tc = QueryFrequency(); static FRACTRL farctrl; };
resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 貪吃蛇.rc 使用 // #define IDR_WAVE1 101 #define IDR_WAVE2 102 #define IDR_WAVE3 103 #define IDR_WAVE4 104 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
GluSnake.cpp(鏈表部分)
#include "GluSnake.h" FRACTRL FRACTRL::farctrl; bool SnakeBody::Creat_SnakeBody(Point site)//創建蛇體并傳入蛇頭坐標 { if(Snake_Body.head!=NULL)destroy_SnakeBody(); Snake_Body.head = (_SnakeBody_Link*)malloc(sizeof(_SnakeBody_Link)); //創建一個蛇體單元,定為蛇頭; if (Snake_Body.head == NULL) { exit(-1); } Snake_Body.end = Snake_Body.head; //蛇尾暫時與蛇頭重合 SnakeColor[Snake_Body.len++] = RGB(rand() % 256, rand() % 256, rand() % 256); //Snake_Body.len = 1; Snake_Body.head->bodyCoord = site; Snake_Body.end->bodyCoord = site; return true; } bool SnakeBody::Add_SnakeBody(Point site)//從頭添加一節蛇體 { _SnakeBody_Link* newBody = (_SnakeBody_Link*)malloc(sizeof(_SnakeBody_Link)); if (newBody == NULL) { exit(-1); } newBody->bodyCoord = site; //為新蛇身坐標賦值 newBody->next = Snake_Body.head;//新的蛇體作為蛇頭 newBody->last = NULL; Snake_Body.head->last = newBody; Snake_Body.head = newBody; SnakeColor[Snake_Body.len++] = RGB(rand() % 256, rand() % 256, rand() % 256); //蛇身長度加1 return 0; } bool SnakeBody::Del_SnakeBody()//從尾部刪除一節蛇體 { if (Snake_Body.len < 1)return Snake_Body.len; Snake_Body.end = Snake_Body.end->last; free(Snake_Body.end->next); Snake_Body.len--; return Snake_Body.len; } void SnakeBody::destroy_SnakeBody()//銷毀整個蛇體 { while (Del_SnakeBody() != 0); } Point SnakeBody::Find_SnakeBody(int len) //找到某節蛇體 { if (len > Snake_Body.len)return { -1,-1 }; _SnakeBody_Link* cur = Snake_Body.head; while (len--)cur = cur->next; return cur->bodyCoord; } void SnakeFood::Food_init() { for (int i = 0; i < 100; i++)food[i] = { 0xff,0xff }; } int SnakeFood::Supple_Food() { Point temp, newFood; for (int i = 0; i < foodSum; i++) { if (food[i].fdxy.x == 0xff && food[i].fdxy.y == 0xff) { newFood.x = rand() % PatternElement_wide; newFood.y = rand() % PatternElement_height; for (int i = 0; i < Snake_Body.len; i++) { temp = Find_SnakeBody(i); if (temp.x == newFood.x && temp.y == newFood.y) { newFood.x = rand() % PatternElement_wide; newFood.y = rand() % PatternElement_height; i = 0; } } food[i].fdxy = newFood; } } return 0; } void Draw::drawSnake() { Point temp; COLORREF SNAKE_COLAR = GREEN; for (int i = 0; i < Snake_Body.len; i++) { temp = Find_SnakeBody(i); temp.x *= 10; temp.y *= 10; setfillcolor(SnakeColor[i]); fillrectangle(temp.x, temp.y, temp.x + 10, temp.y + 10); } } void Draw::drawFood(int kep) { Point temp; for (int i = 0; i <= foodSum - 1; i++) { temp = food[i].fdxy; temp.x *= 10; temp.y *= 10; if (kep == 1)food[i].color = RGB(rand() % 256, rand() % 256, rand() % 256); setfillcolor(food[i].color); // 每次重新賦予食物一個隨機的顏色 fillrectangle(temp.x, temp.y, temp.x + 10, temp.y + 10); } }
SnakeGame.cpp
// 程序名稱:貪吃蛇 #include "GluSnake.h" #include "io.h" int main() { //Draw Snake0; GluSnakeGame* GluSnake = GluSnakeGame::gethInstance(); FRACTRL* pFraCtrl = FRACTRL::gethInstance(); //FPS FpsDetector; //drawInit(); GluSnake->Snake_Init(); GluSnake->drawSnake(); GluSnake->drawFood(1); int movecout = 0; int drawcout = 0; int sign = 0; GluSnake->max_fps = 60; // 開啟批量繪圖,作用是避免閃爍 BeginBatchDraw(); while (1) { //Sleep(2); drawcout++; GluSnake->keyDown(); //速度控制 if (movecout++ >= (10 - GluSnake->Speed)) { GluSnake->Smove(GluSnake->snakeBodyMod); movecout = 0; } //存活控制 if (GluSnake->SnakeCondition == Snake_condition::survive || GluSnake->SnakeCondition == Snake_condition::ate)sign = 1; else if (sign == 1) { GluSnake->playmusicHit(0); sign = 0; } //生成食物 GluSnake->Supple_Food(); //if(drawcout++>5){Snake0.SnakeColor[0] = RGB(rand() % 256, rand() % 256, rand() % 256);} //繪制 cleardevice(); GluSnake->drawSnake(); GluSnake->drawFood(((drawcout % 10) == 0)); GluSnake->drawtext({ 0,0 }); FlushBatchDraw(); static SHORT bPicture = 0; static int png_count = 0; if (!bPicture&&(bPicture = ((GetAsyncKeyState(0x7A))) ? 1 : 0)) { wchar_t buf[100]; _wfinddata_t file; do{ swprintf_s(buf, L"截圖%d.png", png_count++); } while (_wfindfirst(buf, &file)!=-1); saveimage(buf); } else { bPicture = ((GetAsyncKeyState(0x7A))) ? 1 : 0; } //幀控制 pFraCtrl->timeController(GluSnake->max_fps); } EndBatchDraw(); return 0; }
大家趕緊去動手試試吧!
-
游戲
+關注
關注
2文章
745瀏覽量
26329 -
編程
+關注
關注
88文章
3623瀏覽量
93798 -
C++
+關注
關注
22文章
2110瀏覽量
73696 -
貪吃蛇
+關注
關注
0文章
30瀏覽量
9820
原文標題:【項目實戰】C++多文件寫法輕松實現練手小游戲:貪吃蛇!
文章出處:【微信號:cyuyanxuexi,微信公眾號:C語言編程學習基地】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論