1。需求分析:
背景:黑色小方块
界面:分数,等级等一般用全局变量实现
随机图形:首先用数组实现九个图形的四种状态,然后用随机数rand实现随机生成图形(常用方法)
随机图形的移动和翻转:首先要判断能不能,然后进行变动
消消乐:判断条件,下移一行
结束界面:根据自身实力设计即可
2.代码实现:
#include <graphics.h>
# include <stdio.h>
# include <time.h>
# include <conio.h>
# include <graphics.h>
enum DIR {
UP,
DOWN,
LEFT,
RIGHT
};
int a, visit[30][15], Color[30][15], minX = 30, minY = 30, maxX = 310, maxY = 610, speed = 500, score = 0, rank = 0;
int color[] = { GREEN,CYAN,MAGENTA,
BROWN,LIGHTGRAY,LIGHTBLUE,LIGHTGREEN,
LIGHTRED,YELLOW };
/*设计了9种方块,每种方块都有4种形态,每顺时针旋转90°就有一种形态,所以一共是36个方块。
每个方块都用5*5的二维数组来表示,
36种就是36个二维数组,所以用block[36][5][5]来表示
block[k][5][5],(k<36&&k%4==0)表示每种方块的初始形态,
k+1,+2,+3,分别表示第k种方块选择90°、180°、270°之后的形态。*/
int block[36][5][5] =
{
{0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0},
{0,0,1,1,0,0,0,1,0,0,0,0,1,1,0,0,0,1,0,0,0,0,1,1,0},
{0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,1,0,1,0,0,0,0,0},
{0,1,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,1,0,0,0,1,1,0,0},
{0,0,0,0,0,1,0,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0},
{0,1,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0}
};
void Interface();//界面的设计
void nextblock();//生成下一个方块
void newblock();//将下一个放到主方框中
void move(int x, int y, int A);//移动方块
bool Canmove(int x0, int y0, int A, int dir, int next);//判断能否移动
bool Canrotate(int x0, int y0, int A, int next);//判断能否旋转
void mark(int x0, int y0, int A, int next);//标记方块固定后的位置
void full();//判断有没有能够消行的
void down(int x);//有消行的将上面的方块下移一行
int gameover();//判断游戏是否结束
int main() {
Interface();
nextblock();
Sleep(500);
memset(visit, 0, sizeof(visit));//初始化为全部未占有
while (1)
{
newblock();
full();
if (gameover())
break;
}
_getch();
closegraph();
return 0;
}
void Interface() {
char str[20];
initgraph(550, 660);
// 获得窗口句柄
HWND hwnd = GetHWnd();
// 使用 Windows API 修改窗口名称
SetWindowText(hwnd, _T("NEW俄罗斯方块 制作:hhh"));
Sleep(2000);
//游戏进入界面
setfont(40, 0, "隶体");
setcolor(RED);
outtextxy(145, 200, "NEW");
setfont(40, 0, "楷体");
for ( int i = 0; i <= 255; i++)
for (int j = 0; j <= 255; j++)
{
setcolor(RGB((i + j + 128) % 255, i, j));
outtextxy(205, 200, "俄罗斯方块!");
}
Sleep(2000);
setcolor(LIGHTMAGENTA);
Sleep(2500);
outtextxy(130, 300,_T( "请按任意键开始游戏!"));
if (_getch()) {
cleardevice();
//游戏界面
setcolor(WHITE);
rectangle(29, 29, 334, 633);//主界面
rectangle(28, 28, 335, 634);//主界面的框
//下一个图形的预言界面,同时显示分数和等级
rectangle(370, 50, 515, 195);
setfont(24, 0, "楷体");
setcolor(LIGHTGRAY);
outtextxy(405, 215, "下一个:");
setcolor(RED);
outtextxy(405, 280, "分数:");
sprintf_s(str, "%d", score);
outtextxy(415, 310, str);
outtextxy(405, 375, "等级:");
sprintf_s(str, "%d", rank);
outtextxy(425, 405, str);
//操作提示
setfont(22, 0, _T("楷体"));
setcolor(LIGHTBLUE);
outtextxy(390, 475, _T("操作说明:"));
outtextxy(390, 500, _T("↑: 旋转"));
outtextxy(390, 525, _T("↓: 下降"));
outtextxy(390, 550, _T("←: 左移"));
outtextxy(390, 575, _T("→: 右移"));
}
}
void nextblock() {
int x=391, y=71;
//设置黑色背景
setfont(23, 0, "楷体");
setcolor(BLACK);
for(int i=0;i<5;i++)
for(int j=0;j<5;j++)
outtextxy(x + j * 20, y + i * 20, "■");
//随机的一个新图形
srand(time(NULL));
a = rand() % 8;//9种图形
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
if (block[4 * a][i][j])//对应该图形的第一种状态
{
setcolor(color[a]);
outtextxy(x + j * 20, y + i * 20, "■");
}
}
void newblock() {
int x = 130, y = 30, A = a;
//让每一次开始下降都是露出图形的最后一行
if (A == 0 || A == 1 || A == 6)
y -= 60;
else if (A == 2 || A == 3 || A == 5 || A == 7 || A == 8)
y -= 40;
else
y -= 80;
setfont(23, 0, "楷体");
setcolor(color[A]);
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (block[4 * A][i][j]&&y+i*20>=30)
outtextxy(x + j * 20, y + i * 20, "■");
}
}
Sleep(100);
nextblock();
move(x,y,A);
}
void move(int x,int y,int A) {
int k = 0, next = 0, Speed=speed, key, nex;
while (1) {
if (!Canmove(x,y+k,A,DOWN,next)) {
mark(x, y+k, A, next);
break;
}
//背景设置
setcolor(BLACK);
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
if (block[4 * A + next][i][j] == 1 && y + 20 * i + k >= 30)
outtextxy(x + 20 * j, y + 20 * i + k, "■");
if(_kbhit())
switch (_getch()) {
case 'w':
case 'W':
//case '72':
nex = (next + 1) % 4;
if (Canrotate(x,y+k,A,nex))
next = nex;
break;
case 's':
case 'S':
//case '80':
Speed = 50;
break;
case 'a':
case 'A':
//case '75':
if (Canmove(x, y + k + 20, A, LEFT, next))
x -= 20;
break;
case 'd':
case 'D':
//case '77':
if (Canmove(x, y + k + 20, A, RIGHT, next))
x += 20;
break;
case '32':
break;
}
setcolor(color[A]);
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
if (block[4 * A + next][i][j] == 1 && y + 20 * i + k + 20 >= 30)
outtextxy(x + 20 * j, y + 20 * i + k + 20, "■");
Sleep(Speed);
//实现每个循环下降一格
k += 20;
}
}
bool Canmove(int x0,int y0,int A,int dir,int next) {
bool can = true;
int x = (y0 - minY) / 20;
int y = (x0 - minX) / 20;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
switch (dir) {
case DOWN:
if (block[4 * A + next][i][j] == 1 && (x + i + 1 == 30 || (x + i + 1 >= 0 && x + i + 1 < 30 && y + j >= 0 && y + j < 15 && visit[x + i + 1][y + j])))
can = false;
break;
case LEFT:
if (block[4 * A + next][i][j] == 1 && (y+j == 0 || (x + i >= 0 && x + i < 30 && y + j -1>= 0 && y + j -1< 15 && visit[x + i ][y + j-1])))
can = false;
break;
case RIGHT:
if (block[4 * A + next][i][j] == 1 && (y+j+1==15 || (x + i >= 0 && x + i < 30 && y + j >= 0 && y + j -1< 15 && visit[x + i + 1][y + j+1] )))
can = false;
break;
}
}
}
return can;
}
bool Canrotate(int x0,int y0,int A,int next) {
int x, y;
x = (y0 - minY) / 20;
y = (x0 - minX) / 20;
bool can = true;
if (!Canmove(x0, y0, A, DOWN, next))
can = false;
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
if (block[4 * A + next][i][j] && (y + j == -1 || y + j == 15 || (x + i >= 0 && x + i < 30 && y + j >= 0 && y + j < 15 && visit[x + i][y + j] == 1)))
can = false;
return can;
}
void mark(int x0,int y0,int A,int next) {
int i, j, x, y;
x = (y0 - minY) / 20;
y = (x0 - minX) / 20;
for (i = 0; i < 5; i++)
for (j = 0; j < 5; j++)
if (block[4 * A + next][i][j] == 1)
{
visit[x + i][y + j] = 1;
Color[x + i][y + j] = color[A];
}
}
void full() {
int i, j,k=0;
char str[20];
for (i = 29; i >= 0; i--)
{
for (j = 0; j < 15; j++)
{
if (!visit[i][j])
break;
}
if (j == 15) {
k++;
down(i);
i++;
}
setcolor(RED);
score += 10 * k;
sprintf_s(str, "%d", score);
outtextxy(415, 310, str);
rank = (score / 50);
sprintf_s(str, "%d", rank);
outtextxy(425, 405, str);
speed = 500 - 50 * rank;
if (speed == 0)
speed = 100;
if (score >= 300)
speed = 75;
}
}void down(int x) {
for (int i = x; i >= 0; i--) {
for (int j = 0; j < 15; j++) {
if (visit[i - 1][j]) {
visit[i][j] = visit[i - 1][j];
Color[i - 1][j] = Color[i - 1][j];
setcolor(Color[i - 1][j]);
outtextxy(minX + 20 * j, minY + 20 * i, "■");
}
else {//背景
visit[i][j] = visit[i - 1][j];
setcolor(BLACK);
outtextxy(minX + 20 * j, minY + 20 * i, "■");
}
}//背景
setcolor(BLACK);
for (int j = 0; j < 15; j++)
{
visit[0][j] = 0;
outtextxy(minX + 20 * j, minY, "■");
}
}
}
int gameover()
{
int i, j;
for (i = 0; i < 15; i++)
if (visit[0][i] == 1)
{
for (i = 0; i <= 7; i++)
{
//循环式
setcolor(RGB(35 * i, 255 - 35 * i, 255));
for (j = i; j < 30 - i; j++, Sleep(20))
outtextxy(30 + 20 * i, 30 + 20 * j, "■");
for (j = i; j < 15 - i; j++, Sleep(20))
outtextxy(30 + 20 * j, 30 + 20 * (29 - i), "■");
for (j = 30 - 1 - i; j >= i; j--, Sleep(20))
outtextxy(30 + 20 * (14 - i), 30 + 20 * j, "■");
for (j = 15 - 1 - i; j >= i; j--, Sleep(20))
outtextxy(30 + 20 * j, 30 + 20 * i, "■");
}
setcolor(WHITE);
setfont(45, 0, "隶体");
outtextxy(75, 300, "GAME OVER!");
Sleep(5000);
return 1;
}
return 0;
}
3.总结:
好习惯
1.宏定义 常量
2.enum实现DIR方向与int类型转换
注意事项:
1.vs中使用_getch(),_kbhit()
详看这篇(21条消息) 新手开始做项目问题1_hhhhhhhh_hhhhhhh的博客-CSDN博客
2.我的方向键上下左右无法实现
因此采用了wasd键