俄罗斯方块(C/C++)
1.设计内容
(1)设计需要生成游戏界面,包括墙壁和底
(2)然后设计生成俄罗斯方块中七个基础方块,设计旋转程序,将其进行旋转,生成21种方块模型
(3)设计程序随机生成21中模型的方块,并自动下移,设计移动方块,left,right,down移动
(4)设计判断程序,当方块碰到墙壁和底停止移动;当方块堆满一行,消除方块;当方块碰到顶,游戏结束
(5)设计隐藏光标程序,让方块在移动过程中不显示光标
2.需求分析
在本代码中,需要规定一个游戏界卖弄,包括墙壁和底部,构建出大概类似于房子的结构,需要函数来实现方块的产生,通过键盘输入来控制方块的移动(right/left/down),旋转(空格键),需要设定判断条件,当方块触碰到墙壁或者底部,便不再继续向着墙壁或者底部的方向移动,当触碰到顶部时便结束游戏,在此时,还需在设定一个判断,看用户是否要继续游戏(y/n),如果是,便继续游戏,如果否,便按任意键结束游戏,还有就是得设定一个判断,当小方块堆积满一行便进行消除。为了美观,还需要写一个子函数来对方块进行上色和隐藏光标,当然,计分又是游戏的重要一部分,我们还需要设计子函数来对玩家的分数进行记录。
3.系统设计
共设计了10个子函数来实现俄罗斯方块的生成,旋转,自由下落,实现隐藏光标,移动光标,给方块上色,制作出游戏界面,实行判断,看方块有无碰到墙壁,看方块是否堆积成一行,进行消除。记录分数,当方块碰到顶部就退出游戏,再看用户是否要继续游戏(y/n),如果是,便继续游戏,如果否,便按任意键结束游戏。
3.1 模块划分
模块分为方块的产生与清除,方块的移动与颜色,消行于计分,隐藏光标于判断方块是否到底四部分。
功能模块图
4编码及调试
4.1 功能一编码及调试:开始游戏
实现情况:该函数成功编译通过,实现了输出游戏界面,控制旋转次数,随即生成方块,完成了游戏规则的判断,是否还可以移动,游戏是否还继续
void stat_game()
{
int n, ch, t = 0, x = 0, y = FACE_Y / 2 - 2, i, j;
int space1 = 0;//旋转次数
kongge(nn, space1, 4, FACE_Y + 3);
n = nn;
nn = rand() % 7; //随机生成下一块
color(nn);
fangkuai(nn, space1, 4, FACE_Y + 3);
while (1)
{
color(n);
fangkuai(n, space1, x, y);//画-出图形
if (t == 0)
t = 15000;
while (--t)
{
if (kbhit() != 0)//有输入就跳出
break;
}
if (t == 0)
{
if (judge(n, space1, x + 1, y) == 1)
{
kongge(n, space1, x, y);
x++; //自动向下降落
}
else //不能下落后使图形的方为aBox
{
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
if (dia[n][space1].space[i][j] == 1)
{
face.data[x + i][y + j] = Box;
face.color[x + i][y + j] = n;
while (compare());
}
}
}
return;
}
}
else
{
ch = getch();
switch (ch) //移动
{
case LEFT: if (judge(n, space1, x, y - 1) == 1) //判断是否左可以移动
{
kongge(n, space1, x, y);
y--;
}
break;
case RIGHT: if (judge(n, space1, x, y + 1) == 1) //判断是否右可以移动
{
kongge(n, space1, x, y);
y++;
}
break;
case DOWN: if (judge(n, space1, x + 1, y) == 1) //判断是否下可以移动
{
kongge(n, space1, x, y);
x++;
}
break;
case SPACE: if (judge(n, (space1 + 1) % 4, x + 1, y) == 1) //判断是否能旋转
{
kongge(n, space1, x, y);
space1 = (space1 + 1) % 4;
}
break;
case ESC: system("cls");
gotoxy(FACE_X / 2, FACE_Y);
printf("---游戏结束!---\n\n");
gotoxy(FACE_X / 2 + 2, FACE_Y);
printf("---按任意键退出---\n");
getch();
exit(0);
break;
case 'R':
case 'r': main();
exit(0);
case 'S':
case 's': while (1)
{
if (kbhit() != 0)//有输入就跳出
break;
}
break;
}
}
}
}
输入:空
输出:表现在方块的移动,输出“游戏结束”“按任意键退出”等字体
效果:
4.2 功能二编码及调试:进行消除满行
把“功能二”换成系统中的实际功能。实现情况:该函数成功编译通过,通过循环结构来实现了方块堆满一行便进行消除
int compare()
{
int i, j, k, sum;
for (i = FACE_X - 2; i > 4; i--)
{
sum = 0;
for (j = 1; j < FACE_Y - 1; j++)
{
sum += face.data[i][j];
}
if (sum == 0)
break;
if (sum == FACE_Y - 2) //满行减掉,消除
{
grade += 100;
color(7);
gotoxy(FACE_X - 4, 2 * FACE_Y + 2);
printf("分数:%d", grade);
for (j = 1; j < FACE_Y - 1; j++)
{
face.data[i][j] = Kong;
gotoxy(i, 2 * j);
printf(" ");
}
for (j = i; j > 1; j--)
{
sum = 0;
for (k = 1; k < FACE_Y - 1; k++)
{
sum += face.data[j - 1][k] + face.data[j][k];
face.data[j][k] = face.data[j - 1][k];
if (face.data[j][k] == Kong)
{
gotoxy(j, 2 * k);
printf(" ");
}
else
{
gotoxy(j, 2 * k);
color(face.color[j][k]);
printf("■");
}
}
if (sum == 0)
return 1;
}
}
}
for (i = 1; i < FACE_Y - 1; i++)
{
if (face.data[1][i] == Box)
{
char n;
Sleep(2000); //延时
system("cls");
color(7);
do
{
gotoxy(FACE_X / 2 + 2, 2 * (FACE_Y / 3));
printf("是否重新开始游戏(y/n): ");
scanf("%c", &n);
gotoxy(FACE_X / 2 + 4, 2 * (FACE_Y / 3));
if (n != 'n' && n != 'N' && n != 'y' && n != 'Y')
printf("输入错误请重新输!");
else
break;
} while (1);
if (n == 'n' || n == 'N')
{
gotoxy(FACE_X / 2 + 4, 2 * (FACE_Y / 3));
printf("按任意键退出游戏");
exit(0);
}
else if (n == 'y' || n == 'Y')
main();
}
}
return 0;
}
输入:空
输出:表现在方块堆满一行便进行消除
效果:
4.3功能三编码及调试:判断方块是否到底
实现情况:该函数成功编译通过,通过循环结构来判断小方块是否到底,到底便不再向底部移动
int judge(int n, int space_c, int x, int y) //判断是否到底
{
int i, j;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
if (dia[n][space_c].space[i][j] == 0)
continue;
else if (face.data[x + i][y + j] == Wall || face.data[x + i][y + j] == Box)
return 0;
}
}
return 1;
}
输入:空
输出:表现在方块到底便不再移动
效果:
4.4功能三编码及调试:输出空格
实现情况:该函数成功编译通过,成成实现了在4*4矩形中将标记为0的部位为输出为空格
void kongge(int base, int space_c, int x, int y)
{
int i, j;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
gotoxy(x + i, 2 * (y + j));
if (dia[base][space_c].space[i][j] == 1)
printf(" ");
}
}
}
输入:空
输出“ “
效果:
4.5功能三编码及调试:将方块输出
实现情况:该函数成功编译通过,成成实现了在4*4矩形中将标记为1的部位为输出为矩形
void fangkuai(int base, int space_c, int x, int y)
{
int i, j;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
gotoxy(x + i, 2 * (y + j));
if (dia[base][space_c].space[i][j] == 1)
printf("■");
}
}
}
输入:空
输出;■
效果:
4.6功能三编码及调试:俄罗斯方块的基本形状
实现情况:该函数成功编译通过,通过来控制循环结构,在4*4的矩形下生成基本的7种俄罗斯方块,再通过循环结构来对这几种基本形状进行旋转,生成全部的21种方块
void init_dia()
{
int i, j, k, z;
int tmp[4][4];
for (i = 0; i < 3; i++)
dia[0][0].space[1][i] = 1;
dia[0][0].space[2][1] = 1; //土形?
for (i = 1; i < 4; i++)
dia[1][0].space[i][1] = 1;
dia[1][0].space[1][2] = 1; //L形1
for (i = 1; i < 4; i++)
dia[2][0].space[i][2] = 1;
dia[2][0].space[1][1] = 1; //L形2
for (i = 0; i < 2; i++)
{
dia[3][0].space[1][i] = 1;
dia[3][0].space[2][i + 1] = 1; //Z形1
dia[4][0].space[1][i + 1] = 1;
dia[4][0].space[2][i] = 1;//Z形2
dia[5][0].space[1][i + 1] = 1;
dia[5][0].space[2][i + 1] = 1;//田字形
}
for (i = 0; i < 4; i++)
dia[6][0].space[i][2] = 1;//1形?
//基础个形状
for (i = 0; i < 7; i++)
{
for (z = 0; z < 3; z++)
{
for (j = 0; j < 4; j++)
{
for (k = 0; k < 4; k++)
{
tmp[j][k] = dia[i][z].space[j][k];
}
}
for (j = 0; j < 4; j++)
{
for (k = 0; k < 4; k++)
{
dia[i][z + 1].space[j][k] = tmp[4 - k - 1][j];
}
}
}
}
//旋转的21个形状
}
输入:旋转(空格键)
输出:生成21种俄罗斯方块
效果:
4.7功能三编码及调试:设计出游戏界面和俄罗斯方块的移动
实现情况:该函数成功编译通过,通过循环来控制生成游戏界面,再判断来进行俄罗斯方块的移动
void inter_face()//开始界面
{
int i, j;
for (i = 0; i < FACE_X; i++)
{
for (j = 0; j < FACE_Y + 10; j++)
{
if (j == 0 || j == FACE_Y - 1 || j == FACE_Y + 9)
{
face.data[i][j] = Wall;
gotoxy(i, 2 * j);
printf("■");
}
else if (i == FACE_X - 1)
{
face.data[i][j] = Box;
gotoxy(i, 2 * j);
printf("■");
}
else
face.data[i][j] = Kong;
}
}
gotoxy(FACE_X - 18, 2 * FACE_Y + 2);
printf("左移:←");
gotoxy(FACE_X - 16, 2 * FACE_Y + 2);
printf("右移:→");
gotoxy(FACE_X - 14, 2 * FACE_Y + 2);
printf("旋转");
gotoxy(FACE_X - 12, 2 * FACE_Y + 2);
printf("暂停ª");
gotoxy(FACE_X - 10, 2 * FACE_Y + 2);
printf("退出 ESC");
gotoxy(FACE_X - 8, 2 * FACE_Y + 2);
printf("重新开始");
gotoxy(FACE_X - 4, 2 * FACE_Y + 2);
printf("分数%d", grade);
}
输入:<-/->
输出:表现生成的游戏界面和方块的移动上
效果:
4.8功能三编码及调试:移动光标
实现情况:该子函数成功编译通过,实现了通过光标移动来移动俄罗斯方块
void gotoxy(int x, int y) //移动光标
{
COORD coord;
coord.X = y;
coord.Y = x;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
输入:空
输出:表现在方块的移动上
效果:
4.9功能三编码及调试:隐藏光标
实现情况:该函数成功编译通过,且实现了光标的隐藏
void hidden_cursor()//隐藏光标
{
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cci;
GetConsoleCursorInfo(hOut, &cci);
cci.bVisible = 0;//赋1为显示,赋0为隐藏
SetConsoleCursorInfo(hOut, &cci);
}
输入:空
输出:表现在在方块移动时不出现光标
效果:
.10功能三编码及调试:给方块上色
实现情况:该子函数编译成功通过,实现了给方块上色的目标,符合预期设想,通过SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c)来更改颜色
int color(int c)
{
switch (c)
{
case 0: c = 9; break;
case 1:
case 2: c = 12; break;
case 3:
case 4: c = 14; break;
case 5: c = 10; break;
case 6: c = 13; break;
default: c = 7; break;
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //更改文字颜色¦
return 0;
}
输入:空
输出:表现在方块颜色上
效果: