网站后台多个管理员,海外推广代理渠道,有哪些平台可以发布厂家信息,识图搜索在线 照片识别一、游戏效果 俄罗斯方块 二. 游戏背景
俄罗斯方块是久负盛名的游戏#xff0c;它也和贪吃蛇#xff0c;扫雷等游戏位列经典游戏的⾏列。
《俄罗斯方块》#xff08;Tetris#xff0c;俄文#xff1a;Тетрис#xff09;是一款由俄罗斯人阿列克谢帕基特诺夫于1984…一、游戏效果 俄罗斯方块 二. 游戏背景
俄罗斯方块是久负盛名的游戏它也和贪吃蛇扫雷等游戏位列经典游戏的⾏列。
《俄罗斯方块》Tetris俄文Тетрис是一款由俄罗斯人阿列克谢·帕基特诺夫于1984年6月发明的休闲游戏。
该游戏曾经被多家公司代理过。经过多轮诉讼后该游戏的代理权最终被任天堂获得。 任天堂对于俄罗斯方块来说意义重大因为将它与GB搭配在一起后获得了巨大的成功。
《俄罗斯方块》的基本规则是移动、旋转和摆放游戏自动输出的各种方块使之排列成完整的一行或多行并且消除得分。
三、游戏开发日志 基本逻辑结构与贪吃蛇一致
四、游戏实现
我们有了贪吃蛇的知识储备及了解WIN32 API,理解下面内容也就不难了。
4.1 游戏逻辑主体
#includetetris.hint main()
{//修改当前地区为本地模式为了⽀持中⽂宽字符的打印setlocale(LC_ALL, );srand((unsigned int)time(NULL));char ch0;do{GameInit();GameRun();GameOver();system(cls);SetPos(20, 12);printf(再来一局吗(Y/N):\n);SetPos(22, 15);ch getchar();getchar();//清理\n} while (ch y || ch Y);SetPos(0, 29);return 0;
}
4.2 游戏初始化的实现
4.2.1 设置窗口大小、名称及隐藏光标
//设置控制台窗⼝的⼤⼩30⾏30列system(mode con cols60 lines30);//设置cmd窗⼝名称system(title 俄罗斯方块);//获取标准输出的句柄(⽤来标识不同设备的数值)HANDLE hOutput GetStdHandle(STD_OUTPUT_HANDLE);//影藏光标操作CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(hOutput, CursorInfo);//获取控制台光标信息CursorInfo.bVisible false; //隐藏控制台光标SetConsoleCursorInfo(hOutput, CursorInfo);//设置控制台光标状态
4.2.2 打印欢迎界面
void WelcomeToGame()
{SetPos(20, 10);printf(欢迎来到俄罗斯方块);SetPos(20, 23);//让按任意键继续的出现的位置好看点system(pause);system(cls);SetPos(14, 10);printf(↑为变换.↓为加速.←为左移.→为右移\n);SetPos(20, 23);//让按任意键继续的出现的位置好看点system(pause);system(cls);
} 4.2.3 初始化地图
void CreateMap()
{memset(IF.pos, 0, sizeof(int)*30*60);//对数组初始化(如果不初始化FAIL后第二次循环会IF.pos会保留上次数据int i 0;//墙体轮廓SetPos(40, 0);for (i 40; i 60; i 2){wprintf(L%lc, WALL);}SetPos(0,29);for (i 0; i 60; i 2){IF.pos[i][29] 1;wprintf(L%lc, WALL);}//x是0y从1开始增⻓for (i 0; i 30; i){IF.pos[0][i] 1;//标记此处有方块SetPos(0, i);wprintf(L%c, WALL);}for (i 0; i 30; i){IF.pos[38][i] 1;SetPos(38, i);wprintf(L%c, WALL);}for (i 0; i 30; i){IF.pos[58][i] 1;SetPos(58, i);wprintf(L%c, WALL);}SetPos(40, 10);for (i 40; i 60; i 2){IF.pos[i][10] 1;wprintf(L%lc, WALL);}//提示字体SetPos(40, 1);printf(下一个方块);SetPos(44, 12);printf(左移←);SetPos(44, 14);printf(右移→);SetPos(44, 16);printf(加速↓);SetPos(44, 18);printf(变换↑);SetPos(44, 20);printf(暂停: 空格);SetPos(44, 22);printf(退出: Esc);SetPos(44, 24);printf(重新开始:R);SetPos(44, 26);printf(当前分数%d, grade);} 注解 1一个宽字符占2个字节我们设置 cols60 lines30实际就是长宽各30个字节大小 2如上图所示游戏展示区长为20高为30右上部分为下一个方块提示区域正下面为游戏操作提示区 3横轴为x轴向右坐标逐渐增大向下为y轴坐标逐渐增大。x,y)确定宽字符小方块的位置因为宽字符小方块占2个字节宽度所以x轴坐标没有奇数值 4为了方便检测下落方块是否靠墙、落地及碰到其它方块我们定义一个大数组pos[60][30],一一对应地图上每个宽字符小方块)的位置用大数组pos[60][30]记录墙体位置及落地的方块位置 typedef struct InterFace
{int pos[60][30]; //用于标记整个界面有方块的位置1为有0为无}InterFace;InterFace IF; 4.2.4 初始化方块信息
void InitBlockInfo()
{//T字型int i 0;for (i 0; i 3; i){block[0][0].space[1][i] 1;}block[0][0].space[2][1] 1;//‘L字型for (i 1; i 4; i){block[1][0].space[i][1] 1;}block[1][0].space[3][2] 1;//J字形for (i 1; i 4; i){block[2][0].space[i][2] 1;}block[2][0].space[3][1] 1;//’Z字型for (i 0; i 2; i){block[3][0].space[1][i] 1;}for (i 1; i 3; i){block[3][0].space[2][i] 1;}//‘S字型for (i 1; i 3; i){block[4][0].space[1][i] 1;}for (i 0; i 2; i){block[4][0].space[2][i] 1;}//田’字型int j 0;for (i 1; i 3; i){for (j 1; j 3; j){block[5][0].space[i][j] 1;}}//长条型for (i 0; i 4; i){block[6][0].space[i][1] 1;}int m 0;int n 0;int tmp[4][4];for (i 0; i 7; i){block[i][0].speed 200;//初始化下落速度block[i][0].blockstate NORMAL;}for (i 0; i LINES; i){for (j 0; j COLS-1; j){for (m 0; m 4; m){for (n 0; n 4; n){tmp[m][n] block[i][j].space[m][n];}}for (m 0; m 4; m){for (n 0; n 4; n){block[i][j 1].space[n][3-m] tmp[m][n];}}block[i][j 1].speed 200;//初始化下落速度block[i][j 1].blockstate NORMAL;//初始化方块状态}}} 注解
1我们定义结构体二位数组用于存储7种基本形状方块的各自的4种形态的信息共28种。
#define COLS 4
#define LINES 7
enum BlockState{CHANGE,LEFT,RIGHT,DOWN,NORMAL};
typedef struct Block
{int space[4][4];int speed;enum BlockState blockstate;
}Block;
Block block[LINES][COLS] ;//结构体二位数组用于存储7种基本形状方块的各自的4种形态的信息共28种 2首先初始化7种基本形状方块然后基本形状方块通过顺时针旋转依次得到各自剩下的三种形态。 4.3 游戏运行的实现
void GameRun()
{int i rand() % 7, j rand() % 4; //随机获取方块的形状do{int x 18;int y 0;int next_i rand() % 7, next_j rand() % 4;PrintBlock(next_i, next_j, 46, 4);//绘制提示框图形PrintBlock(i, j, 18, 0);//绘制方块初始位置while(IsLegal(i, j, x, y ))//不合法说明到底了{if (KEY_PRESS(VK_UP)){block[i][j].blockstate CHANGE;}else if (KEY_PRESS(VK_DOWN)){block[i][j].blockstate DOWN;}else if (KEY_PRESS(VK_LEFT)){block[i][j].blockstate LEFT;//左移}else if (KEY_PRESS(VK_RIGHT)){block[i][j].blockstate RIGHT;//右移}else if (KEY_PRESS(VK_SPACE)){pause();//暂停}else if (KEY_PRESS(VK_ESCAPE)){state END;//退出break;}else if (KEY_PRESS(R)){state RESTART;//重新开始break;}else//什么都没按{block[i][j].blockstate NORMAL;}BlockMove(block, i, j, x,y);PrintBlock(i, j, x, y);//绘制方块位置Sleep(block[i][j].speed);//每一帧休息的时间}i next_i;j next_j;PrintSpace(i,j, 46, 4);//覆盖提示框图形} while (state RUN);
}
4.3.1 合法判断碰撞检测
//碰撞检测
int IsLegal(int i, int j, int x, int y)
{for (int m 0; m 4; m){for (int n 0; n 4; n){//如果方块落下的位置本来就已经有方块了则不合法if ((block[i][j].space[m][n] 1) (IF.pos[x n * 2][y m] 1))return 0; //不合法}}return 1; //合法
}
4.3.2 覆盖方块
void PrintSpace(int i, int j, int x, int y)//覆盖指定位置方块
{for (int m 0; m 4; m){for (int n 0; n 4; n){if (block[i][j].space[m][n] 1){SetPos(x2*n,ym); //光标跳转到指定位置printf( ); //打印空格覆盖两个空格}}}}
4.3.3 绘制方块
void PrintBlock(int i, int j, int x, int y)
{
int m 0;
int n 0;
for (m 0; m 4; m)
{for (n 0; n 4; n){if (block[i][j].space[m][n] 1){//IF.pos[xn*2][ym] 1;//记录该位置有方块SetPos(x n * 2, y m);wprintf(L%c, BIOCK);}}
}
} 4.3.4 判断满行消除
int JudeIsErase()
{int sign 0;int i 0;for (i 28; i 3; i--){int count 0;for (int j 2; j 38; j 2){if (IF.pos[j][i] 1){count;}}if (count 18)//整行全是方块{sign 1;grade 10;SetPos(44, 26);printf(当前分数%d, grade);//更新当前分数for (int a 2; a 38; a 2){SetPos(a, i);printf( );IF.pos[a][i] 0;}//将该行以上全部下移一格for (int m i; m 3; m--){for (int n 2; n 38; n 2){IF.pos[n][m] IF.pos[n][m - 1]; //将上一行方块移到下一行if (IF.pos[n][m]1) //上一行移下来的是方块打印方块{SetPos(n, m); //光标跳转到该位置wprintf(L%c, BIOCK); //打印方块}else //上一行移下来的是空格打印空格{SetPos(n, m); //光标跳转到该位置printf( ); //打印空格两个空格}}}}}return sign;} 注 1)用大数组pos[60][30],从下往上判断是否满行如果满行则将将该行以上全部下移一格这样就达到消行的目的 2若第十四和第十三行都满行了当检测到第十四行满行将第十三行及以上全部下移一格再次循环从现十三行原十二行开始检测此时会漏掉现十四行原十三行所以引进标识变量sign,只要有消行则变为1当sign0时表示全部方块没有满行了。 4.4.5 判断游戏失败
void JudeIsOver()
{for (int i 2; i 38; i2){if (IF.pos[i][1] 1) //第一行有方块存在{state FAIL;}}}4.4.6 方块移动 void BlockMove(Block block[][COLS], int *i, int *j, int *x, int *y)
{switch (block[*i][*j].blockstate){case CHANGE:if (IsLegal(*i, ((*j) 1) % 4, *x, (*y )1) 1) //判断方块变化后(变化的同时下降一格是否碰撞{//方块旋转后合法才进行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置(*j) ((*j) 1) % 4;(*y);//PrintBlock(i, j, x, y);//绘制下一个变化的方块block[*i][*j].blockstate NORMAL;block[*i][*j].speed 200;}//else //方块再下落就碰撞了已经到达底部//{// //将当前方块的信息录入IF当中// for (int m 0; m 4; m)// {// for (int n 0; n 4; n)// {// if (block[*i][*j].space[m][n] 1)// {// IF.pos[(*x )n*2][(*y )m] 1; //将该位置标记为有方块// // }// }// }// while (JudeIsErase()); //判断此次方块下落是否得分// JudeIsOver();//}break;case LEFT:if (IsLegal(*i, *j,(*x )-2, *y ) 1) //判断方块左移后是否合法{//方块左移后合法才进行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置(*x) (*x) - 2;block[*i][*j].blockstate NORMAL;block[*i][*j].speed 200;//PrintBlock(i, j, x, y);//绘制下一个变化的方块}break;case RIGHT:if (IsLegal(*i, *j, (*x) 2, *y) 1) //判断方块右移后是否合法{//方块右移后合法才进行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置(*x) (*x) 2;block[*i][*j].blockstate NORMAL;block[*i][*j].speed 200;//PrintBlock(i, j, x, y);//绘制下一个变化的方块}break;case DOWN:if (IsLegal(*i, *j,* x , (*y) 1) 1) //判断方块下移后是否合法{//方块下移后合法才进行以下操作PrintSpace(*i,* j, *x,* y); //用空格覆盖当前方块所在位置(*y);block[*i][*j].blockstate NORMAL;if (block[*i][*j].speed50)//避免连续按变成负数会出现bug{block[*i][*j].speed - 50;}}else //碰撞{//将当前方块的信息录入IF当中for (int m 0; m 4; m){for (int n 0; n 4; n){if (block[*i][*j].space[m][n] 1){IF.pos[(*x) n * 2][(*y) m] 1; //将该位置标记为有方块}}}while (JudeIsErase()); //判断此次方块下落是否得分JudeIsOver();}break;case NORMAL:if (IsLegal(*i, *j,*x, (*y) 1) 1) //方块再下落合法{//方块右移后合法才进行以下操作PrintSpace(*i, *j, *x,* y); //用空格覆盖当前方块所在位置(*y);block[*i][*j].blockstate NORMAL;block[*i][*j].speed200;}else //碰撞{//将当前方块的信息录入IF当中for (int m 0; m 4; m){for (int n 0; n 4; n){if (block[*i][*j].space[m][n] 1){IF.pos[(*x) n * 2][(*y) m] 1; //将该位置标记为有方块}}}while (JudeIsErase()); //判断此次方块下落是否得分JudeIsOver();}break;}}注 1这里我们根据方块状态判断方块的下一步位置和形状需要改变参数所以传的是地址 2只有在正常下落和加速下落才需要描述不合法时碰撞时状况因为此时已经到底了。变换方块时不需要描述不合法碰撞时状况因为L型在变换时容易与边界发生碰撞若此时将方块位置记录在pos[60][30]的大数组中导致跳出该循环进行下一个方块判断导致该方块悬浮在半空的BUG 4.4 游戏结束的实现
enum GameState{ RUN 1,END,FAIL,RESTART };
enum GameState state;void GameOver()
{switch (state){case END:SetPos(14, 15);printf(您主动退出游戏\n);SetPos(14, 18);system(pause);break;case FAIL:Sleep(1000); //留给玩家反应时间system(cls); //清空屏幕SetPos(20, 10);printf(小垃圾游戏结束\n);SetPos(20, 14);printf(你的最终得分为: %d , grade);SetPos(20, 18);system(pause);break;case RESTART:system(cls); //清空屏幕SetPos(26, 10);printf(重新开始!\n);SetPos(22, 14);printf(小垃圾你准备好了吗\n);Sleep(2000);main();break;}
} 注 1对游戏的三个状态分别进行描述。 五、完整代码
5.1 tetris.h
#includeWindows.h
#include locale.h
#includestdbool.h
#includestdio.h
#includestdlib.h
#includetime.h
#includewindows.h
#define WALL L□
#define BIOCK L■
#define COLS 4
#define LINES 7
#define R 0x52
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)0x1) ? 1 : 0)
enum GameState{ RUN 1,END,FAIL,RESTART };
enum GameState state;enum BlockState{CHANGE,LEFT,RIGHT,DOWN,NORMAL};
int grade;
typedef struct InterFace
{int pos[60][30]; //用于标记整个界面有方块的位置1为有0为无}InterFace;InterFace IF;
typedef struct Block
{int space[4][4];int speed;enum BlockState blockstate;
}Block;
Block block[LINES][COLS] ;//结构体二位数组用于存储7种基本形状方块的各自的4种形态的信息共28种
void GameInit();
void CreateMap();
void InitBlockInfo();
void GameRun();
void PrintBlock(int i,int j,int x,int y);//ij 为方块形状x,y为打印坐标
void BlockMove(Block block[][COLS], int *i, int *j, int *x, int *y);
void PrintSpace(int i, int j, int x, int y);
int JudeIsErase();
void JudeIsOver();
void GameOver();
5.2 tetris.c
#includetetris.h//设置光标的坐标
void SetPos(short x, short y)
{COORD pos { x, y };HANDLE hOutput NULL;//获取标准输出的句柄(⽤来标识不同设备的数值)hOutput GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posSetConsoleCursorPosition(hOutput, pos);}
void WelcomeToGame()
{SetPos(20, 10);printf(欢迎来到俄罗斯方块);SetPos(20, 23);//让按任意键继续的出现的位置好看点system(pause);system(cls);SetPos(14, 10);printf(↑为变换.↓为加速.←为左移.→为右移\n);SetPos(20, 23);//让按任意键继续的出现的位置好看点system(pause);system(cls);
}
void CreateMap()
{memset(IF.pos, 0, sizeof(int)*30*60);//对数组初始化(如果不初始化FAIL后第二次循环会IF.pos会保留上次数据int i 0;//墙体轮廓SetPos(40, 0);for (i 40; i 60; i 2){wprintf(L%lc, WALL);}SetPos(0,29);for (i 0; i 60; i 2){IF.pos[i][29] 1;wprintf(L%lc, WALL);}//x是0y从1开始增⻓for (i 0; i 30; i){IF.pos[0][i] 1;//标记此处有方块SetPos(0, i);wprintf(L%c, WALL);}for (i 0; i 30; i){IF.pos[38][i] 1;SetPos(38, i);wprintf(L%c, WALL);}for (i 0; i 30; i){IF.pos[58][i] 1;SetPos(58, i);wprintf(L%c, WALL);}SetPos(40, 10);for (i 40; i 60; i 2){IF.pos[i][10] 1;wprintf(L%lc, WALL);}//提示字体SetPos(40, 1);printf(下一个方块);SetPos(44, 12);printf(左移←);SetPos(44, 14);printf(右移→);SetPos(44, 16);printf(加速↓);SetPos(44, 18);printf(变换↑);SetPos(44, 20);printf(暂停: 空格);SetPos(44, 22);printf(退出: Esc);SetPos(44, 24);printf(重新开始:R);/*SetPos(44, 26);printf(最高纪录:%d, max);*/SetPos(44, 26);printf(当前分数%d, grade);/*getchar();*/}
void InitBlockInfo()
{//T字型int i 0;for (i 0; i 3; i){block[0][0].space[1][i] 1;}block[0][0].space[2][1] 1;//‘L字型for (i 1; i 4; i){block[1][0].space[i][1] 1;}block[1][0].space[3][2] 1;//J字形for (i 1; i 4; i){block[2][0].space[i][2] 1;}block[2][0].space[3][1] 1;//’Z字型for (i 0; i 2; i){block[3][0].space[1][i] 1;}for (i 1; i 3; i){block[3][0].space[2][i] 1;}//‘S字型for (i 1; i 3; i){block[4][0].space[1][i] 1;}for (i 0; i 2; i){block[4][0].space[2][i] 1;}//田’字型int j 0;for (i 1; i 3; i){for (j 1; j 3; j){block[5][0].space[i][j] 1;}}//长条型for (i 0; i 4; i){block[6][0].space[i][1] 1;}int m 0;int n 0;int tmp[4][4];for (i 0; i 7; i){block[i][0].speed 200;//初始化下落速度block[i][0].blockstate NORMAL;}for (i 0; i LINES; i){for (j 0; j COLS-1; j){for (m 0; m 4; m){for (n 0; n 4; n){tmp[m][n] block[i][j].space[m][n];}}for (m 0; m 4; m){for (n 0; n 4; n){block[i][j 1].space[n][3 - m] tmp[m][n];}}block[i][j 1].speed 200;//初始化下落速度block[i][j 1].blockstate NORMAL;//初始化方块状态}}}void GameInit()
{//设置控制台窗⼝的⼤⼩30⾏30列system(mode con cols60 lines30);//设置cmd窗⼝名称system(title 俄罗斯方块);//获取标准输出的句柄(⽤来标识不同设备的数值)HANDLE hOutput GetStdHandle(STD_OUTPUT_HANDLE);//影藏光标操作CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(hOutput, CursorInfo);//获取控制台光标信息CursorInfo.bVisible false; //隐藏控制台光标SetConsoleCursorInfo(hOutput, CursorInfo);//设置控制台光标状态//打印欢迎界⾯WelcomeToGame();//初始化地图CreateMap();//初始化方块信息InitBlockInfo();//初始化游戏状态state RUN;
}
void PrintBlock(int i, int j, int x, int y)
{
int m 0;
int n 0;
for (m 0; m 4; m)
{for (n 0; n 4; n){if (block[i][j].space[m][n] 1){//IF.pos[xn*2][ym] 1;//记录该位置有方块SetPos(x n * 2, y m);wprintf(L%c, BIOCK);}}
}
}
void pause()//暂停
{while (1){Sleep(300);if (KEY_PRESS(VK_SPACE)){break;}}
}
//碰撞检测
int IsLegal(int i, int j, int x, int y)
{for (int m 0; m 4; m){for (int n 0; n 4; n){//如果方块落下的位置本来就已经有方块了则不合法if ((block[i][j].space[m][n] 1) (IF.pos[x n * 2][y m] 1))return 0; //不合法}}return 1; //合法
}
void PrintSpace(int i, int j, int x, int y)//覆盖指定位置方块
{for (int m 0; m 4; m){for (int n 0; n 4; n){if (block[i][j].space[m][n] 1){SetPos(x2*n,ym); //光标跳转到指定位置printf( ); //打印空格覆盖两个空格}}}}
int JudeIsErase()
{int sign 0;int i 0;for (i 28; i 3; i--){int count 0;for (int j 2; j 38; j 2){if (IF.pos[j][i] 1){count;}}if (count 18)//整行全是方块{sign 1;grade 10;SetPos(44, 26);printf(当前分数%d, grade);//更新当前分数for (int a 2; a 38; a 2){SetPos(a, i);printf( );IF.pos[a][i] 0;}//将该行以上全部下移一格for (int m i; m 3; m--){for (int n 2; n 38; n 2){IF.pos[n][m] IF.pos[n][m - 1]; //将上一行方块移到下一行if (IF.pos[n][m]1) //上一行移下来的是方块打印方块{SetPos(n, m); //光标跳转到该位置wprintf(L%c, BIOCK); //打印方块}else //上一行移下来的是空格打印空格{SetPos(n, m); //光标跳转到该位置printf( ); //打印空格两个空格}}}}}return sign;}
void JudeIsOver()
{for (int i 2; i 38; i2){if (IF.pos[i][1] 1) //第一行有方块存在{state FAIL;}}}void BlockMove(Block block[][COLS], int *i, int *j, int *x, int *y)
{switch (block[*i][*j].blockstate){case CHANGE:if (IsLegal(*i, ((*j) 1) % 4, *x, (*y )1) 1) //判断方块变化后(变化的同时下降一格是否碰撞{//方块旋转后合法才进行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置(*j) ((*j) 1) % 4;(*y);//PrintBlock(i, j, x, y);//绘制下一个变化的方块block[*i][*j].blockstate NORMAL;block[*i][*j].speed 200;}//else //方块再下落就碰撞了已经到达底部//{// //将当前方块的信息录入IF当中// for (int m 0; m 4; m)// {// for (int n 0; n 4; n)// {// if (block[*i][*j].space[m][n] 1)// {// IF.pos[(*x )n*2][(*y )m] 1; //将该位置标记为有方块// // }// }// }// while (JudeIsErase()); //判断此次方块下落是否得分// JudeIsOver();//}break;case LEFT:if (IsLegal(*i, *j,(*x )-2, *y ) 1) //判断方块左移后是否合法{//方块左移后合法才进行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置(*x) (*x) - 2;block[*i][*j].blockstate NORMAL;block[*i][*j].speed 200;//PrintBlock(i, j, x, y);//绘制下一个变化的方块}break;case RIGHT:if (IsLegal(*i, *j, (*x) 2, *y) 1) //判断方块右移后是否合法{//方块右移后合法才进行以下操作PrintSpace(*i, *j, *x, *y); //用空格覆盖当前方块所在位置(*x) (*x) 2;block[*i][*j].blockstate NORMAL;block[*i][*j].speed 200;//PrintBlock(i, j, x, y);//绘制下一个变化的方块}break;case DOWN:if (IsLegal(*i, *j,* x , (*y) 1) 1) //判断方块下移后是否合法{//方块下移后合法才进行以下操作PrintSpace(*i,* j, *x,* y); //用空格覆盖当前方块所在位置(*y);block[*i][*j].blockstate NORMAL;if (block[*i][*j].speed50)//避免连续按变成负数会出现bug{block[*i][*j].speed - 50;}}else //碰撞{//将当前方块的信息录入IF当中for (int m 0; m 4; m){for (int n 0; n 4; n){if (block[*i][*j].space[m][n] 1){IF.pos[(*x) n * 2][(*y) m] 1; //将该位置标记为有方块}}}while (JudeIsErase()); //判断此次方块下落是否得分JudeIsOver();}break;case NORMAL:if (IsLegal(*i, *j,*x, (*y) 1) 1) //方块再下落合法{//方块右移后合法才进行以下操作PrintSpace(*i, *j, *x,* y); //用空格覆盖当前方块所在位置(*y);block[*i][*j].blockstate NORMAL;block[*i][*j].speed200;}else //碰撞{//将当前方块的信息录入IF当中for (int m 0; m 4; m){for (int n 0; n 4; n){if (block[*i][*j].space[m][n] 1){IF.pos[(*x) n * 2][(*y) m] 1; //将该位置标记为有方块}}}while (JudeIsErase()); //判断此次方块下落是否得分JudeIsOver();}break;}}void GameRun()
{int i rand() % 7, j rand() % 4; //随机获取方块的形状do{int x 18;int y 0;int next_i rand() % 7, next_j rand() % 4;PrintBlock(next_i, next_j, 46, 4);//绘制提示框图形PrintBlock(i, j, 18, 0);//绘制方块初始位置while(IsLegal(i, j, x, y ))//不合法说明到底了{if (KEY_PRESS(VK_UP)){block[i][j].blockstate CHANGE;}else if (KEY_PRESS(VK_DOWN)){block[i][j].blockstate DOWN;}else if (KEY_PRESS(VK_LEFT)){block[i][j].blockstate LEFT;//左移}else if (KEY_PRESS(VK_RIGHT)){block[i][j].blockstate RIGHT;//右移}else if (KEY_PRESS(VK_SPACE)){pause();//暂停}else if (KEY_PRESS(VK_ESCAPE)){state END;//退出break;}else if (KEY_PRESS(R)){state RESTART;//重新开始break;}else//什么都没按{block[i][j].blockstate NORMAL;}BlockMove(block, i, j, x,y);PrintBlock(i, j, x, y);//绘制方块位置Sleep(block[i][j].speed);//每一帧休息的时间}i next_i;j next_j;PrintSpace(i,j, 46, 4);//覆盖提示框图形} while (state RUN);
}
void GameOver()
{switch (state){case END:SetPos(14, 15);printf(您主动退出游戏\n);SetPos(14, 18);system(pause);break;case FAIL:Sleep(1000); //留给玩家反应时间system(cls); //清空屏幕SetPos(20, 10);printf(小垃圾游戏结束\n);SetPos(20, 14);printf(你的最终得分为: %d , grade);SetPos(20, 18);system(pause);break;case RESTART:system(cls); //清空屏幕SetPos(26, 10);printf(重新开始!\n);SetPos(22, 14);printf(小垃圾你准备好了吗\n);Sleep(2000);main();break;}
}
5.3 test.c
#includetetris.hint main()
{//修改当前地区为本地模式为了⽀持中⽂宽字符的打印setlocale(LC_ALL, );srand((unsigned int)time(NULL));char ch0;do{GameInit();GameRun();GameOver();system(cls);SetPos(20, 12);printf(再来一局吗(Y/N):\n);SetPos(22, 15);ch getchar();getchar();//清理\n} while (ch y || ch Y);SetPos(0, 29);return 0;
}