欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何用c語言完成俄羅斯方塊小游戲

 更新時間:2024年12月02日 10:00:50   作者:BoBoo文睡不醒  
這篇文章主要介紹了如何使用C語言開發(fā)一個簡單的俄羅斯方塊游戲,涵蓋了游戲設(shè)計、數(shù)據(jù)結(jié)構(gòu)、核心邏輯和實現(xiàn)步驟,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

這估計是你在編程學(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) 文件操作

ReadGradeWriteGrade負責(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++的多態(tài)性

    深入理解C++的多態(tài)性

    本篇文章是對C++的多態(tài)性進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • c語言printf函數(shù)的使用詳解

    c語言printf函數(shù)的使用詳解

    本篇文章是對c語言中printf函數(shù)的使用進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • C++中關(guān)于std::queue?中遇到釋放內(nèi)存錯誤的問題

    C++中關(guān)于std::queue?中遇到釋放內(nèi)存錯誤的問題

    這篇文章主要介紹了std::queue中遇到釋放內(nèi)存錯誤的問題,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • c++重載的詳細總結(jié)

    c++重載的詳細總結(jié)

    作為成員函數(shù)重載符,對于雙目操作符重載函數(shù)只需一個形參,對于單目操作符重載函數(shù)不需要形參
    2013-09-09
  • C++11新特性之變長參數(shù)模板詳解

    C++11新特性之變長參數(shù)模板詳解

    本文主要介紹了C++11變長參數(shù)模板,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • 在C語言中對utmp文件進行查找和寫入操作的函數(shù)小結(jié)

    在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項目

    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
  • 一篇文章帶你了解C語言二分查找的簡單應(yīng)用

    一篇文章帶你了解C語言二分查找的簡單應(yīng)用

    這篇文章主要介紹了二分查找算法在C語言程序中的使用示例,文中最后提到了使用二分查找法一個需要注意的地方,需要的朋友可以參考下
    2021-08-08
  • C++ 中 vector 的常用操作方法匯總

    C++ 中 vector 的常用操作方法匯總

    在C++的STL中,vector是一個動態(tài)數(shù)組,可以在運行時調(diào)整大小,本文介紹了vector的初始化、元素訪問、修改、迭代器操作、容量管理以及性能優(yōu)化技巧,通過這些操作,可以有效地使用vector管理數(shù)據(jù),本文介紹C++  vector 操作,感興趣的朋友一起看看吧
    2024-10-10
  • C++11系列學(xué)習(xí)之類型推導(dǎo)

    C++11系列學(xué)習(xí)之類型推導(dǎo)

    這篇文章主要介紹了C++11系列學(xué)習(xí)之類型推導(dǎo),文章基于C++的相關(guān)資料展開對主題的詳細內(nèi)容介紹,具有一定的參考價值需要的小伙伴可參考一下
    2022-04-04

最新評論