C語(yǔ)言實(shí)現(xiàn)俄羅斯方塊的六種模式詳程建議收藏
--------寫(xiě)在前面--------
第一次做標(biāo)題黨,大家輕噴哈。這個(gè)游戲是博主在大一c語(yǔ)言實(shí)訓(xùn)時(shí)獨(dú)立完成的,所有內(nèi)容均為原創(chuàng)。小游戲耗時(shí)5天完成,除了常見(jiàn)的單人模式外,增加了作弊模式,雙人模式,計(jì)時(shí)賽等玩法,真滴很好玩哦。雖然現(xiàn)在看起來(lái)很簡(jiǎn)陋,但對(duì)于當(dāng)時(shí)的我來(lái)說(shuō)實(shí)屬不易,從頁(yè)面設(shè)計(jì)到游戲背景音樂(lè)的選取再到關(guān)鍵算法的編寫(xiě),每一步都凝匯了自己的努力,通宵鏖戰(zhàn)的畫(huà)面依然歷歷在目?,F(xiàn)在分享出來(lái),一方面是希望可以幫助到大家,另一方面也想紀(jì)念美好的大一時(shí)光。源碼地址放在文末了,大家自取。
-----------------------正文-----------------------
最終效果
主頁(yè)
模式選擇
按1進(jìn)入單人模式
按2進(jìn)入雙人模式
標(biāo)準(zhǔn)模式:除了左右下移動(dòng),增加了直接下落和暫停,增加了等級(jí)判定
地獄模式:在標(biāo)準(zhǔn)模式的前提下加快了下落速度,更刺激!
作弊模式:增加炫彩模式,變形,加速以及減速功能
雙人標(biāo)準(zhǔn)賽:設(shè)計(jì)了一個(gè)判定勝負(fù)平局的算法
雙人地獄存活賽:刺激到不行??!
雙人計(jì)時(shí)賽:設(shè)置了計(jì)時(shí)器,重新設(shè)定了判定勝負(fù)平局的算法,在規(guī)定時(shí)間內(nèi)分?jǐn)?shù)高者勝!
結(jié)束動(dòng)畫(huà)
代碼部分
main.c
#include <stdio.h> #include <stdlib.h> #include "game.h" #include "mywindows.h" #include <conio.h> #include <mmsystem.h> #pragma comment (lib, "winmm.lib") void chooseMode(){ if(kbhit()){ mciSendString("close g",NULL,0,NULL); switch(getch()){ case 49: case 97: gameInit(1);break; case 50: case 98: gameInit(2);break; case 51: case 99: gameInit(3);break; } } } void chooseMode2(){ if(kbhit()){ mciSendString("close g",NULL,0,NULL); switch(getch()){ case 49: case 97: gameInit1(1);break; case 50: case 98: gameInit1(2);break; case 51: case 99: gameInit1(3);break; } } } int main() { ///初始化句柄,必須放在最開(kāi)始 initHandle(); ///開(kāi)始動(dòng)畫(huà) mciSendString("open 俄羅斯方塊進(jìn)入音樂(lè).mp3 alias g",NULL,0,NULL); mciSendString("play g repeat",NULL,0,NULL); printAnimation(); if(kbhit()){ getch(); chooseWindow(); } if(kbhit()){ switch(getch()){ case 49: case 97: chooseWindow2();chooseMode();break; case 50: case 98: chooseWindow3();chooseMode2();break; } } return 0; }
mywindows.c
#include "mywindows.h" //尖括號(hào)常用于引入系統(tǒng)頭文件,雙引號(hào)常用于引入自己定義的頭文件 默認(rèn)檢索順序不同 HANDLE handle; //函數(shù)定義 void initHandle(){ handle = GetStdHandle(STD_OUTPUT_HANDLE); hideCursor();//游戲啟動(dòng)后隱藏光標(biāo)位置 // } void setColor(int color){ SetConsoleTextAttribute(handle,color); } void setPos(int x,int y){ COORD coord = {x*2,y}; //字母abcd:一個(gè)字符,漢字:兩個(gè)字符 SetConsoleCursorPosition(handle,coord); //設(shè)置句柄位置 } void hideCursor(){ CONSOLE_CURSOR_INFO info; //系統(tǒng)的結(jié)構(gòu)體變量名字都是大寫(xiě) info.bVisible = FALSE; //設(shè)置光標(biāo)是否可見(jiàn) info.dwSize = 1; //設(shè)置光標(biāo)寬度(1-100) SetConsoleCursorInfo(handle,&info);//指針取地址符 }
game.c 關(guān)鍵代碼
單人模式窗體打印
void printGradeLevel1(int num){ switch(num){ case 1: grade1+=10; break; case 2: grade1+=30; break; case 3: grade1+=50; break; case 4: grade1+=80; break; } if(grade1 < 100){ level1 = 1; } else if(grade1 >= 100 && grade1 < 300){ level1 =2; } setColor(0x0c); setPos(4,8); printf("分?jǐn)?shù):%d",grade1); setPos(4,9); printf("等級(jí):%d",level1); } void printGradeLevel2(int num){ switch(num){ case 1: grade2+=10; break; case 2: grade2+=30; break; case 3: grade2+=50; break; case 4: grade2+=80; break; } if(grade2 < 100){ level2 = 1; } else if(grade2 >= 100 && grade2 < 300){ level2 =2; } setColor(0x09); setPos(51,8); printf("分?jǐn)?shù):%d",grade2); setPos(51,9); printf("等級(jí):%d",level2); }
雙人模式窗體打印
void windowPrint2(int x,int y){ int i,j; //用來(lái)遍歷二維數(shù)組 for(i=0;i<25;i++){ for(j=0;j<58;j++){ if(windowShape2[i][j] == 1){ setColor(0xc0); setPos(x+j,y+i); //x是列,y是行 printf("%2s",""); // <--> printf(" "); } } } for(i=0;i<25;i++){ for(j=29;j<58;j++){ if(windowShape2[i][j] == 1){ setColor(0x90); setPos(x+j,y+i); //x是列,y是行 printf("%2s",""); // <--> printf(" "); } } } } //操作規(guī)則框架 void printInfo(){ setColor(0x0c); setPos(2,2); printf("N"); setPos(2,3); printf("E"); setPos(2,4); printf("X"); setPos(3,3); printf("T"); setPos(3,14); printf("紅方操作規(guī)則"); setPos(3,15); printf("------------"); setPos(2,16); printf("按 a 或 A 左移"); setPos(2,17); printf("按 d 或 D 右移"); setPos(2,18); printf("按 s 或 S 下移"); setPos(2,19); printf("按 w 或 W 變方向"); setPos(2,20); printf("按 q 直接下落"); setColor(0x03); setPos(49,2); printf("N"); setPos(49,3); printf("E"); setPos(49,4); printf("X"); setPos(50,3); printf("T"); setPos(50,14); printf("藍(lán)方操作規(guī)則"); setPos(50,15); printf("------------"); setPos(49,16); printf("按 ←鍵 左移"); setPos(49,17); printf("按 →鍵 右移"); setPos(49,18); printf("按 ↑鍵 下移"); setPos(49,19); printf("按 ↓鍵 變方向"); setPos(49,20); printf("按 回車 直接下落"); setPos(27,1); printf("--戰(zhàn)況--"); setPos(27,3); printf("--------"); }
方塊打印
void printBlock1(int x,int y,int shape,int status,int color){ int i,j; for(i = 0;i<4;i++){ for(j = 0;j<4;j++){ if(block[shape][status][i][j] == 1){ setColor(color); setPos(x+j,y+i); printf("■"); } } } } void printBlock2(int x,int y,int shape,int status,int color){ int i,j; for(i = 0;i<4;i++){ for(j = 0;j<4;j++){ if(block[shape][status][i][j] == 1){ setColor(color); setPos(x+j,y+i); printf("■"); } } } }
碰撞檢測(cè)
//碰撞檢測(cè)基于下一個(gè)位置的檢測(cè),數(shù)組與界面坐標(biāo)的對(duì)應(yīng) int crash1(int x,int y,int shape,int status){ int i,j; for(i = 0;i<4;i++){ for(j = 0;j<4;j++){ if(block[shape][status][i][j] == 1){ if(windowShape2[y+i][x+j] == 1){ ///發(fā)生碰撞 if(cur_block1.x == 17 && cur_block1.y == 1){ ///游戲結(jié)束 return -2; } ///方塊落到游戲池底部,發(fā)生碰撞 return -1; } } } } return 0; }
勝負(fù)判斷
void bottomBlock1(){ while(crash1(cur_block1.x,cur_block1.y+1,cur_block1.shape,cur_block1.status) != -1&&crash1(cur_block1.x,cur_block1.y+1,cur_block1.shape,cur_block1.status) != -2){ cur_block1.y += 1; } if(crash1(cur_block1.x,cur_block1.y+1,cur_block1.shape,cur_block1.status) == -1){ ///發(fā)生碰撞:方塊落到游戲池底部 ///產(chǎn)生新的方塊:下一個(gè)方塊值 -> 當(dāng)前正在下落的方塊,重新產(chǎn)生下一個(gè)方塊 save1(); removeLine1(); // lineClear(); updateGame1(); copyBlock1(); } else if(crash1(cur_block1.x,cur_block1.y+1,cur_block1.shape,cur_block1.status) == -2){ ///游戲結(jié)束 } }
按鍵檢測(cè)以及雙人勝負(fù)判斷算法
int gameInit1(int mode){ if(mode == 1){ mciSendString("open 雙人標(biāo)準(zhǔn).mp3 alias d",NULL,0,NULL); mciSendString("play d repeat",NULL,0,NULL); } if(mode == 2){ mciSendString("open 雙人地獄.mp3 alias e",NULL,0,NULL); mciSendString("play e repeat",NULL,0,NULL); } if(mode == 3){ mciSendString("open 雙人限時(shí).mp3 alias f",NULL,0,NULL); mciSendString("play f repeat",NULL,0,NULL); } int counter = 180; float speed ; if(mode == 1){ speed = 0.45; } else if(mode == 2){ speed = 0.25; } else if(mode == 3){ speed = 1; } ///初始化句柄,必須放在最開(kāi)始 initHandle(); ///打開(kāi)音樂(lè)文件 windowPrint2(0,0); printInfo(); printGradeLevel1(0); printGradeLevel2(0); ///游戲開(kāi)始時(shí)間 clock_t startTime = clock(); ///定時(shí)器 clock_t time1,time2; time1 = clock(); startBlock1(); startBlock2(); nextBlock1(); nextBlock2(); while(1){ //按鍵驅(qū)動(dòng) ///檢測(cè)是否有按鍵按下 if(kbhit()){ switch(getch()){ case 'w': case 'W': changeStatusBlock1();break; case 'a': case 'A': leftBlock1();break; case 'd': case 'D': rightBlock1();break; case 's': case 'S': downBlock1();break; case 72: changeStatusBlock2();break; case 75: leftBlock2();break; case 77: rightBlock2();break; case 80: downBlock2();break; case 32: bottomBlock1();break; case 13: bottomBlock2();break; } } time2 = clock(); if(mode == 1|| mode == 2){ ///每0.45秒下落一次 if((float)(time2-time1)/CLOCKS_PER_SEC > speed){ setPos(27,2); if(downBlock1() == -2) { if(grade2>grade1){ printf("藍(lán) 方 勝!");break; } else if(downBlock2() == -2){ if(grade1==grade2){ printf("平 局");break; } else{ printf("紅 方 勝!");break; } } } setPos(27,3); if(downBlock2() == -2) { if(grade1>grade2){ printf("紅 方 勝!");break; } else if(downBlock1() == -2){ if(grade1==grade2){ printf("平 局");break; } else{ printf("藍(lán) 方 勝!");break; } } } time1 = time2; } } else if(mode == 3){ setPos(2,10); printf("剩余時(shí)間:%3dS",counter); setPos(50,10); printf("剩余時(shí)間:%3dS",counter); if((float)(time2-time1)/CLOCKS_PER_SEC > speed){ counter--; setPos(27,2); if(counter == 0){ if(grade2>grade1){ printf("藍(lán) 方 勝!");break; } if(grade1==grade2){ printf("平 局");break; } else{ printf("紅 方 勝!");break; } } if(downBlock1() == -2) { if(grade2>grade1){ printf("藍(lán) 方 勝!");break; } else if(downBlock2() == -2){ if(grade1==grade2){ printf("平 局");break; } else{ printf("紅 方 勝!");break; } } } setPos(27,3); if(downBlock2() == -2) { if(grade1>grade2){ printf("紅 方 勝!");break; } else if(downBlock1() == -2){ if(grade1==grade2){ printf("平 局");break; } else{ printf("藍(lán) 方 勝!");break; } } } time1 = time2; } } } if(mode == 1){ mciSendString("close d",NULL,0,NULL); } if(mode == 2){ mciSendString("close e",NULL,0,NULL); } if(mode == 3){ mciSendString("close f",NULL,0,NULL); } Sleep(1150); printOver(); printFinish(mode); }
初始界面動(dòng)畫(huà)打印
void printStart(int x,int y){ //隨機(jī)產(chǎn)生顏色 int color = rand()%0x10; //處理黑色的情況 if(color == 0x00) { color = 0x0f; } setColor(color); setPos(x,y); printf("■■■■■ ■■■■■ ■■■■■ ■■■■ ■■■ ■■■■"); setPos(x,y+1); printf(" ■ ■ ■ ■ ■ ■ ■"); setPos(x,y+2); printf(" ■ ■■■■ ■ ■■■ ■ ■■■"); setPos(x,y+3); printf(" ■ ■ ■ ■ ■ ■ ■"); setPos(x,y+4); printf(" ■ ■■■■■ ■ ■ ■ ■■■ ■■■"); setPos(25,14); printf("按任意鍵開(kāi)始游戲!"); setPos(50,30); printf("author 趙敬軒"); }
game.c 完整代碼
源碼地址
gitee:https://gitee.com/i-dream-code/Tetris/
github:https://github.com/zhaojingxuan123/StrongestTetris
百度云盤(pán):鏈接:https://pan.baidu.com/s/1PX1gALLB0znXnE8e2c_YSQ
提取碼:1234
導(dǎo)入方法
博主使用的是codeblocks,一款很好用的編譯器
網(wǎng)盤(pán)鏈接:鏈接:https://pan.baidu.com/s/18bYwcgggK9mLjca4gckxtQ
提取碼:1234
安裝好codeblocks后,將 20200705.cbp 直接拖到codeblocks的左側(cè)面板即可
上方build=>run即可運(yùn)行
總結(jié)
現(xiàn)在回過(guò)頭來(lái)開(kāi),這個(gè)項(xiàng)目存在著很多問(wèn)題,比如代碼臃腫,復(fù)用性差,沒(méi)有利用到設(shè)計(jì)模式,重復(fù)造了很多輪子。除此之外,有一些功能設(shè)定也不夠合理,比如沒(méi)有返回上一級(jí)菜單,切換模式只能重新編譯游戲才可以進(jìn)入。游戲是一次性游戲,如果增加計(jì)分板來(lái)存儲(chǔ)玩家的最高分也可以增加游戲體驗(yàn)。日后有機(jī)會(huì)再完善一下。
“在繁華中自律,在落魄中自愈”, 恍惚間,回首歲月靜好。
到此這篇關(guān)于C語(yǔ)言實(shí)現(xiàn)俄羅斯方塊的六種模式詳程建議收藏的文章就介紹到這了,更多相關(guān)C語(yǔ)言 俄羅斯方塊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VS2017開(kāi)發(fā)C語(yǔ)言出現(xiàn)“no_init_all“的解決辦法
這篇文章介紹了VS2017開(kāi)發(fā)C語(yǔ)言出現(xiàn)“no_init_all“的解決辦法,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12C++實(shí)現(xiàn)將內(nèi)容寫(xiě)入文件的方法總結(jié)
本文主要總結(jié)了一下C/C++將內(nèi)容寫(xiě)入文件的方法,C的方法有些單調(diào),畢竟沒(méi)有庫(kù)函數(shù)。C++則豐富些,下面我把搜集到的整理一下,供大家參考2023-04-04SQL Server中的數(shù)據(jù)復(fù)制到的Access中的函數(shù)
SQL Server中的數(shù)據(jù)復(fù)制到的Access中,表的結(jié)構(gòu)相同 不要提用openrowset,因?yàn)锳ccess文件和SQL Server不在一臺(tái)機(jī)器上2008-11-11C語(yǔ)言實(shí)現(xiàn)隨機(jī)抽取紙牌程序
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)隨機(jī)抽取紙牌程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Qt GUI圖形圖像開(kāi)發(fā)之Qt表格控件QTableView簡(jiǎn)單使用方法及QTableView與QTableWidget區(qū)
這篇文章主要介紹了Qt GUI圖形圖像開(kāi)發(fā)之Qt表格控件QTableView簡(jiǎn)單使用方法,需要的朋友可以參考下2020-03-03C語(yǔ)言使用回溯法解旅行售貨員問(wèn)題與圖的m著色問(wèn)題
回溯法即是在按條件搜索走不通的情況下退回再選擇其他路線的方法,這里我們來(lái)看C語(yǔ)言使用回溯法解旅行售貨員問(wèn)題與圖的m著色問(wèn)題的方法示例:2016-07-07C語(yǔ)言實(shí)現(xiàn)超市管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)超市管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07