C語言俄羅斯方塊遊戲解析(圖文+源碼)

 
如下文章來源於嵌入式Linux ,做者寫代碼的籃球球癡

最近在看俄羅斯方塊的遊戲,看到一個大神寫的俄羅斯方塊代碼,很是很是優秀,拿出來解析給你們看看,也但願你們本身嘗試運行試試,從中能獲得一些啓發。c++

#先了解下俄羅斯方塊的幾個形狀

一共分紅 7 形狀,有的形狀有 4種狀態,有的形狀有1種狀態。數組

無論是多少種狀態,一個方塊須要一個2個字節來存儲,也就是16bit來保存一個方塊的信息。ide

基於上面的理論,咱們能夠使用4x4的數組來保存方塊的信息。函數

注意,下面代碼中的 "■" 佔用的是2個字節。設計

#寫代碼來顯示這個方塊

方塊數據,這兩個數組實際上是兩種遊戲模式的方塊信息,咱們只須要分析一種就能夠了。3d

int TGM[7][4]={{0x159D,0x89AB,0x159D,0x89AB},{0x126A,0x4856,0x159A,0x4526},{0x926A,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x5926,0x0156,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x156A,0x4152,0x156A,0x4152}};
int SRS[7][4]={{0x159D,0x89AB,0x26AE,0x4567},{0x0159,0x4856,0x159A,0x4526},{0x8159,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x4815,0x459A,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x0459,0x8596,0x156A,0x4152}};

咱們分析這段代碼code

/***********擦除顯示*************/
int Display(int x, int y, int CAC, int Mode)
{
    for(j=0;j<=3;j++){
        P[j]=CAC&0xF, CAC>>=4;
        if     (Mode==1){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf("■");}
        else if(Mode==0){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf(" ");}
    }
    return 0;
}

P[j]=CAC&0xF 取到的是 4個bit,而後經過判斷這 4個bit決定輸出方塊的位置。blog

咱們拿 0這個方塊的數據0x4859 對應的二進制 0B0100100001011001來作個例子。遊戲

0100  ----> 對應座標(1,0)
1000  ----> 對應座標(2,0)
0101  ----> 對應座標(1,1)
1001  ----> 對應座標(2,1)

經過這個座標,咱們會輸出這樣一個方塊get

再舉個例子

咱們拿 Z這個方塊的數據0x0156 對應的二進制 0B0000000101010110來作個例子。

0000  ----> 對應座標(0,0)
0001  ----> 對應座標(0,1)
0101  ----> 對應座標(1,1)
0110  ----> 對應座標(1,2)

顯示方塊沒有問題了

 

上面的代碼中,若是mode等於0的話,也就是把某個方塊從顯示中擦除掉。

 

咱們用devc++寫的程序,須要知道幾個細節。

#構建固定行列的窗口

正常狀況,咱們顯示cmd是一個默認值的黑框「以下圖」。

實際上,咱們能夠用代碼來固定cmd的框框大小。

默認代碼輸出

示例代碼:

#include   <conio.h>
#include   <stdio.h>
#include  <stdlib.h>
#include    <time.h>
#include <Windows.h>

/**********Main主函數***********/
int main()
{
    system("color F1&mode con cols=35 lines=25");
    getchar();
    return 0;
}

程序輸出:

#延遲函數的設計

跟其餘不一樣的是,這個程序的延遲沒有使用usleep,我以爲這也是很是值得稱讚的地方,不過空跑cpu,哈哈,是有那麼一點調皮。

把延遲放到鍵值獲取的函數中。

/**********按鍵獲取**************/
int Getkey(int N,int T)
{
    int start=clock();
    if(KEY_V==115){return 115;}
    do{
        if(kbhit()){
                KEY_V=(int)(getch());
                if(KEY_V<97){KEY_V+=32;}
                return KEY_V;
        }
        for(i=0;i<=N;i++);
    }while((clock()-start)<T);
    dy=1;
    return -1;
}

 

就先簡單講解下這些,至於得分機制,碰撞檢測,消除的代碼,尚未想好怎麼講明白,須要再花時間剖析剖析。

 

#試玩一下

#源碼

#include   <conio.h>
#include   <stdio.h>
#include  <stdlib.h>
#include    <time.h>
#include <Windows.h>
 
/**********初始化參數************/
int i,j,N,T,F,J,X,Y,dx,dy,  KEY_V,  Cache1,Cache2,NU,NI,RU,RI,  P_X,P_Y,POS_H_MAX,  LEVEL=1,SCORE=0,  P[4],  POINT_V[12][22],  MARK[21],  FLAG[5]={0,0,0,1,0};
int TGM[7][4]={{0x159D,0x89AB,0x159D,0x89AB},{0x126A,0x4856,0x159A,0x4526},{0x926A,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x5926,0x0156,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x156A,0x4152,0x156A,0x4152}};
int SRS[7][4]={{0x159D,0x89AB,0x26AE,0x4567},{0x0159,0x4856,0x159A,0x4526},{0x8159,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x4815,0x459A,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x0459,0x8596,0x156A,0x4152}};
 
/**********光標位置函數**********/
void Pos(int x,int y)
{
        COORD pos;
        HANDLE hOutput;
        pos.X=2*x;
        pos.Y=y;
        hOutput=GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleCursorPosition(hOutput,pos);
}
 
void HideCursor()
{
        CONSOLE_CURSOR_INFO cursor_info={1,0};
        SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}
 
/**********初始化界面************/
void CreatUI()
{
        int i,j,BOUNDARY;
        printf("┏━━━━━━━━━━┓\n");
        for(j=1;j<=20;j++) {
                if     (j==3){printf("┃          ┃LEVEL:1\n");}
                else if(j==5){printf("┃          ┃SCORE:0\n");}
                else if(j==7){printf("┃          ┃NEXT   \n");}
                else         {printf("┃          ┃\n");}
        }
        printf("┗━━━━━━━━━━┛\n");
        printf(" CopyRight@2016~2018 BY HAPPY\n");
        for(j=1;j<=21;j++){
                for(i=0;i<=11;i++){
                        BOUNDARY=i*(i-11)*(j-21);
                        if(BOUNDARY==0){
                                POINT_V[i][j]=1;
                        }else{
                                POINT_V[i][j]=0;
                        }                
                }
        }
}
 
/**********按鍵獲取**************/
int Getkey(int N,int T)
{
    int start=clock();
    if(KEY_V==115){return 115;}
    do{
        if(kbhit()){
                KEY_V=(int)(getch());
                if(KEY_V<97){KEY_V+=32;}
                return KEY_V;
        }
        for(i=0;i<=N;i++);
        printf("%d\n",clock()-start);
    }while((clock()-start)<T);
    dy=1;
    return -1;
}
 
/***********塊體轉置*************/
int Rote(int S, int I)
{
    return (F==0)?TGM[S][(I+4)%4]:SRS[S][(I+4)%4];
}
 
/***********擦除顯示*************/
int Display(int x, int y, int CAC, int Mode)
{
    for(j=0;j<=3;j++){
        P[j]=CAC&0xF, CAC>>=4;
        if     (Mode==1){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf("■");}
        else if(Mode==0){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf(" ");}
    }
    return 0;
}
 
/***********固化塊體*************/
int DoBlocks()
{
        //~~~遊戲結束
        if(Y<2){
                Pos(1,22);printf("GAME OVER!");
                exit(0);
        }
        //~~~固化塊體
        POS_H_MAX=0, FLAG[3]=1;
        for(j=0;j<=3;j++){
                P_X=(P[j]>>2)+X,P_Y=(P[j]&0x3)+Y;
                if(POS_H_MAX<P_Y){POS_H_MAX=P_Y;}
                POINT_V[P_X][P_Y]=1;
        }
        //~~~關卡得分
        for(j=Y;j<=POS_H_MAX;j++){
                FLAG[2]=1;
                for(i=1;i<=10;i++){
                        if(POINT_V[i][j]==0){FLAG[2]=0;}
                }
                if(FLAG[2]){
                        SCORE+=10,MARK[j]=1;
                        if(SCORE==400){
                                SCORE=0,LEVEL+=1,T-=100;
                                FLAG[4]=1;        
                        }
                }
        }
        //~~~極品消行
        for(j=20;j>=5;j--){
                if(FLAG[4]){
                        for(i=1;i<=10;i++){
                                POINT_V[i][j]=0;
                                Pos(i,j);printf(" ");
                        }
                }
                else if(MARK[j])
                {
                        MARK[j]=0,J=j-1;
                        for(N=1;N<=3;N++){
                                if(MARK[J]){J--;}
                        }
                        MARK[J]=1;
                        for(i=1;i<=10;i++){
                                Pos(i,j);
                                if(POINT_V[i][j]=POINT_V[i][J]){
                                        printf("■");
                                }else{
                                        printf(" ");
                                }
                        }
                }
        }
        FLAG[4]=0;
        return 0;
}
 
/***********碰撞檢測*************/
int CheckCollision()
{                                
    for(j=0;j<=3;j++){
        P_X=(P[j]>>2)+X+dx,P_Y=(P[j]&0x3)+Y+dy;
        if(POINT_V[P_X][P_Y]){
            if(dx!=0){return 1;}
            if(dy){
                DoBlocks();
                Pos(12,3);printf("LEVEL:%-3d",LEVEL);
                Pos(12,5);printf("SCORE:%-3d",SCORE);
                return 2;
            }
            if(KEY_V==119){FLAG[0]=1;}
        }
    }
    return 0;
}
unsigned 
/***********循環核心*************/
int GameCycle(int N, int T, int F)
{
        srand((unsigned)time(NULL));RU=rand()%7,RI=(rand()%4);
        while(1){
                if(FLAG[3]){
                        Display(12,8,Rote(RU,RI),0);
                        X=4,Y=1, NU=RU,NI=RI, RU=rand()%7,RI=(rand()%4), FLAG[3]=0,KEY_V=0;
                        Display(12,8,Rote(RU,RI),1);
                        Display(X, Y,Rote(NU,NI),1);
                }
                dx=0,dy=0;        
                KEY_V=Getkey(N,T);
                if(KEY_V==119){
                    NI++;
                    Display(X,Y,Rote(NU,NI),2);
                }//旋W
                else if(KEY_V==115){dy= 1;}//下S
                else if(KEY_V==97 ){dx=-1;}//左A
                else if(KEY_V==100){dx= 1;}//右D
                else if(KEY_V==112){getch(); }//暫停P
                else if(KEY_V==113){return 0;}//退出Q
                if(dx!=0 || dy!=0 || KEY_V==119){
                    if(!CheckCollision()){
                            if(FLAG[0]){
                                    NI--,FLAG[0]=0;
                                    Display(X,Y,Rote(NU,NI),0);
                            }
                            else if(KEY_V==119){
                                    Display(X,Y,Rote(NU,NI-1),0);
                            }else{
                                    Display(X,Y,Rote(NU,NI),0);
                            }
                            Display(X+dx,Y+dy,Rote(NU,NI),1);
                            X+=dx,Y+=dy;
                    }
                }
        }
        return 0;
}
 
/**********Main主函數***********/
int main()
{
        system("color F0&mode con cols=35 lines=25");
        HideCursor();
        CreatUI();
        GameCycle(10,800,1);
        return 0;
}

#後記

裏面涉及的不少東西,我都沒有徹底總結出來,做者的巧妙很是令我佩服,後面應該還會有文章分析。

你們有什麼想法或者本身的解讀,能夠留言,歡迎一塊兒討論,共同進步,探索代碼裏面的奧祕和樂趣。

方塊消除

準備下一個方塊

得分機制

Game Over 判斷

相關文章
相關標籤/搜索