日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【C语言】百行代码实现—俄罗斯方块

發(fā)布時(shí)間:2023/12/10 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【C语言】百行代码实现—俄罗斯方块 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 自述
  • 整體框架和流程
    • 開(kāi)始游戲頁(yè)面設(shè)計(jì)
    • 游戲設(shè)計(jì)流程介紹
  • 注意
  • 可執(zhí)行源碼-全部

自述

這個(gè)代碼是19年的末尾寫(xiě)的,最近就想著想把這個(gè)用博客分享出來(lái),一方面是為了鞏固自己的知識(shí),另一方面也希望同學(xué)們能夠因?yàn)檫@篇文章有所收獲,所以也在原來(lái)代碼的基礎(chǔ)上添加了蠻多注釋的(感覺(jué)就和重新寫(xiě)了一遍俄羅斯方塊似的😂) ,如果對(duì)于這篇文章有任何問(wèn)題都可以提出來(lái),也希望這篇文章能夠幫助到大家,謝謝大家。

整體框架和流程

開(kāi)始游戲頁(yè)面設(shè)計(jì)

歡迎界面

游戲開(kāi)始界面

這些大家應(yīng)該都可以知道這個(gè)頁(yè)面的布局了吧,接下來(lái)我們來(lái)了解對(duì)于這些布局我們應(yīng)該要利用哪些函數(shù)來(lái)設(shè)置


初始化畫(huà)布
如果我們像要進(jìn)行圖形界面的編程和繪畫(huà)的話就一定要去初始化畫(huà)布

//初始畫(huà)布initgraph(550,660);//表示初始化長(zhǎng)和寬

設(shè)置窗口標(biāo)題:利用window接口

//設(shè)置窗口標(biāo)題:利用window接口HWND hwnd=GetHWnd();//hwnd變量來(lái)接收,句柄SetWindowText(hwnd,_T("俄羅斯方塊 宋同學(xué)2019.12.18"));

對(duì)于顯示中間的文字

//設(shè)置文本的字體樣式setfont(40,0,_T("微軟雅黑"));//高度40個(gè)像素寬度自己適應(yīng)setcolor(WHITE);outtextxy(205,200,_T("俄羅斯方塊"));//利用坐標(biāo)setfont(22,0,_T("楷體"));outtextxy(175,300,_T("編程從俄羅斯方塊開(kāi)始!"));Sleep(3000);//休眠3秒鐘那樣轉(zhuǎn)到游戲界面就不會(huì)突兀,讓玩家有準(zhǔn)備時(shí)間

對(duì)于歡迎界面所有的代碼:

void welcome(){//初始畫(huà)布initgraph(550,660);//設(shè)置窗口標(biāo)題:利用window接口HWND hwnd=GetHWnd();//hwnd變量來(lái)接收,句柄,用于SetWindowText(hwnd,_T("俄羅斯方塊 宋同學(xué)2019.12.18"));//設(shè)置文本的字體樣式setfont(40,0,_T("微軟雅黑"));//高度40個(gè)像素寬度自己適應(yīng)setcolor(WHITE);outtextxy(205,200,_T("俄羅斯方塊"));setfont(22,0,_T("楷體"));outtextxy(175,300,_T("編程從俄羅斯方塊開(kāi)始!"));Sleep(3000);//休眠3秒鐘那樣轉(zhuǎn)到游戲界面就不會(huì)突兀,讓玩家有準(zhǔn)備時(shí)間}

初始化游戲界面
有些變量是設(shè)置為全局變量所以先不要太在意,等會(huì)兒都會(huì)有源碼分享出來(lái)

//初始化游戲界面 void initGameScene(){char str[16];cleardevice();//繪制長(zhǎng)方形框rectangle(27,27,336,635);rectangle(29,29,334,633);rectangle(370,50,515,195);setfont(24,0,_T("楷體"));setcolor(LIGHTGRAY);outtextxy(405,215,_T("下一個(gè)"));setcolor(RED);outtextxy(405,280,_T("分?jǐn)?shù)"));//score是分?jǐn)?shù),定義為全局變量,等會(huì)回有源碼分享sprintf(str,"%d",score);//把score寫(xiě)入到str中轉(zhuǎn)換為字符串形式outtextxy(415,310,str);//和分?jǐn)?shù)的設(shè)置同理outtextxy(405,375,_T("等級(jí)"));sprintf(str,"%d",rank);outtextxy(415,405,str);//操作說(shuō)明setcolor(LIGHTBLUE);outtextxy(390,475,_T("操作說(shuō)明"));outtextxy(390,500,"↑:旋轉(zhuǎn)");outtextxy(390,525,"↓:下降");outtextxy(390,550,"←:左移");outtextxy(390,575,"→:右移");outtextxy(390,600,"空格:暫停");}

由于代碼量還是有一些的所以接下來(lái)就不每一個(gè)函數(shù)都進(jìn)行分開(kāi)講,感覺(jué)那樣那這篇博客就有點(diǎn)長(zhǎng)了,后面有源碼有挺多注釋的,大家可以慢慢看。

游戲設(shè)計(jì)流程介紹

  • 設(shè)計(jì)圖形界面
  • 更新"下一個(gè)"框框(利用隨機(jī)取,當(dāng)主窗口的圖形落到最底下就更新(初始就直接先來(lái)一個(gè)))
  • 將下一個(gè)框框中的形態(tài)的圖形復(fù)刻到主窗口的中
  • 實(shí)現(xiàn)主游戲窗口的方塊進(jìn)行移動(dòng)
  • 進(jìn)行消行處理
  • 更新分?jǐn)?shù)以及等級(jí)

注意

其實(shí)這游戲設(shè)計(jì)起來(lái)其實(shí)不難,在我看來(lái)難點(diǎn)可能是出現(xiàn)在消行和更新方塊的操作上,還有一點(diǎn)要注意的是,圖形的更新可以通過(guò)先用黑方塊把指定位置都給覆蓋然后再重新畫(huà)一個(gè)(這一點(diǎn)即適用于方塊的下落也適用于下一個(gè)方塊的更新上),對(duì)于消行的處理要注意的是:如果第i行的方塊滿了我們把這一行消了以后那么我們還要再次判斷i行而不能直接判斷i+1行,因?yàn)楫?dāng)我們第i行消了以后上面的方塊壓下來(lái)那么第i行可能還可以再消一次。


由于可能圖形界面大家沒(méi)怎么接觸過(guò)所以我就單獨(dú)的寫(xiě)了一下,其他的部分可能還是得靠大家自己的理解了,也沒(méi)辦法把所有的想法全部都寫(xiě)出來(lái),下面的源碼中加了蠻多的注釋還是希望大家看看代碼看看注釋這樣也能理解的深刻一些。

可執(zhí)行源碼-全部

頭文件

#include<stdio.h> #include<string> #include<iostream> #include<graphics.h>//圖形界面編程 #include<time.h> #include<conio.h>//kbhit()鍵盤(pán)捕捉的使用需要的頭文件

各種宏定義及全局變量

#define BLOCK_COUNT 5 #define BlOCK_WIDTH 5 #define BLOCK_HEIGHT 5 #define UNIT_SIZE 20//右上角打印的方塊的像素 #define START_X 130 #define START_Y 30 #define KEY_UP 72//向上按鍵ASCII碼值為72 #define KEY_RIGHT 77 #define KEY_LEFT 75 #define KEY_DOWN 80 #define KEY_SPACE 32 int score=0;//分?jǐn)?shù) int rank=0;//等級(jí)int speed=500; int minX=30; int minY=30;//設(shè)置枚舉值 typedef enum{BLOCK_UP,BLOCK_RIGHT,BLOCK_DOWN,BLOCK_LEFT}block_dir_t;typedef enum{MOVE_DOWN,MOVE_LEFT,MOVE_RIGHT }move_dir_t;int NextIndex=-1;//下一個(gè)方塊的種類 int BlockIndex=-1;//當(dāng)前方塊的種類int color[BLOCK_COUNT]={GREEN,CYAN,MAGENTA,BROWN,YELLOW };int visit[30][15];int markcolor[30][15];//表示對(duì)應(yīng)位置的方塊的顏色//初始化各種方塊,包括其形態(tài)變換 int block[BLOCK_COUNT*4][BlOCK_WIDTH][BLOCK_HEIGHT]={// | 形方塊{ 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 },// L 形方塊{ 0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 },// 田 形方塊{ 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0 },// T 形方塊{ 0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 },// Z 形方塊{ 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0 },{ 0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0 }, };//5種方塊每種方塊4種形態(tài)方塊的表示是用5行5列來(lái)表示

各類方法

void welcome(){//初始畫(huà)布initgraph(550,660);//設(shè)置窗口標(biāo)題:利用window接口HWND hwnd=GetHWnd();//hwnd變量來(lái)接收SetWindowText(hwnd,_T("俄羅斯方塊 宋同學(xué)2019.12.18"));//設(shè)置文本的字體樣式setfont(40,0,_T("微軟雅黑"));//高度40個(gè)像素寬度自己適應(yīng)setcolor(WHITE);outtextxy(205,200,_T("俄羅斯方塊"));setfont(22,0,_T("楷體"));outtextxy(175,300,_T("編程從俄羅斯方塊開(kāi)始!"));Sleep(3000);//休眠3秒鐘那樣轉(zhuǎn)到游戲界面就不會(huì)突兀,讓玩家有準(zhǔn)備時(shí)間} //初始化游戲界面 void initGameScene(){char str[16];cleardevice();rectangle(27,27,336,635);rectangle(29,29,334,633);rectangle(370,50,515,195);setfont(24,0,_T("楷體"));setcolor(LIGHTGRAY);outtextxy(405,215,_T("下一個(gè)"));setcolor(RED);outtextxy(405,280,_T("分?jǐn)?shù)"));sprintf(str,"%d",score);outtextxy(415,310,str);outtextxy(405,375,_T("等級(jí)"));sprintf(str,"%d",rank);outtextxy(415,405,str);//操作說(shuō)明setcolor(LIGHTBLUE);outtextxy(390,475,_T("操作說(shuō)明"));outtextxy(390,500,"↑:旋轉(zhuǎn)");outtextxy(390,525,"↓:下降");outtextxy(390,550,"←:左移");outtextxy(390,575,"→:右移");outtextxy(390,600,"空格:暫停");} //清除右上角方塊(用一個(gè)又一個(gè)的小黑方塊把它遮住) void clearBlock(){setcolor(BLACK);setfont(23,0,"楷體");//高度23for(int i=0;i<BLOCK_HEIGHT;i++){for(int j=0;j<BlOCK_WIDTH;j++){//"■"int x=391+UNIT_SIZE*j;int y=71+UNIT_SIZE*i;outtextxy(x,y, "■"); }} }//在右上角區(qū)域繪制下一個(gè)方塊 void drawBlock(int x,int y){setcolor(color[NextIndex]);setfont(23,0,"楷體");//高度23for(int i=0;i<BLOCK_HEIGHT;i++){for(int j=0;j<BlOCK_WIDTH;j++){//"■"if(block[NextIndex*4][i][j]==1){//對(duì)應(yīng)哪一種方塊(NextIndex*4)int x2=x+UNIT_SIZE*j;int y2=y+UNIT_SIZE*i;outtextxy(x2,y2, "■"); }}}}//繪制方塊,特定位置特定方向的方塊 void drawBlock(int x,int y,int blockIndex,block_dir_t dir){setcolor(color[NextIndex]);setfont(23,0,"楷體");//高度23,寬度自動(dòng)適應(yīng)int id=blockIndex*4+dir;for(int i=0;i<BLOCK_HEIGHT;i++){for(int j=0;j<BlOCK_WIDTH;j++){//"■"if(block[id][i][j]==1){//對(duì)應(yīng)哪一種方塊(NextIndex*4)int x2=x+UNIT_SIZE*j;int y2=y+UNIT_SIZE*i;outtextxy(x2,y2, "■"); }}} }//下降過(guò)程中的清除方塊的過(guò)程,一個(gè)一個(gè)的 //清除指定位置指定方向的方塊 //參數(shù)x:方塊的左上角的x坐標(biāo) //參數(shù)y:方塊的左上角在游戲區(qū)域內(nèi)的坐標(biāo),距離游戲區(qū)域頂部的距離 void clearBlock(int x,int y,block_dir_t dir){setcolor(BLACK);int id=BlockIndex*4+dir;y+=START_Y ;for(int i=0;i<5;i++){for(int j=0;j<5;j++){if(block[id][i][j]==1){ //擦除該方塊的第i行第j列outtextxy(x+20*j,y+20*i,"■");}}} }void nextblock(){clearBlock();//清除//隨機(jī)選擇一種方塊srand(time(NULL));//使用時(shí)間函數(shù)的返回值來(lái)作為時(shí)間種子NextIndex=rand()%BLOCK_COUNT;drawBlock(391,71);//畫(huà)方塊(根據(jù)序號(hào)) }//如果可以在指定位置可以向指定位置移動(dòng)就返回1,否則返回0 int moveAble(int x0,int y0,move_dir_t moveDir,block_dir_t blockDir){//計(jì)算當(dāng)前方塊的左上角在30*15的游戲區(qū)域中的位置(多少行多少列)int x=(y0-minY)/UNIT_SIZE;//一個(gè)方塊表示20像素int y=(x0-minX)/UNIT_SIZE;int id=BlockIndex*4+blockDir;//哪個(gè)方塊哪種狀態(tài)int ret=1;if(moveDir==MOVE_DOWN){for(int i=0;i<5;i++){for(int j=0;j<5;j++){if(block[id][i][j]==1&&(x+i+1>=30||visit[x+i+1][y+j]==1)){ret=0;}}}}else if(moveDir==MOVE_LEFT){for(int i=0;i<5;i++){for(int j=0;j<5;j++){if(block[id][i][j]==1&&(y+j==0||visit[x+i][y+j-1]==1)){ret=0; }}}}else if(moveDir==MOVE_RIGHT){for(int i=0;i<5;i++){for(int j=0;j<5;j++){if(block[id][i][j]==1&&(y+j+1==15||visit[x+i][y+j+1]==1)){ret=0; }}}}return ret; }//檢測(cè)函數(shù)是否結(jié)束 void failCheck(){//剛下來(lái)的時(shí)候都是向上的if(!moveAble(START_X,START_Y,MOVE_DOWN,BLOCK_UP)){setcolor(WHITE);setfont(45,0,"隸體");outtextxy(75,300,"GAME OVER!");Sleep(1000);system("pause");closegraph();exit(0);} }//等待函數(shù),這樣比sleep函數(shù)好的地方是不會(huì)卡頓 void wait(int interval){int count=interval/10;for(int i=0;i<count;i++){Sleep(10);if(kbhit()){return;}} }//很多時(shí)候可以利用反向思維去完成函數(shù) //判斷當(dāng)前方塊能否轉(zhuǎn)向到指定方向 //注意此時(shí)還沒(méi)開(kāi)始轉(zhuǎn) int rotatable(int x,int y,block_dir_t Dir){int id=BlockIndex*4+Dir;int xIndex=(y-minY)/20;int yIndex=(x-minX)/20;if(!moveAble(x,y,MOVE_DOWN,Dir)){return 0;}for(int i=0;i<5;i++){for(int j=0;j<5;j++){if(block[id][i][j]==1&&(yIndex+j<0||yIndex+j>=15||visit[xIndex+i][yIndex+j]==1)){return 0;}}}return 1; }//方塊固定函數(shù) void mark(int x,int y,int blockIndex,block_dir_t dir){int id=blockIndex*4+dir;int x2=(y-minX)/20;int y2=(x-minY)/20;for(int i=0;i<5;i++){for(int j=0;j<5;j++){if(block[id][i][j]==1){visit[x2+i][y2+j]=1;markcolor[x2+i][y2+j]=color[blockIndex];}}}} //進(jìn)行上下左右的移動(dòng) void move(){int x=START_X;int y=START_Y;int k=0;//偏移量block_dir_t blockDir=BLOCK_UP;int cur_speed=speed;//先檢測(cè)游戲是否失敗,再確認(rèn)下降failCheck();//持續(xù)下降while(1){if(kbhit()){int key=getch();//和kbhit()組合使用if(key==KEY_SPACE){//碰到空格getch();//實(shí)現(xiàn)暫停}}//清除當(dāng)前方塊clearBlock(x,k,blockDir);//清除特定位置特定朝向的方塊if(kbhit()){int key=getch();if(key==KEY_UP){/*********************變形*要求:要判斷能不能翻轉(zhuǎn)-》定義函數(shù)********************/block_dir_t nextDir=(block_dir_t)((blockDir+1)%4);if(rotatable(x,y+k,nextDir)){blockDir=nextDir;}}else if(key==KEY_DOWN){cur_speed=50;}else if(key==KEY_LEFT){if(moveAble(x,y+k+20,MOVE_LEFT,blockDir)){x-=UNIT_SIZE;}}else if(key==KEY_RIGHT){if(moveAble(x,y+k+20,MOVE_RIGHT,blockDir)){x+=UNIT_SIZE;}}}k+=20;//繪制當(dāng)前方塊drawBlock(x,y+k,BlockIndex,blockDir);wait(cur_speed);//利用sleep函數(shù)不太靈活容易出現(xiàn)卡頓所以就自己設(shè)計(jì)一個(gè)函數(shù)//方塊的“固化”處理if(!moveAble(x,y+k,MOVE_DOWN,blockDir)){mark(x,y+k,BlockIndex,blockDir);//固定函數(shù)break;}}}void newblock(){//確定即將使用的方塊的類別BlockIndex=NextIndex;//繪制剛從頂部下降的方塊drawBlock(START_X,START_Y);//讓新出現(xiàn)的方塊停頓一會(huì)兒,讓用戶識(shí)別到Sleep(100);//0.1秒//右上角區(qū)域繪制下一個(gè)方塊nextblock();//方塊降落move(); }void down(int x){for(int i=x;i>0;i--){//清除第i行,第j列的方塊消失for(int j=0;j<15;j++){if(visit[i-1][j]){visit[i][j]=1;markcolor[i][j]=markcolor[i-1][j];setcolor(markcolor[i][j]);outtextxy(20*j+minX,20*i+minY,"■");}else{visit[i][j]=0;setcolor(BLACK);outtextxy(20*j+minX,20*i+minY,"■");}}}//清除最上面行setcolor(BLACK);for(int j=0;j<15;j++){visit[0][j]=0;outtextxy(20*j+minX,minY,"■");} }//更新分?jǐn)?shù) void addScore(int lines){char str[10];setcolor(RED);score+=lines*10;sprintf(str,"%d",score);outtextxy(415,310,str); }//更新等級(jí) void updateGrade(){//更新等級(jí)提示//50分隔一級(jí)char str[16];rank=score/50+1;sprintf(str,"%d",rank);outtextxy(425,405,str);//更新速度,自己可調(diào)整speed=500-rank*100;if(speed<=100){speed=100;}}//消行處理 void check(){int i;int j;int clearLines=0;for(i=29;i>=0;i--){//利用visit函數(shù)可以提高效率for(j=0;j<15&&visit[i][j];j++);//執(zhí)行到此處時(shí)有兩種情況//1.i行沒(méi)滿,此時(shí)j<15//2.i行滿了,此時(shí)j>=15if(j>=15){//此時(shí)第i行已經(jīng)滿了//此時(shí)來(lái)消除第i行down(i);i++;//可能那一行可能還是可以消的所以再檢測(cè)一遍clearLines++;}}//更新分?jǐn)?shù)addScore(clearLines);//更新等級(jí)updateGrade(); }

主方法:

int main(){welcome();initGameScene();//產(chǎn)生新方塊nextblock();Sleep(500);//初始化訪問(wèn)數(shù)組memset(visit,0,sizeof(visit));//從地址開(kāi)始清為零大小為整個(gè)visit的數(shù)組大小while(1){newblock();//消除滿行,并更新分?jǐn)?shù)和等級(jí)check();}system("pause");closegraph();return 0; }

總結(jié)

以上是生活随笔為你收集整理的【C语言】百行代码实现—俄罗斯方块的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。