功能结构
因为在玩的时候,需要用户自主按键,所以在控制台界面实现。
欢迎界面的搭建
#define FrameX 13 //游戏窗口左上角的X轴坐标
#define FrameY 3 //游戏窗口左上角的Y轴坐标
#define Frame_height 20 //游戏窗口的高度
#define Frame_width 18 //游戏窗口的宽度
struct Tetris
{
int number; //方块个数
int score; //成绩
int speed; //速度
int leval; //等级
int x;
int y;
int flag; //当前方块序号
int next; //下一个方块序号
};
HANDLE hOut;
int n;
int i, j, Temp, Temp1, Temp2; //Temp,Temp1,Temp2用来存放方块变换的过程值
int a[80][80] = { 0 }; //用来标记游戏屏幕图案,0表示没有方块,1表示有方块,2表示边框
int b[4]; //用来存放方块的位置,因为每个俄罗斯方块都是由4个小方块组成,所以创建大小为4的数组
因为在布局上面需要坐标进行调整,所以我们先写出坐标函数,gotoxy(int x,int y)
void gotoxy(int x,int y)
{
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
COORD不懂可以看一下 :https://zhidao.baidu.com/question/243419995.html
COORD pos 就是一个结构体变量,使用SetConsoleCursorPosition函数来定位光标位置。
我们需要将字体,字符颜色进行调整,所以改变颜色函数color(int n)
int color(int n)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), n);
return 0;
}
SetConsoleTextAttribute是控制台窗口颜色背景颜色的函数。
实现标题位置与颜色改变,就需要上面的,坐标函数以及颜色改变函数。
void title()
{
color(15);
gotoxy(24, 3);
printf(" 俄 罗 斯 方 块 \n");
color(11);
gotoxy(18, 5);
printf("■");
gotoxy(18, 6);
printf("■■");
gotoxy(18, 7);
printf("■");
color(14);
gotoxy(26, 6);
printf("■■");
gotoxy(28, 7);
printf("■■");//
color(10);
gotoxy(36, 6);
printf("■■");
gotoxy(36, 7);
printf("■■");
color(13);
gotoxy(45, 5);
printf("■");
gotoxy(45, 6);
printf("■");
gotoxy(45, 7);
printf("■");
gotoxy(45, 8);
printf("■");
color(12);
gotoxy(56, 6);
printf("■");//
gotoxy(52, 7);
printf("■■■");
}
void welcom()
{
color(12);
gotoxy(25, 12);
printf("1.开始游戏");
gotoxy(40, 12);
printf("2.按键说明");
gotoxy(25, 17);
printf("3.游戏规则");
gotoxy(40, 17);
printf("4.退出");
gotoxy(21, 22);
color(3);
printf("请选择[1 2 3 4]:");
scanf_s("%d", &n); //输入选项
switch (n)
{
case 1:
system("cls");
DrwaGameframe(); //制作游戏窗口,后面会重点讲
Gameplay(); //开始游戏,后面会重点讲
break;
case 2:
explation(); //按键说明函数
break;
case 3:
regulation(); //游戏规则函数
break;
case 4:
exit(0); //关闭游戏函数
break;
}
}
void DrwaGameframe()
{
gotoxy(FrameX + Frame_width - 8, FrameY - 2);
color(12);
printf("俄 罗 斯 方 块");
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 7);
color(14);
printf("*** 下一个方块 ***");
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 13);
printf("*** *** *** ***");
color(13);
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 15);
printf("↑:反转");
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 17);
printf("空格:暂停");
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 19);
printf("esc:退出");
a[FrameX][FrameY + Frame_height] = 2;
a[FrameX + 2 * Frame_width - 2][FrameY + Frame_height] = 2;
for (int i = 2; i < 2 * Frame_width - 2; ++i)
{
gotoxy(FrameX + i, FrameY);
printf("═");
}
for (int i = 2; i < 2 * Frame_width - 2; ++i)
{
gotoxy(FrameX + i, FrameY + Frame_height);
printf("═");
a[FrameX + i][FrameY + Frame_height] = 2;
}
for (int i = 1; i < Frame_height; i++)
{
gotoxy(FrameX, FrameY + i);
printf("║");
a[FrameX][FrameY + i] = 2;
}
for (int i = 1; i < Frame_height; i++)
{
gotoxy(FrameX + 2 * Frame_width - 2, FrameY + i);
printf("║");
a[FrameX + 2 * Frame_width - 2][FrameY + i] = 2;
}
}
因为俄罗斯方块有7个种类
经过上面7种方块,不断进行翻转,总共有19种变化。
void MackTetris(struct Tetris *tetris)
{
a[tetris->x][tetris->y] = b[0]; //中心方块位置的图形状态
switch (tetris->flag) //共7大类,19种类型
{
case 1: /*田字方块 ■■
□■ */
{
color(10);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x + 2][tetris->y - 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 2: /*直线方块 ■□■■*/
{
color(13);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x + 2][tetris->y] = b[2];
a[tetris->x + 4][tetris->y] = b[3];
break;
}
case 3: /*直线方块 ■
■
□
■ */
{
color(13);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y - 2] = b[2];
a[tetris->x][tetris->y + 1] = b[3];
break;
}
case 4: /*T字方块 ■□■
■ */
{
color(11);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x + 2][tetris->y] = b[2];
a[tetris->x][tetris->y + 1] = b[3];
break;
}
case 5: /* 顺时针90°T字方块 ■
■□
■*/
{
color(11);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y + 1] = b[2];
a[tetris->x - 2][tetris->y] = b[3];
break;
}
case 6: /* 顺时针180°T字方块 ■
■□■*/
{
color(11);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x - 2][tetris->y] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 7: /* 顺时针270°T字方块 ■
□■
■ */
{
color(11);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y + 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 8: /* Z字方块 ■□
■■*/
{
color(14);
a[tetris->x][tetris->y + 1] = b[1];
a[tetris->x - 2][tetris->y] = b[2];
a[tetris->x + 2][tetris->y + 1] = b[3];
break;
}
case 9: /* 顺时针Z字方块 ■
■ □
■ */
{
color(14);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x - 2][tetris->y] = b[2];
a[tetris->x - 2][tetris->y + 1] = b[3];
break;
}
case 10: /* 反转Z字方块 ■■
■□ */
{
color(14);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x - 2][tetris->y - 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 11: /* 顺时针Z字方块 ■
■□
■ */
{
color(14);
a[tetris->x][tetris->y + 1] = b[1];
a[tetris->x - 2][tetris->y - 1] = b[2];
a[tetris->x - 2][tetris->y] = b[3];
break;
}
case 12: /* 7字方块 ■■
□
■ */
{
color(12);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y + 1] = b[2];
a[tetris->x - 2][tetris->y - 1] = b[3];
break;
}
case 13: /* 顺时针90°7字方块 ■
■□■*/
{
color(12);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x + 2][tetris->y - 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 14: /* 顺时针180°7字方块 ■
□
■■ */
{
color(12);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y + 1] = b[2];
a[tetris->x + 2][tetris->y + 1] = b[3];
break;
}
case 15: /* 顺时针270°7字方块 ■□■
■ */
{
color(12);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x - 2][tetris->y + 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 16: /* 反转7字方块 ■■
□
■ */
{
color(12);
a[tetris->x][tetris->y + 1] = b[1];
a[tetris->x][tetris->y - 1] = b[2];
a[tetris->x + 2][tetris->y - 1] = b[3];
break;
}
case 17: /* 顺时针转90°7字方块 ■□■
■*/
{
color(12);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x + 2][tetris->y + 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 18: /* 顺时针转180°7字方块 ■
□
■■ */
{
color(12);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y + 1] = b[2];
a[tetris->x - 2][tetris->y + 1] = b[3];
break;
}
case 19: /* 顺指针转270°7字方块 ■
■□■*/
{
color(12);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x - 2][tetris->y - 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
}
}
上面代码因排版的问题,注释的图案不太清楚,如果理解不足的话,可以自己画一下。
在这些俄罗斯方块中定义一个中心小方块,用于表示其余三个小方块。
打印俄罗斯方块
void PrintTetris(struct Tetris *tetris)
{
for (int i = 0; i < 4; ++i) //因为用数组表示小方块,所以每个元素值为1
{
b[i] = 1;
}
MackTetris(tetris); //制作俄罗斯方块
for (i = tetris->x - 2; i <= tetris->x + 4; i += 2) //打印方块,因为tetris->x为中心方块的坐标,
//同时一个俄罗斯方块由4个小方块组成,所以其最大值为tetris->x + 4
//因为小方块占2个字符,所以加2
{
for (j = tetris->y - 2; i <= tetris->y + 1; j++)
{
if (a[i][j] == 1 && j > FrameY)
{
gotoxy(i, j);
printf("■");
}
}
}
//以下代码为打印旁边成绩区
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 1);
color(4);
printf("level : ");
color(12);
printf(" %d", tetris->level);
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 3);
color(4);
printf("score : ");
color(12);
printf(" %d", tetris->score);
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 5);
color(4);
printf("speed : ");
color(12);
printf(" %dms", tetris->speed);
}
在做完前期工作后,接下来需要判断:
方块下落时,下面的位置可不可以放下该方块,左移或右移是否可以移动;
如果可以移动,擦除上一次所在位置,在下一个位置打印一下方块;
满行时,清除该行方块;
随机产生方块类型。
判断方块可否移动
首先,判断该位置是不是空,如果能放下该方块,则表示该方块可以移动。
判断是否为空,首先判断该位置中心方块是不是空,接着判断该类型的俄罗斯方块各自的“■”位置上是否有图案,如果没有,则表示可以移动。
判断方法:在刚开始定义全局变量时,a[80][80]= { 0 },同时,在打印方块时b[i] = 1。因此可以用 a[x][y] == 1,来判断该点是否为空。
int ifMove(struct Tetris *tetris) //判断该点是否可以移动
{
if(a[tetris->x][tetris->y]!=0)//当中心方块位置上有图案时,返回值为0,即不可移动
{
return 0;
}
else
{
if(
( tetris->flag==1 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==2 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x+2][tetris->y]==0 && a[tetris->x+4][tetris->y]==0 ) ) ||
( tetris->flag==3 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y-2]==0 && a[tetris->x][tetris->y+1]==0 ) ) ||
( tetris->flag==4 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x+2][tetris->y]==0 && a[tetris->x][tetris->y+1]==0 ) ) ||
( tetris->flag==5 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y]==0 ) ) ||
( tetris->flag==6 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x-2][tetris->y]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==7 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==8 && ( a[tetris->x][tetris->y+1]==0 &&
a[tetris->x-2][tetris->y]==0 && a[tetris->x+2][tetris->y+1]==0 ) ) ||
( tetris->flag==9 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x-2][tetris->y]==0 && a[tetris->x-2][tetris->y+1]==0 ) ) ||
( tetris->flag==10 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x-2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==11 && ( a[tetris->x][tetris->y+1]==0 &&
a[tetris->x-2][tetris->y-1]==0 && a[tetris->x-2][tetris->y]==0 ) ) ||
( tetris->flag==12 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y-1]==0 ) ) ||
( tetris->flag==15 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x-2][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==14 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y+1]==0 && a[tetris->x+2][tetris->y+1]==0 ) ) ||
( tetris->flag==13 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==16 && ( a[tetris->x][tetris->y+1]==0 &&
a[tetris->x][tetris->y-1]==0 && a[tetris->x+2][tetris->y-1]==0 ) ) ||
( tetris->flag==19 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x-2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==18 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y+1]==0 ) ) ||
( tetris->flag==17 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x+2][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) )
{
return 1;
}
}
return 0;
}
tetris->flag 用来判断该俄罗斯方块的方块类型,同时根据上面制作方块种类的所设置的中心方块,来判断其余三个方块的位置上是否为空。
由于上述有19种类型,每个类型都需要考虑一遍。
清理方块
清理方块有点类似于上面的打印方块,上面打印方块就是将b[i] = 1,在对应的位置上打印“■”,清理刚好相反,在对应的位置b[i] ,设置为0,在对应的位置上打印空格。
void CleanTetris(struct Tetris *tetris)
{
for (int i = 0; i < 4; ++i)
{
b[i] = 0;
}
MackTetris(tetris);
for (i = tetris->x - 2; i <= tetris->x + 4; i += 2) //■X ■■ X为中心方块
{
for (j = tetris->y - 2; j <= tetris->y + 1; j++)
{
if (a[i][j] == 0 && j > FrameY)
{
gotoxy(i, j);
printf(" ");
}
}
}
}
随机产生方块种类
void Flag(struct Tetris *tetris)
{
tetris->number++;
srand(time(NULL));
if (tetris->number == 1)
{
tetris->flag = rand() % 19 + 1;
}
tetris->flag = rand() % 19 + 1;
}
调用随机函数,flag在定义的时候就是留做当前俄罗斯方块的序号,number= 1表示刚开始,tetris->flag = rand() % 19 + 1;
判断是否满行,并删除满行
在删除时,需要两个操作,1:判断满行并删除,2:将满行上面的方块下移
void Del_Fullline(struct Tetris *tetris)
{
int k, del_rows = 0; //分别用于记录某行方块的个数和删除方块的行数的变量
for (j = FrameY + Frame_height - 1; j >= FrameY + 1; j--)
{
k = 0;
for (i = FrameX + 2; iFrameY; k--) //如果删除行以上的位置有方块,则先清除,再将方块下移一个位置
{
for (i = FrameX + 2; iscore += 10 * del_rows; //每删除一行,得10分
if (del_rows>0 && (tetris->score % 100 == 0 || tetris->score / 100>tetris->level - 1))
{ //如果得1000分即累计删除10行,速度加快20ms并升一级
tetris->speed -= 20;
tetris->level++;
}
}
test
void Gameplay()
{
int n;
struct Tetris t, *tetris = &t; //定义结构体的指针并指向结构体变量
char ch; //定义接收键盘输入的变量
tetris->number = 0; //初始化俄罗斯方块数为0个
tetris->speed = 300; //初始移动速度为300ms
tetris->score = 0; //初始游戏的分数为0分
tetris->level = 1; //初始游戏为第1关
while (1)
{
Flag(tetris); //随机产生方序号
Temp = tetris->flag; //记住当前俄罗斯方块序号
tetris->x = FrameX + 2 * Frame_width + 6; //获得预览界面方块的x坐标
tetris->y = FrameY + 10; /**/
tetris->flag = tetris->next; //获取下一个俄罗斯方块的序号
PrintTetris(tetris);
tetris->x = FrameX + Frame_width; //获取窗口中心方块的坐标
tetris->y = FrameY - 1;
tetris->flag = Temp; //取出当前的俄罗斯方块序号
while (1)
{
label:PrintTetris(tetris); //打印俄罗斯方块
Sleep(tetris->speed); //延缓时间
CleanTetris(tetris);
Temp1 = tetris->x;
Temp2 = tetris->flag;
if (_kbhit()) //判断键盘输入
{
ch = _getch();
if (ch == 75) //左移键ASCII码
{
tetris->x -= 2;
}
if (ch == 77) //按 →键则向右动,中心横坐标加2
{
tetris->x += 2;
}
if (ch == 72) //按 ↑键则变体,即当前方块顺时针转90度
{
if (tetris->flag >= 2 && tetris->flag <= 3)
{
tetris->flag++;
tetris->flag %= 2;
tetris->flag += 2;
}
if (tetris->flag >= 4 && tetris->flag <= 7)
{
tetris->flag++;
tetris->flag %= 4;
tetris->flag += 4;
}
if (tetris->flag >= 8 && tetris->flag <= 11)
{
tetris->flag++;
tetris->flag %= 4;
tetris->flag += 8;
}
if (tetris->flag >= 12 && tetris->flag <= 15)
{
tetris->flag++;
tetris->flag %= 4;
tetris->flag += 12;
}
if (tetris->flag >= 16 && tetris->flag <= 19)
{
tetris->flag++;
tetris->flag %= 4;
tetris->flag += 16;
}
}
if (ch == 32) //按空格键,暂停
{
PrintTetris(tetris);
while (1)
{
if (_kbhit()) //再按空格键,继续游戏
{
ch = _getch();
if (ch == 32)
{
goto label;
}
}
}
}
if (ch == 27)
{
system("cls");
memset(a, 0, 6400 * sizeof(int)); //初始化BOX数组
welcom();
}
if (ifMove(tetris) == 0) //如果不可动,上面操作无效
{
tetris->x = Temp1;
tetris->flag = Temp2;
}
else //如果可动,执行操作
{
goto label;
}
}
tetris->y++; //没有按键tetris->y++;
if (ifMove(tetris) == 0)
{
tetris->y--;
PrintTetris(tetris);
Del_Fullline(tetris);
break;
}
}
for (i = tetris->y - 2; i < tetris->y + 2; i++)
{
if (FrameY == i) //表示到顶
{
system("cls");
gotoxy(17, 18);
color(14);
printf("1.再来一局");
gotoxy(44, 18);
printf("2.退出");
scanf_s("%d", &n);
switch (n)
{
case 1:
system("cls");
Replay(tetris);
break;
case 2:
break;
}
}
}
tetris->flag = tetris->next; //清理右边窗口
tetris->x = FrameX + 2 * Frame_width + 6;
tetris->y = FrameY + 10;
CleanTetris(tetris);
}
}
void Replay(struct Tetris *tetris)
{
system("cls"); //清屏
memset(a, 0, 6400 * sizeof(int)); //初始化BOX数组,否则不会正常显示方块,导致游戏直接结束
DrwaGameframe(); //制作游戏窗口
Gameplay(); //开始游戏
}
操作说明
void explation()
{
color(13);
system("cls");
gotoxy(32, 3);
printf("按键说明");
gotoxy(26, 6);
printf("1:玩家可以根据← →来进行移动方块");
gotoxy(26, 8);
printf("2:通过↑来变换方块");
gotoxy(26, 10);
printf("3:空格可以暂停游戏");
gotoxy(26, 12);
printf("4:Esc暂停游戏");
gotoxy(26, 14);
printf("\n");
system("pause");
system("cls");
main();
}
void regulation()
{
color(14);
system("cls");
gotoxy(36, 3);
printf("游戏规则");
gotoxy(20, 6);
printf("由小方块组成的不同形状的板块陆续从屏幕上方落下来");
gotoxy(20, 8);
printf("玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条");
gotoxy(20, 10);
printf("这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励");
gotoxy(20, 12);
printf("没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束");
printf("\n\n");
system("pause"); //暂停函数
system("cls");
main();
}
#pragma once
#include
#include
#include
#include
void gotoxy(int x, int y); //光标移到指定位置
void DrwaGameframe(); //绘制游戏边框
void Flag(struct Tetris *); //随机产生方块类型的序号
void MakeTetris(struct Tetris *); //制作俄罗斯方块
void PrintTetris(struct Tetris *); //打印俄罗斯方块
void CleanTetris(struct Tetris *); //清除俄罗斯方块的痕迹
int ifMove(struct Tetris *); //判断是否能移动,返回值为1,能移动,否则,不能移动
void Del_Fullline(struct Tetris *); //判断是否满行,并删除满行的俄罗斯方块
void Gameplay(); //开始游戏
void regulation(); //游戏规则
void explation(); //按键说明
void welcom(); //欢迎界面
void Replay(struct Tetris *); //重新开始游戏
void title(); //欢迎界面上方的标题
tetris.c文件
#include"Tetris.h"
#define FrameX 13 //游戏窗口左上角的X轴坐标
#define FrameY 3 //游戏窗口左上角的Y轴坐标
#define Frame_height 20 //游戏窗口的高度
#define Frame_width 18 //游戏窗口的宽度
struct Tetris
{
int number; //方块个数
int score; //成绩
int speed; //速度
int level; //等级
int x;
int y;
int flag; //当前方块序号
int next; //下一个方块序号
};
HANDLE hOut;
int n;
int i, j, Temp, Temp1, Temp2; //Temp,Temp1,Temp2用来存放方块变换的过程值
int a[80][80] = { 0 }; //用来标记游戏屏幕图案,0表示没有方块,1表示有方块,2表示边框
int b[4]; //用来存放方块的位置,因为每个俄罗斯方块都是由4个小方块组成,所以创建大小为4的数组
void gotoxy(int x,int y)
{
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
int color(int n)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), n);
return 0;
}
void title()
{
color(15);
gotoxy(24, 3);
printf(" 俄 罗 斯 方 块 \n");
color(11);
gotoxy(18, 5);
printf("■");
gotoxy(18, 6);
printf("■■");
gotoxy(18, 7);
printf("■");
color(14);
gotoxy(26, 6);
printf("■■");
gotoxy(28, 7);
printf("■■");//
color(10);
gotoxy(36, 6);
printf("■■");
gotoxy(36, 7);
printf("■■");
color(13);
gotoxy(45, 5);
printf("■");
gotoxy(45, 6);
printf("■");
gotoxy(45, 7);
printf("■");
gotoxy(45, 8);
printf("■");
color(12);
gotoxy(56, 6);
printf("■");//
gotoxy(52, 7);
printf("■■■");
}
void welcom()
{
color(12);
gotoxy(25, 12);
printf("1.开始游戏");
gotoxy(40, 12);
printf("2.按键说明");
gotoxy(25, 17);
printf("3.游戏规则");
gotoxy(40, 17);
printf("4.退出");
gotoxy(21, 22);
color(3);
printf("请选择[1 2 3 4]:");
scanf_s("%d", &n); //输入选项
switch (n)
{
case 1:
system("cls");
DrwaGameframe(); //制作游戏窗口
Gameplay(); //开始游戏
break;
case 2:
explation(); //按键说明函数
break;
case 3:
regulation(); //游戏规则函数
break;
case 4:
exit(0); //关闭游戏函数
break;
}
}
void DrwaGameframe()
{
gotoxy(FrameX + Frame_width - 8, FrameY - 2);
color(12);
printf("俄 罗 斯 方 块");
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 7);
color(14);
printf("*** 下一个方块 ***");
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 13);
printf("*** *** *** ***");
color(13);
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 15);
printf("↑:反转");
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 17);
printf("空格:暂停");
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 19);
printf("esc:退出");
a[FrameX][FrameY + Frame_height] = 2;
a[FrameX + 2 * Frame_width - 2][FrameY + Frame_height] = 2;
for (int i = 2; i < 2 * Frame_width - 2; ++i)
{
gotoxy(FrameX + i, FrameY);
printf("═");
}
for (int i = 2; i < 2 * Frame_width - 2; ++i)
{
gotoxy(FrameX + i, FrameY + Frame_height);
printf("═");
a[FrameX + i][FrameY + Frame_height] = 2;
}
for (int i = 1; i < Frame_height; i++)
{
gotoxy(FrameX, FrameY + i);
printf("║");
a[FrameX][FrameY + i] = 2;
}
for (int i = 1; i < Frame_height; i++)
{
gotoxy(FrameX + 2 * Frame_width - 2, FrameY + i);
printf("║");
a[FrameX + 2 * Frame_width - 2][FrameY + i] = 2;
}
}
void MakeTetris(struct Tetris *tetris)
{
a[tetris->x][tetris->y] = b[0]; //中心方块位置的图形状态
switch (tetris->flag) //共7大类,19种类型
{
case 1: /*田字方块 ■■
□■ */
{
color(10);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x + 2][tetris->y - 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 2: /*直线方块 ■□■■*/
{
color(13);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x + 2][tetris->y] = b[2];
a[tetris->x + 4][tetris->y] = b[3];
break;
}
case 3: /*直线方块 ■
■
□
■ */
{
color(13);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y - 2] = b[2];
a[tetris->x][tetris->y + 1] = b[3];
break;
}
case 4: /*T字方块 ■□■
■ */
{
color(11);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x + 2][tetris->y] = b[2];
a[tetris->x][tetris->y + 1] = b[3];
break;
}
case 5: /* 顺时针90°T字方块 ■
■□
■*/
{
color(11);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y + 1] = b[2];
a[tetris->x - 2][tetris->y] = b[3];
break;
}
case 6: /* 顺时针180°T字方块 ■
■□■*/
{
color(11);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x - 2][tetris->y] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 7: /* 顺时针270°T字方块 ■
□■
■ */
{
color(11);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y + 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 8: /* Z字方块 ■□
■■*/
{
color(14);
a[tetris->x][tetris->y + 1] = b[1];
a[tetris->x - 2][tetris->y] = b[2];
a[tetris->x + 2][tetris->y + 1] = b[3];
break;
}
case 9: /* 顺时针Z字方块 ■
■□
■ */
{
color(14);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x - 2][tetris->y] = b[2];
a[tetris->x - 2][tetris->y + 1] = b[3];
break;
}
case 10: /* 反转Z字方块 ■■
■□ */
{
color(14);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x - 2][tetris->y - 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 11: /* 顺时针Z字方块 ■
■□
■ */
{
color(14);
a[tetris->x][tetris->y + 1] = b[1];
a[tetris->x - 2][tetris->y - 1] = b[2];
a[tetris->x - 2][tetris->y] = b[3];
break;
}
case 12: /* 7字方块 ■■
□
■ */
{
color(12);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y + 1] = b[2];
a[tetris->x - 2][tetris->y - 1] = b[3];
break;
}
case 13: /* 顺时针90°7字方块 ■
■□■*/
{
color(12);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x + 2][tetris->y - 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 14: /* 顺时针180°7字方块 ■
□
■■ */
{
color(12);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y + 1] = b[2];
a[tetris->x + 2][tetris->y + 1] = b[3];
break;
}
case 15: /* 顺时针270°7字方块 ■□■
■ */
{
color(12);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x - 2][tetris->y + 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 16: /* 反转7字方块 ■■
□
■ */
{
color(12);
a[tetris->x][tetris->y + 1] = b[1];
a[tetris->x][tetris->y - 1] = b[2];
a[tetris->x + 2][tetris->y - 1] = b[3];
break;
}
case 17: /* 顺时针转90°7字方块 ■□■
■*/
{
color(12);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x + 2][tetris->y + 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
case 18: /* 顺时针转180°7字方块 ■
□
■■ */
{
color(12);
a[tetris->x][tetris->y - 1] = b[1];
a[tetris->x][tetris->y + 1] = b[2];
a[tetris->x - 2][tetris->y + 1] = b[3];
break;
}
case 19: /* 顺指针转270°7字方块 ■
■□■*/
{
color(12);
a[tetris->x - 2][tetris->y] = b[1];
a[tetris->x - 2][tetris->y - 1] = b[2];
a[tetris->x + 2][tetris->y] = b[3];
break;
}
}
}
void PrintTetris(struct Tetris *tetris)
{
for (i = 0; i<4; i++)
{
b[i] = 1; //数组b[4]的每个元素的值都为1
}
MakeTetris(tetris); //制作游戏窗口
for (i = tetris->x - 2; i <= tetris->x + 4; i += 2)
{
for (j = tetris->y - 2; j <= tetris->y + 1; j++)
{
if (a[i][j] == 1 && j>FrameY)
{
gotoxy(i, j);
printf("■"); //打印边框内的方块
}
}
}
//打印菜单信息
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 1);
color(4);
printf("level : ");
color(12);
printf(" %d", tetris->level);
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 3);
color(4);
printf("score : ");
color(12);
printf(" %d", tetris->score);
gotoxy(FrameX + 2 * Frame_width + 3, FrameY + 5);
color(4);
printf("speed : ");
color(12);
printf(" %dms", tetris->speed);
}
int ifMove(struct Tetris *tetris) //判断该点是否可以移动
{
if(a[tetris->x][tetris->y]!=0)//当中心方块位置上有图案时,返回值为0,即不可移动
{
return 0;
}
else
{
if(
( tetris->flag==1 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==2 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x+2][tetris->y]==0 && a[tetris->x+4][tetris->y]==0 ) ) ||
( tetris->flag==3 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y-2]==0 && a[tetris->x][tetris->y+1]==0 ) ) ||
( tetris->flag==4 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x+2][tetris->y]==0 && a[tetris->x][tetris->y+1]==0 ) ) ||
( tetris->flag==5 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y]==0 ) ) ||
( tetris->flag==6 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x-2][tetris->y]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==7 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==8 && ( a[tetris->x][tetris->y+1]==0 &&
a[tetris->x-2][tetris->y]==0 && a[tetris->x+2][tetris->y+1]==0 ) ) ||
( tetris->flag==9 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x-2][tetris->y]==0 && a[tetris->x-2][tetris->y+1]==0 ) ) ||
( tetris->flag==10 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x-2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==11 && ( a[tetris->x][tetris->y+1]==0 &&
a[tetris->x-2][tetris->y-1]==0 && a[tetris->x-2][tetris->y]==0 ) ) ||
( tetris->flag==12 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y-1]==0 ) ) ||
( tetris->flag==15 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x-2][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==14 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y+1]==0 && a[tetris->x+2][tetris->y+1]==0 ) ) ||
( tetris->flag==13 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==16 && ( a[tetris->x][tetris->y+1]==0 &&
a[tetris->x][tetris->y-1]==0 && a[tetris->x+2][tetris->y-1]==0 ) ) ||
( tetris->flag==19 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x-2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||
( tetris->flag==18 && ( a[tetris->x][tetris->y-1]==0 &&
a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y+1]==0 ) ) ||
( tetris->flag==17 && ( a[tetris->x-2][tetris->y]==0 &&
a[tetris->x+2][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) )
{
return 1;
}
}
return 0;
}
void CleanTetris(struct Tetris *tetris)
{
for (int i = 0; i < 4; ++i)
{
b[i] = 0;
}
MakeTetris(tetris);
for (i = tetris->x - 2; i <= tetris->x + 4; i += 2) //■X ■■ X为中心方块
{
for (j = tetris->y - 2; j <= tetris->y + 1; j++)
{
if (a[i][j] == 0 && j > FrameY)
{
gotoxy(i, j);
printf(" ");
}
}
}
}
void Flag(struct Tetris *tetris)
{
tetris->number++;
srand((int)time(NULL));
if (tetris->number == 1)
{
tetris->flag = rand() % 19 + 1;
}
tetris->next = rand() % 19 + 1;
}
void Del_Fullline(struct Tetris *tetris)
{
int k, del_rows = 0; //分别用于记录某行方块的个数和删除方块的行数的变量
for (j = FrameY + Frame_height - 1; j >= FrameY + 1; j--)
{
k = 0;
for (i = FrameX + 2; iFrameY; k--) //如果删除行以上的位置有方块,则先清除,再将方块下移一个位置
{
for (i = FrameX + 2; iscore += 10 * del_rows; //每删除一行,得10分
if (del_rows>0 && (tetris->score % 100 == 0 || tetris->score / 100>tetris->level - 1))
{ //如果得100分即累计删除10行,速度加快30ms并升一级
tetris->speed -= 30;
tetris->level++;
}
}
void Gameplay()
{
int n;
struct Tetris t, *tetris = &t; //定义结构体的指针并指向结构体变量
char ch; //定义接收键盘输入的变量
tetris->number = 0; //初始化俄罗斯方块数为0个
tetris->speed = 300; //初始移动速度为300ms
tetris->score = 0; //初始游戏的分数为0分
tetris->level = 1; //初始游戏为第1关
while (1)
{
Flag(tetris); //随机产生方序号
Temp = tetris->flag; //记住当前俄罗斯方块序号
tetris->x = FrameX + 2 * Frame_width + 6; //获得预览界面方块的x坐标
tetris->y = FrameY + 10; /**/
tetris->flag = tetris->next; //获取下一个俄罗斯方块的序号
PrintTetris(tetris);
tetris->x = FrameX + Frame_width; //获取窗口中心方块的坐标
tetris->y = FrameY - 1;
tetris->flag = Temp; //取出当前的俄罗斯方块序号
while (1)
{
label:PrintTetris(tetris); //打印俄罗斯方块
Sleep(tetris->speed); //延缓时间
CleanTetris(tetris);
Temp1 = tetris->x;
Temp2 = tetris->flag;
if (_kbhit()) //判断键盘输入
{
ch = _getch();
if (ch == 75) //左移键ASCII码
{
tetris->x -= 2;
}
if (ch == 77) //按 →键则向右动,中心横坐标加2
{
tetris->x += 2;
}
if (ch == 72) //按 ↑键则变体,即当前方块顺时针转90度
{
if (tetris->flag >= 2 && tetris->flag <= 3)
{
tetris->flag++;
tetris->flag %= 2;
tetris->flag += 2;
}
if (tetris->flag >= 4 && tetris->flag <= 7)
{
tetris->flag++;
tetris->flag %= 4;
tetris->flag += 4;
}
if (tetris->flag >= 8 && tetris->flag <= 11)
{
tetris->flag++;
tetris->flag %= 4;
tetris->flag += 8;
}
if (tetris->flag >= 12 && tetris->flag <= 15)
{
tetris->flag++;
tetris->flag %= 4;
tetris->flag += 12;
}
if (tetris->flag >= 16 && tetris->flag <= 19)
{
tetris->flag++;
tetris->flag %= 4;
tetris->flag += 16;
}
}
if (ch == 32) //按空格键,暂停
{
PrintTetris(tetris);
while (1)
{
if (_kbhit()) //再按空格键,继续游戏
{
ch = _getch();
if (ch == 32)
{
goto label;
}
}
}
}
if (ch == 27)
{
system("cls");
memset(a, 0, 6400 * sizeof(int)); //初始化BOX数组
welcom();
}
if (ifMove(tetris) == 0) //如果不可动,上面操作无效
{
tetris->x = Temp1;
tetris->flag = Temp2;
}
else //如果可动,执行操作
{
goto label;
}
}
tetris->y++; //没有按键tetris->y++;
if (ifMove(tetris) == 0)
{
tetris->y--;
PrintTetris(tetris);
Del_Fullline(tetris);
break;
}
}
for (i = tetris->y - 2; i < tetris->y + 2; i++)
{
if (FrameY == i) //表示到顶
{
system("cls");
gotoxy(17, 18);
color(14);
printf("1.再来一局");
gotoxy(44, 18);
printf("2.退出");
scanf_s("%d", &n);
switch (n)
{
case 1:
system("cls");
Replay(tetris);
break;
case 2:
break;
}
}
}
tetris->flag = tetris->next; //清理右边窗口
tetris->x = FrameX + 2 * Frame_width + 6;
tetris->y = FrameY + 10;
CleanTetris(tetris);
}
}
void Replay(struct Tetris *tetris)
{
system("cls"); //清屏
memset(a, 0, 6400 * sizeof(int)); //初始化BOX数组,否则不会正常显示方块,导致游戏直接结束
DrwaGameframe(); //制作游戏窗口
Gameplay(); //开始游戏
}
void explation()
{
color(13);
system("cls");
gotoxy(32, 3);
printf("按键说明");
gotoxy(26, 6);
printf("1:玩家可以根据← →来进行移动方块");
gotoxy(26, 8);
printf("2:通过↑来变换方块");
gotoxy(26, 10);
printf("3:空格可以暂停游戏");
gotoxy(26, 12);
printf("4:Esc暂停游戏");
gotoxy(26, 14);
printf("\n");
system("pause");
system("cls");
main();
}
void regulation()
{
color(14);
system("cls");
gotoxy(36, 3);
printf("游戏规则");
gotoxy(20, 6);
printf("由小方块组成的不同形状的板块陆续从屏幕上方落下来");
gotoxy(20, 8);
printf("玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条");
gotoxy(20, 10);
printf("这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励");
gotoxy(20, 12);
printf("没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束");
printf("\n\n");
system("pause"); //暂停函数
system("cls");
main();
}
test.c文件
#include"Tetris.h"
int main()
{
title();
welcom();
printf("\n\n\n\n");
system("pause");
return 0;
}