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

