如何用c語言完成俄羅斯方塊小游戲
這估計是你在編程學(xué)習(xí)過程中的第一個小游戲開發(fā),怎么說呢,在這里只針對剛學(xué)程序設(shè)計的學(xué)生,就是說剛接觸C語言沒多久,有一點功底的學(xué)生看看,簡陋的代碼,簡陋的實現(xiàn)
注:該代碼內(nèi)注釋加入個人理解,若有錯誤,多多包涵
由于不需要插件,繪制出來的玩意恐怕不夠美觀,多多理解
語言:C的不能再C。(沒涉及啥很高深的玩意)
(高級一點的話用EasyX插件里面的玩意,那就很炫酷了)
涉及內(nèi)容:光標(biāo)移動,顏色,圖形繪制、消去、覆蓋和打印,鍵盤按鍵敲擊的讀取,計時器,文件的創(chuàng)建與讀取
看似簡單,實際上一點也不難
1. 游戲概述
俄羅斯方塊是一款經(jīng)典的益智游戲,主要功能包括:
- 顯示游戲界面
- 隨機生成方塊并控制其移動和旋轉(zhuǎn)
- 消除完整行
- 記錄并保存最高分
- 游戲結(jié)束判斷
本代碼使用控制臺操作,并通過系統(tǒng)函數(shù)控制光標(biāo)位置、打印字符實現(xiàn)圖形效果。
2. 游戲開發(fā)所需知識
在實現(xiàn)類似游戲之前,需掌握以下知識:
- 控制臺操作:控制光標(biāo)位置 (
CursorJump
) 和隱藏光標(biāo) (HideCursor
)。 - 顏色設(shè)置:通過
SetConsoleTextAttribute
改變文本顏色。 - 二維數(shù)組操作:表示游戲區(qū)域與方塊信息。
- 隨機數(shù)生成:使用
rand()
生成隨機方塊。 - 鍵盤事件:讀取按鍵事件(如
_kbhit
和_getch
)。 - 文件操作:保存和讀取游戲歷史最高分。
3. 代碼解讀
3.1 數(shù)據(jù)結(jié)構(gòu)
Face
- 表示整個游戲界面。
data
: 標(biāo)記某處是否有方塊(0無,1有)。color
: 存儲方塊顏色信息。
Block
- 存儲7種方塊的4種形態(tài)。
- 通過二維數(shù)組
space
表示方塊形狀。
struct Face { int data[ROW][COL + 10]; int color[ROW][COL + 10]; }; struct Block { int space[4][4]; } block[7][4];
3.2 核心模塊
(1) 界面初始化
使用InitInterface
繪制邊框與提示信息:
- 通過
CursorJump
設(shè)置光標(biāo)位置。 - 使用
printf("■")
繪制邊框。 - 邊框信息存入
face.data
。
void InitInterface() { color(7); for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL + 10; j++) { if (j == 0 || j == COL - 1 || j == COL + 9) { face.data[i][j] = 1; CursorJump(2 * j, i); printf("■"); } } } }
(2) 方塊初始化
InitBlockInfo
存儲方塊的7種形狀和4種旋轉(zhuǎn)狀態(tài):
- 每種形狀的基本狀態(tài)由
block[shape][0]
定義。 - 使用二維旋轉(zhuǎn)公式生成其余三種形態(tài)。
for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { block[shape][form + 1].space[i][j] = temp[3 - j][i]; } }
(3) 方塊繪制與移動
DrawBlock
:在指定位置打印方塊。DrawSpace
:用空格覆蓋指定位置,實現(xiàn)“擦除”效果。
void DrawBlock(int shape, int form, int x, int y) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (block[shape][form].space[i][j] == 1 && y + i > 0) { CursorJump(2 * (x + j), y + i); printf("■"); } } } }
(4) 鍵盤事件處理
通過_getch
檢測按鍵:
LEFT
:左移RIGHT
:右移DOWN
:快速下落SPACE
:旋轉(zhuǎn)
switch (ch) { case LEFT: if (IsLegal(shape, form, x - 1, y)) x--; break; case RIGHT: if (IsLegal(shape, form, x + 1, y)) x++; break; case SPACE: if (IsLegal(shape, (form + 1) % 4, x, y)) form = (form + 1) % 4; break; }
(5) 行清除與得分判斷
JudeFunc
實現(xiàn):
- 檢查行是否滿。
- 若滿,則消除行并將上方行下移。
- 更新得分。
if (sum == COL - 2) { grade += 10; for (int j = 1; j < COL - 1; j++) { face.data[i][j] = 0; CursorJump(2 * j, i); printf(" "); } }
(6) 游戲邏輯與結(jié)束判斷
StartGame
控制方塊下落、鍵盤響應(yīng)與結(jié)束判斷:
- 隨機生成方塊并控制其運動。
- 檢測頂層是否有方塊,若有則結(jié)束游戲。
(7) 文件操作
ReadGrade
與WriteGrade
負責(zé)讀取和保存最高分:
- 通過
fopen
打開文件,保存最高分。 - 使用二進制方式存儲成績,防止文本存儲帶來的額外處理。
FILE* pf = fopen("record.txt", "r"); fread(&max, sizeof(int), 1, pf);
4. 編寫俄羅斯方塊的步驟
- 明確需求:
- 游戲邏輯(下落、移動、旋轉(zhuǎn)、消除)。
- 數(shù)據(jù)存儲(方塊形態(tài)、游戲區(qū)域)。
- 圖形展示(控制臺或GUI)。
- 搭建基礎(chǔ)框架:
- 初始化游戲區(qū)域、邊框與方塊。
- 定義隨機生成與旋轉(zhuǎn)邏輯。
- 逐步完善:
- 實現(xiàn)消除與得分邏輯。
- 增加鍵盤事件處理。
- 添加文件操作功能。
- 優(yōu)化與美化:
- 增加動畫效果。
- 提高界面美觀度(如使用插件或圖形庫)。
通過本代碼,你可以學(xué)習(xí)到俄羅斯方塊的實現(xiàn)核心邏輯,同時可以在此基礎(chǔ)上嘗試更高級的圖形化實現(xiàn),如使用EasyX插件或其他圖形庫,進一步提升游戲的視覺體驗和功能豐富性。
您說:
對每一步進行詳細的介紹
ChatGPT 說:
ChatGPT
編寫俄羅斯方塊游戲的詳細步驟與說明
本文將從代碼結(jié)構(gòu)與邏輯出發(fā),逐步拆解如何實現(xiàn)一個俄羅斯方塊游戲,幫助理解并指導(dǎo)如何從零開始構(gòu)建類似的游戲。
1. 明確需求與設(shè)計思路
俄羅斯方塊的基本玩法包括以下幾部分:
- 隨機生成方塊:從7種形狀中隨機生成一個方塊,并在游戲區(qū)域頂部下落。
- 方塊運動控制:通過鍵盤實現(xiàn)左右移動、加速下落和旋轉(zhuǎn)操作。
- 碰撞檢測與消行:檢測方塊是否碰到其他方塊或到達底部,并判斷是否需要消行。
- 得分與游戲結(jié)束:計算得分,判斷是否達到游戲結(jié)束條件。
- 數(shù)據(jù)持久化:保存和讀取最高分記錄。
設(shè)計中還需考慮:
- 游戲的界面繪制。
- 使用二維數(shù)組存儲游戲區(qū)域狀態(tài)。
- 持續(xù)的方塊生成與處理邏輯。
2. 搭建基礎(chǔ)框架
2.1 確定游戲區(qū)域和方塊結(jié)構(gòu)
俄羅斯方塊的核心在于使用二維數(shù)組存儲數(shù)據(jù)狀態(tài):
Face
結(jié)構(gòu)體:存儲整個游戲區(qū)域狀態(tài)。
data
數(shù)組:記錄每個位置是否有方塊。color
數(shù)組:記錄每個位置的顏色。
Block
結(jié)構(gòu)體:存儲所有方塊的形狀及其4種旋轉(zhuǎn)狀態(tài)。
#define ROW 29 // 游戲區(qū)域行數(shù) #define COL 20 // 游戲區(qū)域列數(shù) struct Face { int data[ROW][COL + 10]; // 游戲區(qū)域方塊標(biāo)記 int color[ROW][COL + 10]; // 方塊顏色 }; struct Block { int space[4][4]; // 存儲方塊形狀 } block[7][4]; // 7種形狀,每種4種狀態(tài)
2.2 初始化游戲區(qū)域與邊框
繪制游戲界面的邊框,展示基本信息(如得分、提示):
- 利用雙重循環(huán),遍歷所有格子。
- 使用特定符號(如
■
)繪制邊框。 - 在右側(cè)區(qū)域打印提示文字。
代碼實現(xiàn):
void InitInterface() { color(7); // 設(shè)置邊框顏色為白色 for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL + 10; j++) { if (j == 0 || j == COL - 1 || j == COL + 9) { face.data[i][j] = 1; // 邊框標(biāo)記為有方塊 CursorJump(2 * j, i); printf("■"); } else if (i == 0 || i == ROW - 1) { face.data[i][j] = 1; // 頂部和底部邊框 printf(" ■"); } else { face.data[i][j] = 0; // 非邊框區(qū)域標(biāo)記為空 } } } // 打印右側(cè)提示信息 CursorJump(2 * COL, 1); printf("下一個方塊:"); CursorJump(2 * COL + 4, ROW - 3); printf("當(dāng)前分數(shù):%d", grade); }
3. 初始化方塊形狀與邏輯
3.1 定義方塊形狀
俄羅斯方塊共有7種形狀,每種形狀有4種旋轉(zhuǎn)狀態(tài)。通過二維數(shù)組定義每種形狀的初始狀態(tài),并通過旋轉(zhuǎn)公式生成其他狀態(tài)。
代碼實現(xiàn):
void InitBlockInfo() { // 定義“T”形 for (int i = 0; i <= 2; i++) block[0][0].space[1][i] = 1; block[0][0].space[2][1] = 1; // 定義其他形狀... // 通過旋轉(zhuǎn)公式生成每種形狀的其他3種狀態(tài) for (int shape = 0; shape < 7; shape++) { for (int form = 0; form < 3; form++) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { block[shape][form + 1].space[i][j] = block[shape][form].space[3 - j][i]; } } } } }
4. 方塊的運動控制
4.1 鍵盤事件檢測
通過_kbhit
檢測鍵盤輸入,并用_getch
捕獲按鍵值:
LEFT
:左移。RIGHT
:右移。DOWN
:加速下落。SPACE
:旋轉(zhuǎn)。
實現(xiàn)示例:
char ch = getch(); switch (ch) { case LEFT: if (IsLegal(shape, form, x - 1, y)) x--; break; case RIGHT: if (IsLegal(shape, form, x + 1, y)) x++; break; case SPACE: if (IsLegal(shape, (form + 1) % 4, x, y)) form = (form + 1) % 4; break; case ESC: exit(0); // 退出游戲 }
4.2 碰撞檢測
使用IsLegal
函數(shù)判斷方塊是否可以移動或旋轉(zhuǎn):
- 檢查方塊每個格子是否會越界或與已有方塊重疊。
- 返回
1
表示合法,0
表示不合法。
int IsLegal(int shape, int form, int x, int y) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (block[shape][form].space[i][j] == 1 && (x + j < 0 || x + j >= COL || y + i >= ROW || face.data[y + i][x + j] == 1)) { return 0; // 非法 } } } return 1; // 合法 }
5. 方塊落地與消行
5.1 方塊落地
每次方塊下落時:
- 檢查其下一步是否越界或碰到其他方塊。
- 如果不能繼續(xù)下落,則將當(dāng)前方塊寫入
Face
結(jié)構(gòu)。
5.2 行滿判斷與消除
當(dāng)某一行填滿時:
- 刪除該行并將上方所有行下移。
- 更新分數(shù)。
代碼實現(xiàn):
int JudeFunc() { for (int i = ROW - 2; i > 4; i--) { int sum = 0; for (int j = 1; j < COL - 1; j++) sum += face.data[i][j]; if (sum == COL - 2) { // 滿行 grade += 10; for (int j = 1; j < COL - 1; j++) { face.data[i][j] = 0; CursorJump(2 * j, i); printf(" "); } for (int m = i; m > 1; m--) { // 上方行下移 for (int n = 1; n < COL - 1; n++) { face.data[m][n] = face.data[m - 1][n]; face.color[m][n] = face.color[m - 1][n]; } } } } return 0; }
6. 游戲結(jié)束與得分記錄
6.1 游戲結(jié)束判斷
若方塊到達頂層,則游戲結(jié)束,提示玩家是否重開一局。
6.2 保存與讀取最高分
通過文件操作保存最高分:
- 使用
fopen
以只讀或?qū)懩J酱蜷_文件。 - 利用
fwrite
保存分數(shù)。
代碼實現(xiàn):
void ReadGrade() { FILE* pf = fopen("record.txt", "r"); if (pf == NULL) { pf = fopen("record.txt", "w"); fwrite(&grade, sizeof(int), 1, pf); } fread(&max, sizeof(int), 1, pf); fclose(pf); }
7. 整合與優(yōu)化
- 游戲主循環(huán): 控制方塊生成、運動、碰撞和消除,維護游戲邏輯流。
- 視覺優(yōu)化: 增加更多顏色和圖案設(shè)計,使游戲界面更美觀。
以上是詳細的游戲開發(fā)步驟,你可以在此基礎(chǔ)上進行個性化擴展,比如增加背景音樂、提升圖形效果,或者引入難度等級功能。
c(下面的代碼經(jīng)過了詳細的注釋,希望大家慢慢研究)
//作品:tetris(帶注釋) // by:BoBoo文睡不醒 //注:該代碼內(nèi)注釋加入個人理解,若有錯誤,多多包涵 //由于不需要插件,繪制出來的玩意恐怕不夠美觀,多多理解 //語言:C的不能再C。(沒涉及啥很高深的玩意)(高級一點的話用EasyX插件里面的玩意,那就很炫酷了) //涉及內(nèi)容:光標(biāo)移動,顏色,圖形繪制、消去、覆蓋和打印,鍵盤按鍵敲擊的讀取,計時器,文件的創(chuàng)建與讀取 //看似簡單,實際上一點也不難 //頭文件部分: #include <stdio.h> #include <Windows.h> //提供了一些在控制臺程序中進行輸入輸出、控制光標(biāo)位置、設(shè)置文本屬性等方面的函數(shù)和常量 //在代碼中使用了其中的一些函數(shù)比如SetConsoleCursorPosition()、SetConsoleTextAttribute()來控制光標(biāo)位置和設(shè)置文本顏色 #include <stdlib.h> //這個頭文件包含了一些常用的函數(shù)和類型定義,用于進行內(nèi)存分配、進程控制、字符串轉(zhuǎn)換等操作 #include <time.h> //這個頭文件包含了一些與時間相關(guān)的函數(shù)和類型定義 #include <conio.h> //于檢測鍵盤輸入字符,以及在控制臺上進行一些簡單的控制,比如隱藏光標(biāo)等 //在代碼中用于于檢測鍵盤輸入,比如判斷是否按下了特定的方向鍵或者空格鍵,從而控制俄羅斯方塊的移動、旋轉(zhuǎn)等操作。 //宏定義 #define ROW 29 //游戲區(qū)行數(shù) #define COL 20 //游戲區(qū)列數(shù) //接下來說明敲擊鍵盤該按鍵的值 #define DOWN 80 //方向鍵:下 #define LEFT 75 //方向鍵:左 #define RIGHT 77 //方向鍵:右 #define SPACE 32 //空格鍵 #define ESC 27 //Esc鍵 //兩個結(jié)構(gòu)體 struct Face { int data[ROW][COL + 10]; //用于標(biāo)記指定位置是否有方塊(1為有,0為無) int color[ROW][COL + 10]; //用于記錄指定位置的方塊顏色編碼 }face; struct Block { int space[4][4]; }block[7][4]; //用于存儲7種基本形狀方塊的各自的4種形態(tài)的信息,共28種 //函數(shù)部分?函數(shù)。 //隱藏光標(biāo) void HideCursor(); //光標(biāo)跳轉(zhuǎn) void CursorJump(int x, int y); //初始化界面 void InitInterface(); //初始化方塊信息 void InitBlockInfo(); //顏色設(shè)置 void color(int num); //畫出方塊 void DrawBlock(int shape, int form, int x, int y); //空格覆蓋 void DrawSpace(int shape, int form, int x, int y); //合法性判斷 int IsLegal(int shape, int form, int x, int y); //判斷得分與結(jié)束 int JudeFunc(); //游戲主體邏輯函數(shù) void StartGame(); //從文件讀取最高分 void ReadGrade(); //更新最高分到文件 void WriteGrade(); //全局變量 int max, grade; //主函數(shù) int main() { #pragma warning (disable:4996) //消除警告 max = 0, grade = 0; //初始化變量 system("title 俄羅斯方塊"); //設(shè)置cmd窗口的名字 system("mode con lines=29 cols=60"); //設(shè)置cmd窗口的大小 HideCursor(); //隱藏光標(biāo) ReadGrade(); //從文件讀取最高分到max變量 InitInterface(); //初始化界面 InitBlockInfo(); //初始化方塊信息 srand((unsigned int)time(NULL)); //設(shè)置隨機數(shù)生成的起點 StartGame(); //開始游戲 return 0; } //關(guān)于隱藏光標(biāo)的操作(不懂他在干啥)(估計就是隱藏光標(biāo)) void HideCursor() { CONSOLE_CURSOR_INFO curInfo; //定義光標(biāo)信息的結(jié)構(gòu)體變量 curInfo.dwSize = 1; //如果沒賦值的話,隱藏光標(biāo)無效 curInfo.bVisible = FALSE; //將光標(biāo)設(shè)置為不可見 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //獲取控制臺句柄 SetConsoleCursorInfo(handle, &curInfo); //設(shè)置光標(biāo)信息 } //為了解清晰其中,關(guān)鍵結(jié)構(gòu)CONSOLE_CURSOR_INFO在其頭文件當(dāng)中的內(nèi)容如下: // (自己去查,我也不知道) //光標(biāo)跳轉(zhuǎn)(在屏幕上進行輸出時,我們需要光標(biāo)先移動到目標(biāo)位置再進行輸出,因此,光標(biāo)跳轉(zhuǎn)函數(shù)也是必不可少的) void CursorJump(int x, int y) { COORD pos; //定義光標(biāo)位置的結(jié)構(gòu)體變量 pos.X = x; //橫坐標(biāo)設(shè)置 pos.Y = y; //縱坐標(biāo)設(shè)置 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //獲取控制臺句柄 SetConsoleCursorPosition(handle, pos); //設(shè)置光標(biāo)位置 } //初始化界面 //主界面的外層的邊框打印 void InitInterface() { color(7); //顏色設(shè)置為白色 for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL + 10; j++) { if (j == 0 || j == COL - 1 || j == COL + 9) { face.data[i][j] = 1; //標(biāo)記該位置有方塊 CursorJump(2 * j, i); printf("■"); } else if (i == ROW - 1 || i == 0)//首末兩行邊框的打印 { face.data[i][j] = 1; //標(biāo)記該位置有方塊 printf(" ■");//此處予以說明,為了整齊起見,每行方塊之間加入空格。下方代碼同理 } else face.data[i][j] = 0; //標(biāo)記該位置無方塊 } } for (int i = COL; i < COL + 10; i++)//右側(cè)邊框分隔線 { face.data[8][i] = 1; //標(biāo)記該位置有方塊 CursorJump(2 * i, 8); printf("■"); } //從這里開始就是寫右側(cè)邊框中的文字與提示語句(無需細看,無腦打印,想整什么活都可以整) CursorJump(2 * COL, 1); printf("下一個方塊:"); CursorJump(2 * COL + 4, ROW - 19); printf("左移:←"); CursorJump(2 * COL + 4, ROW - 17); printf("右移:→"); CursorJump(2 * COL + 4, ROW - 15); printf("加速:↓"); CursorJump(2 * COL + 4, ROW - 13); printf("旋轉(zhuǎn):空格"); CursorJump(2 * COL + 4, ROW - 11); printf("暫停: S"); CursorJump(2 * COL + 4, ROW - 9); printf("退出: Esc"); CursorJump(2 * COL + 4, ROW - 7); printf("重新開始:R"); CursorJump(2 * COL + 4, ROW - 5); printf("最高紀(jì)錄:%d", max); CursorJump(2 * COL + 4, ROW - 3); printf("當(dāng)前分數(shù):%d", grade); } //初始化方塊信息 //存儲各種形狀的俄羅斯方塊 void InitBlockInfo() { //“T”形 for (int i = 0; i <= 2; i++) block[0][0].space[1][i] = 1; block[0][0].space[2][1] = 1; //“L”形 for (int i = 1; i <= 3; i++) block[1][0].space[i][1] = 1; block[1][0].space[3][2] = 1; //“J”形 for (int i = 1; i <= 3; i++) block[2][0].space[i][2] = 1; block[2][0].space[3][1] = 1; for (int i = 0; i <= 1; i++) { //“Z”形 block[3][0].space[1][i] = 1; block[3][0].space[2][i + 1] = 1; //“S”形 block[4][0].space[1][i + 1] = 1; block[4][0].space[2][i] = 1; //“O”形 block[5][0].space[1][i + 1] = 1; block[5][0].space[2][i + 1] = 1; } //“I”形 for (int i = 0; i <= 3; i++) block[6][0].space[i][1] = 1; int temp[4][4]; for (int shape = 0; shape < 7; shape++) //7種形狀 { for (int form = 0; form < 3; form++) //4種形態(tài)(已經(jīng)有了一種,這里每個還需增加3種) { //獲取第form種形態(tài) for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { temp[i][j] = block[shape][form].space[i][j]; } } //將第form種形態(tài)順時針旋轉(zhuǎn),得到第form+1種形態(tài) for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { block[shape][form + 1].space[i][j] = temp[3 - j][i]; } } } } } //顏色設(shè)置 void color(int c) { switch (c) { case 0: c = 13; //“T”形方塊設(shè)置為紫色 break; case 1: case 2: c = 12; //“L”形和“J”形方塊設(shè)置為紅色 break; case 3: case 4: c = 10; //“Z”形和“S”形方塊設(shè)置為綠色 break; case 5: c = 14; //“O”形方塊設(shè)置為黃色 break; case 6: c = 11; //“I”形方塊設(shè)置為淺藍色 break; default: c = 7; //其他默認設(shè)置為白色(若需將顏色重置可把color變?yōu)?) break; } SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //顏色設(shè)置 //注:SetConsoleTextAttribute是一個API(應(yīng)用程序編程接口)(from百度,自行了解) } //畫出方塊 void DrawBlock(int shape, int form, int x, int y) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (block[shape][form].space[i][j] == 1 && y + i > 0) //如果該位置有方塊 { CursorJump(2 * (x + j), y + i); //光標(biāo)跳轉(zhuǎn)到指定位置 printf("■"); //輸出方塊 } } } //由于不怎么會如何讓打印的圖形不影響邊框(上邊框),于是愚蠢的我決定再打印一遍上邊框 //大家有更好的方法自行嘗試 color(7);//不加的話第一行的顏色會與圖形的顏色相同 for (int j = 0; j < COL + 10; j++) { if (j == 0 || j == COL - 1 || j == COL + 9) { CursorJump(2 * j, 0); printf("■"); } else { printf(" ■"); } } } //空格覆蓋?。?! void DrawSpace(int shape, int form, int x, int y) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (block[shape][form].space[i][j] == 1) //如果該位置有方塊 { CursorJump(2 * (x + j), y + i); //光標(biāo)跳轉(zhuǎn)到指定位置 printf(" "); //打印空格覆蓋(兩個空格) } } } } //合法性判斷 int IsLegal(int shape, int form, int x, int y) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { //如果方塊落下的位置本來就已經(jīng)有方塊了,則不合法 if ((block[shape][form].space[i][j] == 1) && (face.data[y + i][x + j] == 1)) return 0; //不合法 } } return 1; //合法 } //判斷得分與結(jié)束 int JudeFunc() { //判斷是否得分 for (int i = ROW - 2; i > 4; i--) { int sum = 0; //記錄第i行的方塊個數(shù) for (int j = 1; j < COL - 1; j++) { sum += face.data[i][j]; //統(tǒng)計第i行的方塊個數(shù) } if (sum == 0) //該行沒有方塊,無需再判斷其上的層次(無需再繼續(xù)判斷是否得分) break; //跳出循環(huán) if (sum == COL - 2) //該行全是方塊,可得分 { grade += 10; //滿一行加10分 color(7); //顏色設(shè)置為白色 CursorJump(2 * COL + 4, ROW - 3); //光標(biāo)跳轉(zhuǎn)到顯示當(dāng)前分數(shù)的位置 printf("當(dāng)前分數(shù):%d", grade); //更新當(dāng)前分數(shù) for (int j = 1; j < COL - 1; j++) //清除得分行的方塊信息 { face.data[i][j] = 0; //該位置得分后被清除,標(biāo)記為無方塊 CursorJump(2 * j, i); //光標(biāo)跳轉(zhuǎn)到該位置 printf(" "); //打印空格覆蓋(兩個空格) } //把被清除行上面的行整體向下挪一格 for (int m = i; m > 1; m--) { sum = 0; //記錄上一行的方塊個數(shù) for (int n = 1; n < COL - 1; n++) { sum += face.data[m - 1][n]; //統(tǒng)計上一行的方塊個數(shù) face.data[m][n] = face.data[m - 1][n]; //將上一行方塊的標(biāo)識移到下一行 face.color[m][n] = face.color[m - 1][n]; //將上一行方塊的顏色編號移到下一行 if (face.data[m][n] == 1) //上一行移下來的是方塊,打印方塊 { CursorJump(2 * n, m); //光標(biāo)跳轉(zhuǎn)到該位置 color(face.color[m][n]); //顏色設(shè)置為還方塊的顏色 printf("■"); //打印方塊 } else //上一行移下來的是空格,打印空格 { CursorJump(2 * n, m); //光標(biāo)跳轉(zhuǎn)到該位置 printf(" "); //打印空格(兩個空格) } } if (sum == 0) //上一行移下來的全是空格,無需再將上層的方塊向下移動(移動結(jié)束) return 1; //返回1,表示還需調(diào)用該函數(shù)進行判斷(移動下來的可能還有滿行) } } } //判斷游戲是否結(jié)束 for (int j = 1; j < COL - 1; j++) { if (face.data[1][j] == 1) //頂層有方塊存在(以第1行為頂層,不是第0行) { Sleep(1000); //留給玩家反應(yīng)時間 system("cls"); //清空屏幕 color(7); //顏色設(shè)置為白色 CursorJump(2 * (COL / 3), ROW / 2 - 9); if (grade > max)//接下來就是曼妙的人機交互部分 { printf("恭喜你打破最高記錄,最高記錄更新為%d", grade); WriteGrade(); } else if (grade == max) { printf("與最高記錄持平,加油再創(chuàng)佳績", grade); } else { printf("請繼續(xù)加油,當(dāng)前與最高記錄相差%d", max - grade); } CursorJump(2 * (COL / 3), ROW / 2 - 6); printf("GAME OVER"); while (1) { char ch; CursorJump(2 * (COL / 3), ROW / 2 - 3); printf("再來一局?"); CursorJump(2 * (COL / 3), ROW / 2); printf("輸入y并敲擊回車(y/n)進入下一局;"); CursorJump(2 * (COL / 3), ROW / 2 + 3); printf("輸入n并敲擊回車(n/n)結(jié)束游戲;"); CursorJump(2 * (COL / 3), ROW / 2 + 6); printf("敲擊esc則退出游戲;"); if (kbhit() != 0) { printf("Y"); if (getch() == 27) { printf("Y"); system("cls"); //清空屏幕 color(7); CursorJump(COL, ROW / 2); printf(" 游戲結(jié)束 "); CursorJump(COL, ROW / 2 + 2); exit(0); //結(jié)束程序 } //疑點:上面的if判斷和執(zhí)行代碼無法實現(xiàn),還未研究明白****** } scanf("%c", &ch); if (ch == 'y' || ch == 'Y') { system("cls"); main(); } else if (ch == 'n' || ch == 'N') { CursorJump(2 * (COL / 3), ROW / 2 + 5); exit(0); } else { CursorJump(2 * (COL / 3), ROW / 2 + 4); printf("選擇錯誤,請再次選擇"); } } } } return 0; //判斷結(jié)束,無需再調(diào)用該函數(shù)進行判斷 } //游戲主體邏輯函數(shù)!?。? void StartGame() { int shape = rand() % 7, form = rand() % 4; //隨機獲取方塊的形狀和形態(tài) while (1) { int t = 0; int nextShape = rand() % 7, nextForm = rand() % 4; //隨機獲取下一個方塊的形狀和形態(tài) int x = COL / 2 - 2, y = 0; //方塊初始下落位置的橫縱坐標(biāo) color(nextShape); //顏色設(shè)置為下一個方塊的顏色 DrawBlock(nextShape, nextForm, COL + 3, 3); //將下一個方塊顯示在右上角 while (1) { color(shape); //顏色設(shè)置為當(dāng)前正在下落的方塊 DrawBlock(shape, form, x, y); //將該方塊顯示在初始下落位置 if (t == 0) { t = 15000; //這里t越小,方塊下落越快(可以根據(jù)此設(shè)置游戲難度,我的評價是可以搞事) } while (--t) { if (kbhit() != 0) //若鍵盤被敲擊,則退出循環(huán) break; } if (t == 0) //鍵盤未被敲擊 { if (IsLegal(shape, form, x, y + 1) == 0) //方塊再下落就不合法了(已經(jīng)到達底部) { //將當(dāng)前方塊的信息錄入face當(dāng)中 //face:記錄界面的每個位置是否有方塊,若有方塊還需記錄該位置方塊的顏色。 for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (block[shape][form].space[i][j] == 1) { face.data[y + i][x + j] = 1; //將該位置標(biāo)記為有方塊 face.color[y + i][x + j] = shape; //記錄該方塊的顏色數(shù)值 } } } while (JudeFunc()); //判斷此次方塊下落是否得分以及游戲是否結(jié)束 break; //跳出當(dāng)前死循環(huán),準(zhǔn)備進行下一個方塊的下落 } else //未到底部 { DrawSpace(shape, form, x, y); //用空格覆蓋當(dāng)前方塊所在位置 y++; //縱坐標(biāo)自增(下一次顯示方塊時就相當(dāng)于下落了一格了) } } else //鍵盤被敲擊 { //對敲擊鍵盤的按鍵的判斷 //(不理解為什么這個對,上面那個ESC的判斷就有問題)****** char ch = getch(); //讀取keycode switch (ch) { case DOWN: //方向鍵:下 if (IsLegal(shape, form, x, y + 1) == 1) //判斷方塊向下移動一位后是否合法 { //方塊下落后合法才進行以下操作 DrawSpace(shape, form, x, y); //用空格覆蓋當(dāng)前方塊所在位置 y++; //縱坐標(biāo)自增(下一次顯示方塊時就相當(dāng)于下落了一格了) } break; case LEFT: //方向鍵:左 if (IsLegal(shape, form, x - 1, y) == 1) //判斷方塊向左移動一位后是否合法 { //方塊左移后合法才進行以下操作 DrawSpace(shape, form, x, y); //用空格覆蓋當(dāng)前方塊所在位置 x--; //橫坐標(biāo)自減(下一次顯示方塊時就相當(dāng)于左移了一格了) } break; case RIGHT: //方向鍵:右 if (IsLegal(shape, form, x + 1, y) == 1) //判斷方塊向右移動一位后是否合法 { //方塊右移后合法才進行以下操作 DrawSpace(shape, form, x, y); //用空格覆蓋當(dāng)前方塊所在位置 x++; //橫坐標(biāo)自增(下一次顯示方塊時就相當(dāng)于右移了一格了) } break; case SPACE: //空格鍵 if (IsLegal(shape, (form + 1) % 4, x, y + 1) == 1) //判斷方塊旋轉(zhuǎn)后是否合法 { //方塊旋轉(zhuǎn)后合法才進行以下操作 DrawSpace(shape, form, x, y); //用空格覆蓋當(dāng)前方塊所在位置 y++; //縱坐標(biāo)自增(總不能原地旋轉(zhuǎn)吧) form = (form + 1) % 4; //方塊的形態(tài)自增(下一次顯示方塊時就相當(dāng)于旋轉(zhuǎn)了) } break; case ESC: //Esc鍵 system("cls"); //清空屏幕 color(7); CursorJump(COL, ROW / 2); printf(" 游戲結(jié)束 "); CursorJump(COL, ROW / 2 + 2); exit(0); //結(jié)束程序 case 's': case 'S': //暫停 system("pause>nul"); //暫停(按任意鍵繼續(xù)) break; case 'r': case 'R': //重新開始 system("cls"); //清空屏幕 main(); //重新執(zhí)行主函數(shù) } } } shape = nextShape, form = nextForm; //獲取下一個方塊的信息 DrawSpace(nextShape, nextForm, COL + 3, 3); //將右上角的方塊信息用空格覆蓋 } } //從文件讀取最高分 void ReadGrade() { FILE* pf = fopen("俄羅斯方塊最高得分記錄.txt", "r"); //以只讀方式打開文件 if (pf == NULL) //打開文件失敗 { pf = fopen("俄羅斯方塊最高得分記錄.txt", "w"); //以只寫方式打開文件(文件不存在可以自動創(chuàng)建該文件) fwrite(&grade, sizeof(int), 1, pf); //將max寫入文件(此時max為0),即將最高歷史得分初始化為0 } fseek(pf, 0, SEEK_SET); //使文件指針pf指向文件開頭 fread(&max, sizeof(int), 1, pf); //讀取文件中的最高歷史得分到max當(dāng)中 fclose(pf); //關(guān)閉文件 pf = NULL; //文件指針及時置空 } //更新最高分到文件 void WriteGrade() { FILE* pf = fopen("俄羅斯方塊最高得分記錄.txt", "w"); //以只寫方式打開文件 if (pf == NULL) //打開文件失敗 { printf("保存最高得分記錄失敗\n"); exit(0); } fwrite(&grade, sizeof(int), 1, pf); //將本局游戲得分寫入文件當(dāng)中(更新最高歷史得分) fclose(pf); //關(guān)閉文件 pf = NULL; //文件指針及時置空 } //over // //還是那句話 //看似簡單,實際上一點也不難
總結(jié)
到此這篇關(guān)于如何用c語言完成俄羅斯方塊小游戲的文章就介紹到這了,更多相關(guān)c語言俄羅斯方塊小游戲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中關(guān)于std::queue?中遇到釋放內(nèi)存錯誤的問題
這篇文章主要介紹了std::queue中遇到釋放內(nèi)存錯誤的問題,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07在C語言中對utmp文件進行查找和寫入操作的函數(shù)小結(jié)
這篇文章主要介紹了在C語言中對utmp文件進行查找和寫入操作的函數(shù)小結(jié),包括pututline()函數(shù)和getutline()函數(shù)以及getutid()函數(shù),需要的朋友可以參考下2015-08-08Qt創(chuàng)建項目實戰(zhàn)之手把手創(chuàng)建第一個Qt項目
我們在進行軟件開發(fā)學(xué)習(xí)時,有時候需要qt軟件進行代碼的敲寫,下面這篇文章主要給大家介紹了關(guān)于Qt創(chuàng)建項目實戰(zhàn)之手把手創(chuàng)建第一個Qt項目的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-04-04