【C / EasyX】十字消除遊戲的實現方法
阿新 • • 發佈:2019-01-21
十字消除遊戲是以前百度上火過一段時間的一個Flash小遊戲。
遊戲邏輯比較簡單,是我初次接觸EasyX圖形庫後使用EasyX寫的第一個i程式。
遊戲開始後,在棋盤中隨機生成成對的隨即顏色的方塊,預留一些空白位置。玩家點選空白位置,如果所在位置延伸出的十字線上,能遇到相同顏色的方塊,則消除之,累加分數。消除一對方塊會恢復一定時間,不進行任何操作時間條會自動減少,直至耗盡遊戲結束。如果棋盤上剩餘方塊少於一定數量,會繼續隨機生成一定數量的新方塊。
效果圖如下:
原始碼如下:
///////////////////////////////////////////////////////// // 程式名稱:AyaCrossX(十字消除遊戲) // 編譯環境:Visual C++ 6.0 / 2012,EasyX 2013霜降版 // 作 者:ls9512 <http://www.baidu.com/p/ls9512> // 最後修改:2013-11-2 // //系統函式庫 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <conio.h> #include <math.h> #include <time.h> //EasyX圖形庫 #include <graphics.h> //////////////////////////////以下是 巨集定義引數 ////////////////////////////// #define WIN_WIDTH 600 //螢幕寬度 #define WIN_HEIGHT 430 //螢幕高度 #define WIN_BLANKTOP 45 //螢幕頂部空邊 #define WIN_BLANK 25 //螢幕邊緣留空 #define G_BN 20 //方塊橫向數量 #define G_BM 12 //方塊縱向數量 #define G_BW 20 //方塊寬度 #define G_BS 8 //方塊間隔寬度 #define G_BSELECT 3 //方塊選中大小 #define G_FLOATW 4 //選中浮動大小 #define G_BDWO 1 //消失初始大小 #define G_BDW 6 //消失大小 #define G_CN 10 //顏色數量 #define G_BUL 25 //方塊增量程度 #define C_BLOCKPANEL RGB(255, 255, 221) //底板顏色 #define C_HPBAR RGB(255, 170, 255) //HP條顏色 //////////////////////////////以下是資料結構定義////////////////////////////// //方塊結構 struct Block{ COLORREF color; //顏色 float FLOAT; //浮動大小 float DIS; //消除延遲 bool isDIS; //是否消失中 bool isClick; //是否被點選 }; //點結構 struct Point{ int x; int y; }; //矩形結構 struct Rect{ int x; int y; int w; int h; }; //////////////////////////////以下是 全域性變數 ////////////////////////////// int GamePhase = 0; //遊戲階段 0 準備 1 進行 2 結束 3 幫助介面 int isShowFPS = 0; //是否顯示幀數 int mouseX; //滑鼠位置座標X int mouseY; //滑鼠位置座標Y int time_max; //總遊戲時間 int time_now; //當前剩餘時間 int level; //等級 int score; //分數 bool isMouseDown; //滑鼠按下 Block block[G_BM][G_BN]; //方塊陣列 COLORREF Defcolor[G_CN]; //候選顏色陣列 //////////////////////////////以下是 函式宣告 ////////////////////////////// float GetFPS(); //獲取幀數 void HpSleep(DWORD ms); //絕對延時 Point GetMouseLocal(int x, int y); //獲取滑鼠所在座標 void AddBlock(int n); //新增指定個數的未選中方塊 void Init(); //初始化 void DisCheck(int x, int y); //檢測指定位置的消除 int CountBlock(); //統計方塊個數 void Manager(); //邏輯處理 void Draw(); //繪圖處理 void StartDraw(); //遊戲開始繪圖 void GamingDraw(); //遊戲進行中繪圖 void EndDraw(); //遊戲結束繪圖 void HelpDraw(); //遊戲幫助繪圖 void EndFrame(); //幀結束處理 bool IsInRect(int x, int y, Rect r); //是否在矩形內 //////////////////////////////以下是輔助功能函式////////////////////////////// //計算畫面FPS(每秒幀數) float GetFPS() { #define FPS_COUNT 8 static int i = 0; static DWORD oldTime = GetTickCount(); static float fps; if (i > FPS_COUNT) { i = 0; int newTime = GetTickCount(); int elapsedTime = newTime - oldTime; fps = FPS_COUNT / (elapsedTime / 1000.0f); oldTime = newTime; } i++; return fps; } //絕對延時 void HpSleep(DWORD ms) { static clock_t oldclock = clock(); // 靜態變數,記錄上一次 tick oldclock += ms * CLOCKS_PER_SEC / 1000; // 更新 tick if (clock() > oldclock) // 如果已經超時,無需延時 oldclock = clock(); else while(clock() < oldclock) // 延時 Sleep(1); // 釋放 CPU 控制權,降低 CPU 佔用率 } //獲取滑鼠選中點 Point GetMouseLocal(int x, int y) { Point p; x = (x - WIN_BLANK) / (G_BW + G_BS); y = (y - WIN_BLANK - WIN_BLANKTOP) / (G_BW + G_BS); if (x < 0 || x >= G_BN) x = -1; if (y < 0 || y >= G_BM) y = -1; p.x = x; p.y = y; return p; } //是否在矩形內 bool IsInRect(int x, int y, Rect r) { return ((x >= r.x && x <= r.w) && (y >= r.y && y <= r.h)); } //新增制定個數的未選中方塊 void AddBlock(int n) { //隨機生成方塊 int num = 0, x1, y1, x2, y2; int num_max = n; while(num < num_max) { x1 = rand() % G_BN; y1 = rand() % G_BM; x2 = rand() % G_BN; y2 = rand() % G_BM; if(block[y1][x1].isClick && block[y2][x2].isClick) { COLORREF color = Defcolor[rand() % G_CN]; block[y1][x1].color = color; block[y1][x1].isClick = false; block[y1][x1].FLOAT = 0; block[y1][x1].DIS = 0; block[y1][x1].isDIS = false; block[y2][x2].color = color; block[y2][x2].isClick = false; block[y2][x2].FLOAT = 0; block[y2][x2].DIS = 0; block[y2][x2].isDIS = false; num += 2; } } } //初始化 void Init() { //預置顏色 Defcolor[0] = RGB(255, 153, 0); Defcolor[1] = RGB(204, 102, 0); Defcolor[2] = RGB(27, 118, 255); Defcolor[3] = RGB(255, 136, 255); Defcolor[4] = RGB(204, 204, 102); Defcolor[5] = RGB(104, 204, 204); Defcolor[6] = RGB(255, 125, 125); Defcolor[7] = RGB(204, 104, 204); Defcolor[8] = RGB(190, 190, 190); Defcolor[9] = RGB(0, 204, 0); //初始化時間 time_max = 10000; time_now = 10000; //初始化分數 score = 0; //置全選中 for(int i = 0; i < G_BM; i++) { for(int j = 0; j < G_BN; j++) { block[i][j].isClick = true; block[i][j].FLOAT = 0; block[i][j].DIS = 0; } } //新增方塊 AddBlock(G_BN * G_BM * 2 / 3); //清除滑鼠訊息佇列 FlushMouseMsgBuffer(); } //統計方塊個數 int CountBlock() { int n = 0; for(int i = 0; i < G_BN; i++) { for(int j = 0; j < G_BM; j++) { if(!block[j][i].isClick) n++; } } return n; } //檢測指定位置消除 void DisCheck(int x, int y) { //記錄待消除的方塊 Block* b[4]; int i, j, n = 0; //向左 for(i = x; i >= 0; i--) { if(!block[y][i].isClick) { b[n] = &block[y][i]; n++; break; } } //向右 for(i = x; i < G_BN; i++) { if(!block[y][i].isClick) { b[n] = &block[y][i]; n++; break; } } //向上 for(j = y; j >= 0; j--) { if(!block[j][x].isClick) { b[n] = &block[j][x]; n++; break; } } //向下 for(j = y; j < G_BM; j++) { if(!block[j][x].isClick) { b[n] = &block[j][x]; n++; break; } } //消除 for(i = 0; i < n; i++) { for(j = i + 1; j < n; j++) { if(b[i]->color == b[j]->color) { if(!b[i]->isDIS){ b[i]->DIS = G_BDWO; b[i]->isDIS = true; } if(!b[j]->isDIS){ b[j]->DIS = G_BDWO; b[j]->isDIS = true; } } } } isMouseDown = false; } //邏輯處理 void Manager() { //如果在遊戲中 if(GamePhase == 1) { Point p = GetMouseLocal(mouseX, mouseY); int x = p.x, y = p.y; //如果選中 if(p.x != -1 && p.y != -1) { //如果單擊 if(isMouseDown && block[y][x].isClick) { //觸發消除檢測 DisCheck(x, y); } } //新增新方塊新增隨機個數新方塊 if(CountBlock() < G_BN * G_BM / 3) { AddBlock((rand() % 15 + 15) * 2); //時間恢復 time_now += 500; if(time_now > time_max) time_now = time_max; } } } //開始繪圖 void StartDraw() { settextstyle(52, 30, _T("Impact")); //標題 settextcolor(RGB(255, 17, 102)); outtextxy(143, 93, _T("AyaCrossX")); settextcolor(RGB(255, 153, 238)); outtextxy(140, 90, _T("AyaCrossX")); //版本 settextstyle(22, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(340, 153, _T("v0.91 By:ls9512")); //開始遊戲按鈕 Rect r; r.x = 240; r.y = 220; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //開始遊戲按鍵響應 if(isMouseDown) { Init(); GamePhase = 1; isMouseDown = false; } } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T("PLAY GAME")); //幫助遊戲按鈕 r.x = 240; r.y = 270; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //幫助按鍵響應 if(isMouseDown) GamePhase = 3; } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T("HELP INFO")); //退出遊戲按鈕 r.x = 240; r.y = 320; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //退出按鍵響應 if(isMouseDown) exit(0); } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T("EXIT GAME")); //說明 settextstyle(16, 5, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(175, 400, _T("Programing By VC++ & EasyX Date:2013.11.01")); } //遊戲中繪圖 void GameingDraw() { int x, y; COLORREF c, c2; //時間條 x = WIN_BLANK; y = WIN_BLANK; int HPBARW = (G_BW + G_BS) * G_BN - 200; setlinecolor(RGB(255, 17, 255)); setfillcolor(WHITE); fillrectangle(x, y, x + HPBARW, y + 10); setfillcolor(C_HPBAR); fillrectangle(x, y, x + (int)(HPBARW * (1.0 * time_now / time_max)), y + 10); //底板 setlinecolor(RGB(255, 25, 22)); setfillcolor(C_BLOCKPANEL); x = WIN_BLANK - G_BS; y = WIN_BLANK + WIN_BLANKTOP - G_BS; fillrectangle(x, y, x + (G_BW + G_BS) * G_BN + G_BS, y + (G_BW + G_BS) * G_BM + G_BS); //畫方塊 for(int i = 0; i < G_BM; i++) { for(int j = 0; j < G_BN; j++) { if(!block[i][j].isClick) { c = block[i][j].color; c = RGB(GetRValue(c) - (BYTE)G_BUL, GetGValue(c) - (BYTE)G_BUL, GetBValue(c) - (BYTE)G_BUL); int fw = (int)block[i][j].FLOAT; int fd = (int)block[i][j].DIS; setlinecolor(c); setfillcolor(block[i][j].color); x = j * (G_BW + G_BS) + WIN_BLANK - fw - fd; y = i * (G_BW + G_BS) + WIN_BLANK + WIN_BLANKTOP - fw - fd; fillrectangle(x, y, x + G_BW + 2 * (fw + fd), y + G_BW + 2 * (fw + fd)); //浮動縮減 if(block[i][j].FLOAT > 0) block[i][j].FLOAT -= 0.4f; //消失延遲 if(block[i][j].isDIS) { if(block[i][j].DIS < G_BDW) block[i][j].DIS += 0.4f; else { //消除 block[i][j].isClick = true; //加分 score += 100; time_now += 25; if (time_now > time_max) time_now = time_max; } } } } } //獲取滑鼠位置 Point p = GetMouseLocal(mouseX, mouseY); //畫出滑鼠游標 if(p.x != -1 && p.y != -1) { c = RED; x = p.x, y = p.y; int fw; if(block[y][x].isClick) fw = 0; else fw = (int)block[y][x].FLOAT; x = x * (G_BW + G_BS) + WIN_BLANK - G_BSELECT - fw; y = y * (G_BW + G_BS) + WIN_BLANK + WIN_BLANKTOP - G_BSELECT -fw; //選中方塊加亮 if(!block[p.y][p.x].isClick) { //置浮動大小 block[p.y][p.x].FLOAT = G_FLOATW; c2 = block[p.y][p.x].color; } else { c2 = C_BLOCKPANEL; } setlinecolor(c); setfillcolor(c2); fillrectangle(x, y, x + G_BW + 2 * (G_BSELECT + fw), y + G_BW + 2 * (G_BSELECT + fw)); } //畫出分數 settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(155, 51, 68)); TCHAR b[10]; #if _MSC_VER > 1200 _stprintf_s(b, _T("%d"), score); #else _stprintf(b, _T("%d"), score); #endif outtextxy(390, 15, _T("分數:")); settextcolor(RGB(255, 51, 68)); outtextxy(440, 16, b); } //遊戲結束繪圖 void EndDraw() { //標題 settextstyle(52, 30, _T("Impact")); settextcolor(RGB(255, 17, 102)); outtextxy(143, 103, _T("GAME OVER")); settextcolor(RGB(215, 193, 238)); outtextxy(140, 100, _T("GAME OVER")); //版本 settextstyle(34, 12, _T("Verdana")); settextcolor(RGB(255, 51, 68)); TCHAR b[10]; #if _MSC_VER > 1200 _stprintf_s(b, _T("%d"), score); #else _stprintf(b, _T("%d"), score); #endif outtextxy(232, 173, _T("分數:")); outtextxy(290, 173, b); //重新開始遊戲按鈕 Rect r; r.x = 240; r.y = 240; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //開始遊戲按鍵響應 if(isMouseDown) { Init(); GamePhase = 1; } } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T(" REPLAY")); //退出 r.x = 240; r.y = 290; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //退出按鍵響應 if(isMouseDown) exit(0); } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 10, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T("EXIT GAME")); } //遊戲幫助繪圖 void HelpDraw() { //標題 settextstyle(52, 30, _T("Impact")); settextcolor(RGB(255, 17, 152)); outtextxy(233, 33, _T("HELP")); settextcolor(RGB(215, 193, 238)); outtextxy(230, 30, _T("HELP")); //底板 setlinecolor(RGB(255, 17, 204)); setfillcolor(RGB(255, 221, 255)); fillrectangle(60, 100, 550, 280); //版本 settextstyle(18, 10, _T("黑體")); settextcolor(RGB(245, 126, 92)); outtextxy(70, 110, _T("十字消除是一款經典消除類小遊戲。")); outtextxy(70, 145, _T("【玩法】")); outtextxy(80, 170, _T("1.點選空白處,所在橫縱十字線上同色方塊會被消除。")); outtextxy(80, 195, _T("2.消除方塊會累加得分,並恢復一定量的時間.")); outtextxy(80, 220, _T("3.方塊數量少於某數值會出現新方塊並回復時間.")); outtextxy(80, 245, _T("4.時間耗盡時則遊戲結束.")); //返回 Rect r; r.x = 240; r.y = 360; r.w = r.x + 125; r.h = r.y + 35; setlinecolor(RGB(255, 17, 102)); if(IsInRect(mouseX, mouseY, r)) { setfillcolor(RGB(255, 187, 119)); //返回按鍵響應 if(isMouseDown) GamePhase = 0; } else setfillcolor(RGB(255, 255, 204)); fillrectangle(r.x, r.y, r.w, r.h); settextstyle(25, 9, _T("Verdana")); settextcolor(RGB(255, 51, 68)); outtextxy(r.x + 5, r.y + 5, _T("BACK MENU")); } //輸出幀數 void DrawFPS() { //輸出幀數 if(isShowFPS) { settextcolor(RED); settextstyle(14, 0, _T("宋體")); TCHAR s[5]; #if _MSC_VER > 1200 _stprintf_s(s, _T("%.1f"), GetFPS()); #else _stprintf(s, _T("%.1f"), GetFPS()); #endif outtextxy(0, 0, s); } } //繪圖 void Draw() { //清屏 setlinecolor(WHITE); setfillcolor(WHITE); fillrectangle(0, 0, WIN_WIDTH, WIN_HEIGHT); switch(GamePhase) { case 0: StartDraw(); break; case 1: GameingDraw(); break; case 2: EndDraw(); break; case 3: HelpDraw(); break; } DrawFPS(); } //幀結束處理 void EndFrame() { time_now -= 2; //遊戲結束 if(time_now <= 0 && GamePhase == 1) { GamePhase = 2; } } //主入口函式 int main() { //置隨機數種子 srand((unsigned)time(NULL)); //初始化裝置,載入圖片 initgraph(WIN_WIDTH, WIN_HEIGHT); //設定視窗標題 SetWindowText(GetHWnd(), _T("AyaCrossX v0.91 By:ls9512")); cleardevice(); //設定黑色背景 setbkmode(TRANSPARENT); settextcolor(BLACK); //開啟雙緩衝,防止閃屏 BeginBatchDraw(); // 滑鼠訊息變數 MOUSEMSG mmsg; //初始化 Init(); while(true) { //處理滑鼠訊息 while(MouseHit()) { mmsg = GetMouseMsg(); switch(mmsg.uMsg) { case WM_MOUSEMOVE: mouseX = mmsg.x; mouseY = mmsg.y; break; case WM_LBUTTONDOWN: isMouseDown = true; break; case WM_LBUTTONUP: isMouseDown = false; break; } } //邏輯處理 Manager(); //繪圖處理 Draw(); //顯示快取的繪製內容 FlushBatchDraw(); //幀結束處理 EndFrame(); //延遲,幀數控制 HpSleep(18); } // 關閉 EndBatchDraw(); closegraph(); return 0; }
這個程式是我在13年10月時寫的,有些細節也記不太清楚了,在這就不贅述了,如果有疑問的地方可以留言我看到了會回覆解答。
【注】
此程式已被EasyX官網收錄為範例: