C語言實(shí)現(xiàn)俄羅斯方塊的六種模式詳程建議收藏
--------寫在前面--------
第一次做標(biāo)題黨,大家輕噴哈。這個(gè)游戲是博主在大一c語言實(shí)訓(xùn)時(shí)獨(dú)立完成的,所有內(nèi)容均為原創(chuàng)。小游戲耗時(shí)5天完成,除了常見的單人模式外,增加了作弊模式,雙人模式,計(jì)時(shí)賽等玩法,真滴很好玩哦。雖然現(xiàn)在看起來很簡陋,但對于當(dāng)時(shí)的我來說實(shí)屬不易,從頁面設(shè)計(jì)到游戲背景音樂的選取再到關(guān)鍵算法的編寫,每一步都凝匯了自己的努力,通宵鏖戰(zhàn)的畫面依然歷歷在目?,F(xiàn)在分享出來,一方面是希望可以幫助到大家,另一方面也想紀(jì)念美好的大一時(shí)光。源碼地址放在文末了,大家自取。
-----------------------正文-----------------------
最終效果
主頁

模式選擇

按1進(jìn)入單人模式

按2進(jìn)入雙人模式

標(biāo)準(zhǔn)模式:除了左右下移動(dòng),增加了直接下落和暫停,增加了等級判定

地獄模式:在標(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)畫


代碼部分
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()
{
///初始化句柄,必須放在最開始
initHandle();
///開始動(dòng)畫
mciSendString("open 俄羅斯方塊進(jìn)入音樂.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" //尖括號常用于引入系統(tǒng)頭文件,雙引號常用于引入自己定義的頭文件 默認(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)體變量名字都是大寫
info.bVisible = FALSE; //設(shè)置光標(biāo)是否可見
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("等級:%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("等級:%d",level2);
}雙人模式窗體打印
void windowPrint2(int x,int y){
int i,j; //用來遍歷二維數(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("■");
}
}
}
}碰撞檢測
//碰撞檢測基于下一個(gè)位置的檢測,數(shù)組與界面坐標(biāo)的對應(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é)束
}
}按鍵檢測以及雙人勝負(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;
}
///初始化句柄,必須放在最開始
initHandle();
///打開音樂文件
windowPrint2(0,0);
printInfo();
printGradeLevel1(0);
printGradeLevel2(0);
///游戲開始時(shí)間
clock_t startTime = clock();
///定時(shí)器
clock_t time1,time2;
time1 = clock();
startBlock1();
startBlock2();
nextBlock1();
nextBlock2();
while(1){
//按鍵驅(qū)動(dòng)
///檢測是否有按鍵按下
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)畫打印
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("按任意鍵開始游戲!");
setPos(50,30);
printf("author 趙敬軒");
}game.c 完整代碼
源碼地址
gitee:https://gitee.com/i-dream-code/Tetris/
github:https://github.com/zhaojingxuan123/StrongestTetris
百度云盤:鏈接:https://pan.baidu.com/s/1PX1gALLB0znXnE8e2c_YSQ
提取碼:1234
導(dǎo)入方法
博主使用的是codeblocks,一款很好用的編譯器
網(wǎng)盤鏈接:鏈接:https://pan.baidu.com/s/18bYwcgggK9mLjca4gckxtQ
提取碼:1234
安裝好codeblocks后,將 20200705.cbp 直接拖到codeblocks的左側(cè)面板即可

上方build=>run即可運(yùn)行

總結(jié)
現(xiàn)在回過頭來開,這個(gè)項(xiàng)目存在著很多問題,比如代碼臃腫,復(fù)用性差,沒有利用到設(shè)計(jì)模式,重復(fù)造了很多輪子。除此之外,有一些功能設(shè)定也不夠合理,比如沒有返回上一級菜單,切換模式只能重新編譯游戲才可以進(jìn)入。游戲是一次性游戲,如果增加計(jì)分板來存儲玩家的最高分也可以增加游戲體驗(yàn)。日后有機(jī)會(huì)再完善一下。
“在繁華中自律,在落魄中自愈”, 恍惚間,回首歲月靜好。
到此這篇關(guān)于C語言實(shí)現(xiàn)俄羅斯方塊的六種模式詳程建議收藏的文章就介紹到這了,更多相關(guān)C語言 俄羅斯方塊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VS2017開發(fā)C語言出現(xiàn)“no_init_all“的解決辦法
這篇文章介紹了VS2017開發(fā)C語言出現(xiàn)“no_init_all“的解決辦法,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12
C++實(shí)現(xiàn)將內(nèi)容寫入文件的方法總結(jié)
本文主要總結(jié)了一下C/C++將內(nèi)容寫入文件的方法,C的方法有些單調(diào),畢竟沒有庫函數(shù)。C++則豐富些,下面我把搜集到的整理一下,供大家參考2023-04-04
SQL Server中的數(shù)據(jù)復(fù)制到的Access中的函數(shù)
SQL Server中的數(shù)據(jù)復(fù)制到的Access中,表的結(jié)構(gòu)相同 不要提用openrowset,因?yàn)锳ccess文件和SQL Server不在一臺機(jī)器上2008-11-11
Qt GUI圖形圖像開發(fā)之Qt表格控件QTableView簡單使用方法及QTableView與QTableWidget區(qū)
這篇文章主要介紹了Qt GUI圖形圖像開發(fā)之Qt表格控件QTableView簡單使用方法,需要的朋友可以參考下2020-03-03

