如何用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負(fù)責(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)前分?jǐn)?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)某一行填滿時:
- 刪除該行并將上方所有行下移。
- 更新分?jǐn)?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保存分?jǐn)?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)前分?jǐn)?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; //其他默認(rèn)設(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)前分?jǐn)?shù)的位置
printf("當(dāng)前分?jǐn)?shù):%d", grade); //更新當(dāng)前分?jǐn)?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-08
Qt創(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

