C語言實(shí)現(xiàn)消消樂游戲的代碼分享
C和C++游戲趣味編程》一書各個(gè)章節(jié)的案例代碼,每章案例逐步利用學(xué)到的語法知識。
本章我們將編寫十字消除游戲,用戶點(diǎn)擊空白方塊,沿其上下左右方向?qū)ふ业谝粋€(gè)彩色方塊,如果有兩個(gè)或兩個(gè)以上顏色一致,就將其消除。在進(jìn)度條時(shí)間結(jié)束前消除足夠的方塊,可以進(jìn)入下一關(guān),效果如圖所示。
首先實(shí)現(xiàn)隨機(jī)顏色方塊的表示與繪制,鼠標(biāo)點(diǎn)擊與十字消除算法;然后繪制了提示框,繪制倒計(jì)時(shí)進(jìn)度條;接著進(jìn)行了得分計(jì)算、勝負(fù)判斷、多關(guān)卡功能的開發(fā);學(xué)習(xí)了地址與指針的概念,并利用地址傳遞使得程序更加模塊化;最后學(xué)習(xí)了指針和數(shù)組的知識,應(yīng)用動(dòng)態(tài)數(shù)組實(shí)現(xiàn)了游戲尺寸的動(dòng)態(tài)大小調(diào)整。
源碼
#include <graphics.h> #include <conio.h> #include <stdio.h> #include <time.h> #include <stdlib.h> # define BlockSize 40 // 小方塊的長寬大小 # define ColorTypeNum 9 // 除了空白方塊外,其他方塊的顏色的個(gè)數(shù) struct Block // 小方塊結(jié)構(gòu)體 { int x,y; // x y坐標(biāo) int colorId; // 對應(yīng)顏色的下標(biāo) int i,j; // 小方塊在二維數(shù)組中的i j下標(biāo) }; // 全局變量 int RowNum; // 游戲畫面一共RowNum行小方塊 int ColNum; // 游戲畫面一共ColNum列小方塊 Block **blocks = NULL; // 動(dòng)態(tài)二維數(shù)組指針,存儲所有方塊數(shù)據(jù) COLORREF colors[ColorTypeNum+1]; // 顏色數(shù)組,小方塊可能的幾種顏色 int score; // 得分?jǐn)?shù),也就是消去的方塊的個(gè)數(shù) float maxTime; // 這一關(guān)游戲的總時(shí)長 float totalTime; // 減去扣分項(xiàng)后的游戲總時(shí)長 float remainTime; // 剩余時(shí)間 clock_t start, finish; // 用于計(jì)時(shí)的變量 int level = 1; // 當(dāng)前關(guān)卡序號 int noZeroBlockNum; // 非空白區(qū)域的磚塊的個(gè)數(shù) void drawBlockHint(int i,int j,COLORREF color,int isfill) // 繪制出一個(gè)提示線框出來 { setlinecolor(color); setfillcolor(color); if (isfill==1) // 鼠標(biāo)點(diǎn)擊中的方塊,畫填充方塊提示 fillrectangle(blocks[i][j].x,blocks[i][j].y,blocks[i][j].x+BlockSize,blocks[i][j].y+BlockSize); if (isfill==0) // 上下左右四個(gè)方向找到的4個(gè)方塊,畫線框提示 rectangle(blocks[i][j].x,blocks[i][j].y,blocks[i][j].x+BlockSize,blocks[i][j].y+BlockSize); } void writeRecordFile(int recordScore) //保存最高分?jǐn)?shù)據(jù)文件 { FILE *fp; fp = fopen(".\\gameRecord.dat","w"); fprintf(fp,"%d",recordScore); fclose(fp); } int readRecordFile() //讀取最高分?jǐn)?shù)據(jù)文件 { int recordScore; FILE *fp; fp = fopen(".\\gameRecord.dat","r"); // 如果打不開的話,就新建一個(gè)文件,其得分記錄為0分 if (fp==NULL) { writeRecordFile(0); return 0; } else // 能打開這個(gè)文件,就讀取下最高分記錄 { fscanf(fp,"%d",&recordScore); fclose(fp); return recordScore; } } void startup() // 初始化函數(shù) { int i,j; start = clock(); // 記錄當(dāng)前運(yùn)行時(shí)刻 if (level>1) // 如果不是第1關(guān),則先清除二維數(shù)組內(nèi)存,再重新開辟內(nèi)存空間 { for (i=0;i<RowNum;i++) free(blocks[i]); free(blocks); } // 根據(jù)是第幾關(guān),調(diào)整這一關(guān)對應(yīng)的游戲畫面的大小 RowNum = 12 + level/2; // 行數(shù)添加的慢一些,是一個(gè)長方形的形狀 ColNum = 20 + level; // 開辟動(dòng)態(tài)二維數(shù)組 blocks = (Block **) malloc(RowNum*sizeof(Block *)); for (i=0;i<RowNum;i++) blocks[i] = (Block *) malloc(ColNum*sizeof(Block)); maxTime = 200 + level*10; // 這一關(guān)游戲設(shè)定的總時(shí)長,每關(guān)時(shí)長+10秒 totalTime = maxTime; // 游戲總時(shí)長,每次出錯(cuò),會扣10秒鐘 int width = BlockSize*ColNum; // 設(shè)定游戲畫面的大小 int height = BlockSize*(RowNum+3); // 最下面用來顯示一些提示信息 initgraph(width,height); setbkcolor(RGB(220,220,220)); setlinestyle(PS_SOLID,2); cleardevice(); srand(time(0)); BeginBatchDraw(); // 開始批量繪制 score = 0; // 得分?jǐn)?shù),也就是消去的方塊的個(gè)數(shù) noZeroBlockNum = 0; // 非空白區(qū)域的磚塊的個(gè)數(shù) colors[0] = RGB(220,220,220); // 顏色數(shù)組第一種顏色為灰白色,表示空白小方塊 for (i=1;i<ColorTypeNum+1;i++) // 其他幾種顏色為彩色 colors[i] = HSVtoRGB((i-1)*40,0.6,0.8); // 對blocks二維數(shù)組進(jìn)行初始化 for (i=0;i<RowNum;i++) { for (j=0;j<ColNum;j++) { // 取隨機(jī)數(shù),1-6設(shè)為彩色色塊,其他為空白色塊,這樣為空白色塊的幾率高一些 // 初始化時(shí),空白色塊多一些,符合游戲的設(shè)定 int t = rand()%(int(ColorTypeNum*1.5)); // 取隨機(jī)數(shù) if (t<ColorTypeNum+1) blocks[i][j].colorId = t; // 小方塊的顏色序號 else // 其他情況,都為空白顏色方塊 blocks[i][j].colorId = 0; // 小方塊的顏色序號 blocks[i][j].x = j*BlockSize; // 小方塊左上角坐標(biāo) blocks[i][j].y = i*BlockSize; // blocks[i][j].i = i; // 存儲當(dāng)前小方塊在二維數(shù)組中的下標(biāo) blocks[i][j].j = j; if (blocks[i][j].colorId != 0) noZeroBlockNum++; // 統(tǒng)計(jì)隨機(jī)產(chǎn)生的方塊中,非零方塊的個(gè)數(shù) } } } void show() // 繪制函數(shù) { cleardevice(); // 清屏 setlinecolor(RGB(255,255,255)); // 白色線條 int i,j; for (i=0;i<RowNum;i++) { for (j=0;j<ColNum;j++) { // 以對應(yīng)的顏色、坐標(biāo)畫出所有的小方塊 setfillcolor(colors[blocks[i][j].colorId]); fillrectangle(blocks[i][j].x,blocks[i][j].y,blocks[i][j].x+BlockSize,blocks[i][j].y+BlockSize); } } // 根據(jù)剩余時(shí)間,繪制一個(gè)倒計(jì)時(shí)進(jìn)度條,進(jìn)度條按最大時(shí)間maxTime秒繪制 setlinecolor(RGB(255,0,0)); setfillcolor(RGB(255,0,0)); fillrectangle(0,BlockSize*(RowNum+0.2),remainTime*BlockSize*ColNum/maxTime,BlockSize*(RowNum+0.8)); // 輸出得分文字 TCHAR s[80]; setbkmode(TRANSPARENT); _stprintf(s, _T("%d"), score); settextcolor(RGB(0,0,0)); settextstyle(22, 0, _T("宋體")); outtextxy(BlockSize*(ColNum/2-0.1), BlockSize*(RowNum+0.2), s); // 輸出一些游戲提示信息 _stprintf(s, _T("點(diǎn)擊空白方塊,其十字區(qū)域有兩個(gè)或以上相同顏色方塊則消除;不能消除扣時(shí)間")); outtextxy(BlockSize*(ColNum/15.0), BlockSize*(RowNum+1.2), s); _stprintf(s, _T("目前第 %d 關(guān),時(shí)間結(jié)束前得分達(dá)到 %d 可進(jìn)入下一關(guān)"),level,int(noZeroBlockNum*0.9)); outtextxy(BlockSize*(ColNum/5.0), BlockSize*(RowNum+2.2), s); FlushBatchDraw(); // 批量繪制 } void updateWithoutInput() // 和輸入無關(guān)的更新 { // 倒計(jì)時(shí)減少 finish = clock(); // 當(dāng)前時(shí)刻 // 從startup運(yùn)行后,這一關(guān)游戲運(yùn)行了多少秒 double duration = (double)(finish - start) / CLOCKS_PER_SEC; remainTime = totalTime-duration; // 游戲剩余的時(shí)間 // 如果時(shí)間到了 if (remainTime<=0) { // 讀一下文件記錄,如果當(dāng)前得分超過記錄 if (score > readRecordFile()) { // 更新下得分記錄 writeRecordFile(score); // 顯示恭喜超過記錄 show(); settextcolor(RGB(255,0,0)); settextstyle(100, 0, _T("黑體")); outtextxy(BlockSize*(ColNum/30.0), BlockSize*(RowNum/3.0), _T("恭喜打破得分記錄")); FlushBatchDraw(); // 批量繪制 Sleep(2000); } if (score>=int(noZeroBlockNum*0.9)) { level ++; // 如果得分達(dá)到要求,消除掉非空白方塊數(shù)目的90%,關(guān)卡加1 } startup(); // 調(diào)用初始化函數(shù),重新開始游戲 return; } } void updateWithInput() // 和輸入有關(guān)的更新 { if (remainTime<=0) // 時(shí)間到了,不要操作 return; int i,j; MOUSEMSG m; if (MouseHit()) { m = GetMouseMsg(); if(m.uMsg == WM_LBUTTONDOWN) // 當(dāng)按下鼠標(biāo)左鍵時(shí) { // 獲得點(diǎn)擊的小方塊的下標(biāo) int clicked_i = int(m.y)/BlockSize; int clicked_j = int(m.x)/BlockSize; // 點(diǎn)擊到下面提示部分了,不用處理,函數(shù)返回 if (clicked_i>=RowNum) return; // 如果當(dāng)前點(diǎn)擊的不是空白方塊,不需要處理,返回 if (blocks[clicked_i][clicked_j].colorId!=0) return; show(); // 先顯示其他方塊,再繪制提示框,后繪制的在最前面 // 被點(diǎn)擊到的空白方塊,繪制下填充灰色方塊提示框 drawBlockHint(clicked_i,clicked_j,RGB(100,100,100),1); // 定義數(shù)組,存儲上、下、左、右四個(gè)方向找到第一個(gè)不是空白的方塊 Block fourBlocks[4] = {blocks[clicked_i][clicked_j]}; // 初始化為這個(gè)空白的點(diǎn)擊的方塊 int search; // 尋找下標(biāo) // 向上找 for (search=0;clicked_i-search>=0;search++) { if (blocks[clicked_i-search][clicked_j].colorId!=0) // 找到第一個(gè)顏色不是空白的方塊 { fourBlocks[0] = blocks[clicked_i-search][clicked_j]; // 賦給這個(gè)存儲的數(shù)組 break; } } // 向下找 for (search=0;clicked_i+search<RowNum;search++) { if (blocks[clicked_i+search][clicked_j].colorId!=0) // 找到第一個(gè)顏色不是空白的方塊 { fourBlocks[1] = blocks[clicked_i+search][clicked_j]; // 賦給這個(gè)存儲的數(shù)組 break; } } // 向左找 for (search=0;clicked_j-search>=0;search++) { if (blocks[clicked_i][clicked_j-search].colorId!=0) // 找到第一個(gè)顏色不是空白的方塊 { fourBlocks[2] = blocks[clicked_i][clicked_j-search]; // 賦給這個(gè)存儲的數(shù)組 break; } } // 向右找 for (search=0;clicked_j+search<ColNum;search++) { if (blocks[clicked_i][clicked_j+search].colorId!=0) // 找到第一個(gè)顏色不是空白的方塊 { fourBlocks[3] = blocks[clicked_i][clicked_j+search]; // 賦給這個(gè)存儲的數(shù)組 break; } } // 統(tǒng)計(jì)fourBlocks的四個(gè)小方塊,有沒有同樣顏色數(shù)目大于等于2的 int colorStatistics[ColorTypeNum+1] = {0}; // 初始化個(gè)數(shù)為0 int isBadClick = 1; // 假設(shè)點(diǎn)擊的方塊不合適,十字區(qū)域沒有有效消除的方塊 for (i=1;i<ColorTypeNum+1;i++) // i=0是空白顏色,不要統(tǒng)計(jì) { for (j=0;j<4;j++) // 遍歷fourBlocks { if (fourBlocks[j].colorId==i) colorStatistics[i]++; // 方塊顏色為非零的i的話,把對應(yīng)的統(tǒng)計(jì)個(gè)數(shù)+1 } if (colorStatistics[i]>=2) // 如果這種顏色方塊個(gè)數(shù)大于等于2 { isBadClick = 0; // 能消除了,這次點(diǎn)擊是好的操作 // 把對應(yīng)十字區(qū)域要消除的方塊顏色改成空白顏色 for (j=0;j<4;j++) // 遍歷fourBlocks { if (fourBlocks[j].colorId==i) { // 要消除的方塊區(qū)域繪制提示框 drawBlockHint(fourBlocks[j].i,fourBlocks[j].j,RGB(0,0,0),0); // 顏色序號設(shè)為0,也就是空白的灰白色 blocks[fourBlocks[j].i][fourBlocks[j].j].colorId = 0; } } score += colorStatistics[i]; // 得分加上消除的方塊數(shù) } } // 點(diǎn)擊的方塊,十字區(qū)域沒有能消除的方塊,為錯(cuò)誤點(diǎn)擊,減去10秒鐘時(shí)間 if (isBadClick==1) totalTime -= 10; FlushBatchDraw(); // 批量繪制 Sleep(300); // 繪制好提示框后暫停300毫秒 } // while 當(dāng)按下鼠標(biāo)左鍵時(shí) } } int main() // 主函數(shù)運(yùn)行 { startup(); while (1) { show(); updateWithoutInput(); updateWithInput(); } closegraph(); return 0; }
這一章主要講解了指針的相關(guān)語法知識,學(xué)習(xí)了倒計(jì)時(shí)的方法,實(shí)現(xiàn)了十字消除游戲。讀者可以嘗試在本章代碼基礎(chǔ)上繼續(xù)改進(jìn):
1、實(shí)現(xiàn)隨著游戲的進(jìn)行,通過關(guān)卡要求消除方塊的比例越來越高;
2、利用文件讀寫,實(shí)現(xiàn)關(guān)卡數(shù)據(jù)與最高分的記錄與讀取。
到此這篇關(guān)于C語言實(shí)現(xiàn)消消樂游戲的代碼分享的文章就介紹到這了,更多相關(guān)C語言消消樂游戲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
超詳細(xì)解析C++實(shí)現(xiàn)歸并排序算法
歸并排序是比較穩(wěn)定的排序方法。它的基本思想是把待排序的元素分解成兩個(gè)規(guī)模大致相等的子序列。本文將用C++實(shí)現(xiàn)這一排序算法,需要的可以參考一下2022-09-09C語言實(shí)現(xiàn)掃雷游戲(可以自動(dòng)展開)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)掃雷游戲,可以自動(dòng)展開,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11C語言實(shí)現(xiàn)單位車輛調(diào)度管理
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)單位車輛調(diào)度管理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03C++實(shí)現(xiàn)LeetCode(647.回文子字符串)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(647.回文子字符串),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07