首頁>技術>

最近在看俄羅斯方塊的遊戲,看到一個大神寫的俄羅斯方塊程式碼,非常非常優秀,拿出來解析給大家看看,也希望大家自己嘗試執行試試,從中能得到一些啟發。

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

一共分成 7 形狀,有的形狀有 4種狀態,有的形狀有1種狀態。

不管是多少種狀態,一個方塊需要一個2個位元組來儲存,也就是16bit來儲存一個方塊的資訊。

基於上面的理論,我們可以使用4x4的陣列來儲存方塊的資訊。

#寫程式碼來顯示這個方塊

方塊資料,這兩個陣列其實是兩種遊戲模式的方塊資訊,我們只需要分析一種就可以了。

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}};

我們分析這段程式碼

/***********擦除顯示*************/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決定輸出方塊的位置。

我們拿 0這個方塊的資料0x4859 對應的二進位制 0B0100100001011001來做個例子。

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

透過這個座標,我們會輸出這樣一個方塊

再舉個例子

我們拿 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 判斷

12
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 跟光磊學Python開發-Python基本程式開發