【C++项目实现】俄罗斯方块

✍个人博客:https://blog.csdn.net/Newin2020?spm=1011.2415.3001.5343
📚专栏目标:C++项目集合
📣专栏定位:为刚学完C++或还在学习C++过程中的小伙伴提供练手的项目,帮助大家快速熟悉C++语法。
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪
🎏唠叨唠叨:在这个专栏里,我将会整理一些有趣的C++项目来给大家练手~

初始化页面

# include <stdio.h> 
# include <graphics.h> 

// 欢迎界面
void welcome() {
	// 初始化画布
	initgraph(550, 660);

	// 设置窗口标题
	HWND window = GetHWnd(); //获取窗口
	SetWindowText(window, _T("俄罗斯方块")); //设置窗口标题

	// 设置文本的字体样式
	setfont(40, 0, _T("微软雅黑"));	//设置文字样式(0表示自适应)
	setcolor(WHITE);
	outtextxy(205, 200, _T("俄罗斯方块"));

	setfont(22, 0, _T("楷体"));
	outtextxy(150, 300, _T("适度游戏益脑,沉迷游戏伤身"));

	Sleep(3000); //睡眠(暂停)3000毫秒,3秒针
}

初始化游戏环境

int score = 0; // 总分
int rank = 0;  //等级

// 初始化游戏场景
void initGameScene()
{
	char str[16];

	cleardevice();
	setcolor(WHITE);

	rectangle(29, 29, 334, 633);
	rectangle(27, 27, 336, 635);
	rectangle(370, 50, 515, 195);

	setfont(24, 0, _T("楷体"));
	setcolor(LIGHTGRAY);
	outtextxy(405, 215, _T("下一个:"));
	setcolor(RED);
	outtextxy(405, 280, _T("分数:"));
	sprintf(str, "%d", score);
	outtextxy(415, 310, str);
	outtextxy(405, 375, _T("等级:"));
	sprintf(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("→: 右移"));
	outtextxy(390, 600, _T("空格: 暂停"));
}

新方块

#define  BLOCK_COUNT	    5
#define  BLOCK_WIDTH		5
#define  BLOCK_HEIGHT  	    5
#define  UNIT_SIZE		    20  //小砖块的宽度和高度

int color[BLOCK_COUNT] = {
	GREEN,CYAN,MAGENTA,BROWN,YELLOW
};

int NextIndex = -1;

int block[BLOCK_COUNT * 4][BLOCK_HEIGHT][BLOCK_WIDTH] = {
	// | 形方块
	{ 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 },

	// L 形方块
	{ 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 },

	// T 形方块
	{ 0,0,0,0,0,
	0,1,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,0,1,0,
	0,0,1,1,0,
	0,0,0,1,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,0,1,0,0,
	0,1,1,1,0,
	0,0,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,1,0,0,0,
	0,1,1,0,0,
	0,1,0,0,0,
	0,0,0,0,0 },

	// Z 形方块
	{ 0,0,0,0,0,
	0,1,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,0,0,
	0,1,1,0,0,
	0,1,0,0,0,
	0,0,0,0,0 },

	{ 0,0,0,0,0,
	0,1,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,0,0,
	0,1,1,0,0,
	0,1,0,0,0,
	0,0,0,0,0 },
};

//清除右上角提示区方块
void clearBlock() {
	setcolor(BLACK);
	setfont(23, 0, "楷体");

	for (int i = 0; i < BLOCK_HEIGHT; i++) {
		for (int j = 0; j < BLOCK_WIDTH; j++) {
			//"■"
			int x = 391 + j * UNIT_SIZE;
			int y = 71 + i * UNIT_SIZE;
			outtextxy(x, y, "■");
		}
	}
}

//绘制方块
void drawBlock(int x, int y) {
	setcolor(color[NextIndex]);
	setfont(23, 0, "楷体");

	for (int i = 0; i < BLOCK_HEIGHT; i++) {
		for (int j = 0; j < BLOCK_WIDTH; j++) {
			//"■"
			if (block[NextIndex * 4][i][j] == 1) {
				int x2 = x + j * UNIT_SIZE;
				int y2 = y + i * UNIT_SIZE;
				outtextxy(x2, y2, "■");
			}
		}
	}
}

//右上角提示区显示下一个方块
void nextblock() {
	clearBlock(); //清除右上角区域

	//随机选择一种方块
	srand(time(NULL)); //使用时间函数的返回值,来作为随机种子
	NextIndex = rand() % BLOCK_COUNT;

	drawBlock(391, 71);
}

降落方块

使用访问数组来确定是否有方块

int visit[30][15], Color[30][15]; // visit[i][j] == 1 表示该位置有方块

新方块降落

int BlockIndex = -1; //当前方块的种类

//新方块降落
void newblock() {
	//确定即将使用的方块的类别
	BlockIndex = NextIndex;

	//绘制刚从顶部下降的方块
	drawBlock(START_X, START_Y);

	//让新出现的方块暂停一会,让用户识别到
	Sleep(100); //0.1秒

	//在右上角区域,绘制下一个方块
	nextblock();

	//方块降落
	move();
}

方块降落

#define  START_X			130
#define  START_Y			30

#define KEY_UP			72
#define	KEY_RIGHT		77
#define KEY_DOWN		80
#define KEY_LEFT		75
#define KEY_SPACE		32

int speed = 500;
int minX = 30;
int minY = 30;

int markColor[30][15]; //表示对应位置的颜色

typedef enum {
	BLOCK_UP,
	BLOCK_RIGHT,
	BLOCK_DOWN,
	BLOCK_LEFT
} block_dir_t;

typedef enum {
	MOVE_DOWN,
	MOVE_LEFT,
	MOVE_RIGHT
} move_dir_t;

//调整下降速度
void wait(int interval) {
	int count = interval / 10;
	for (int i = 0; i < count; i++) {
		Sleep(10);
		if (kbhit()) {
			return;
		}
	}
}

//标记已到位的方块
void mark(int x, int y, int blockIndex, block_dir_t dir) {
	int id = blockIndex * 4 + dir;
	int x2 = (y - minY) / 20;
	int y2 = (x - minX) / 20;

	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (block[id][i][j] == 1) {
				visit[x2 + i][y2 + j] = 1;
				markColor[x2 + i][y2 + j] = color[blockIndex];
			}
		}
	}
}

//如果在指定位置可以向指定方向移动,就返回1, 否则就返回0
int moveable(int x0, int y0, move_dir_t moveDir, block_dir_t blockDir) {
	//计算当前方块的左上角在30x15的游戏区中的位置(第多少行,第多少列)
	int x = (y0 - minY) / UNIT_SIZE;
	int y = (x0 - minX) / UNIT_SIZE;
	int id = BlockIndex * 4 + blockDir;
	int ret = 1;

	if (moveDir == MOVE_DOWN) {
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (block[id][i][j] == 1 &&
					(x + i + 1 >= 30 || visit[x + i + 1][y + j] == 1)) {
					ret = 0;
				}
			}
		}
	}
	else if (moveDir == MOVE_LEFT) {
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (block[id][i][j] == 1 &&
					(y + j == 0 || visit[x + i][y + j - 1] == 1)) {
					ret = 0;
				}
			}
		}

	}
	else if (moveDir == MOVE_RIGHT) {
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (block[id][i][j] == 1 &&
					(y + j + 1 >= 15 || visit[x + i][y + j + 1] == 1)) {
					ret = 0;
				}
			}
		}
	}

	return ret;
}

//判断当前方块是否可以转向到指定方向
//注意,此时还没有转到该方向!!!
int rotatable(int x, int y, block_dir_t dir) {
	int id = BlockIndex * 4 + dir;
	int xIndex = (y - minY) / 20;
	int yIndex = (x - minX) / 20;
 
	if (!moveable(x, y, MOVE_DOWN, dir)) {
		return 0;
	}

	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (block[id][i][j] == 1 &&
				(yIndex + j < 0 || yIndex + j >= 15 || visit[xIndex + i][yIndex + j] == 1)) {
				return 0;
			}
		}
	}

	return 1;
}

//方块降落
void move() {
	int x = START_X;
	int y = START_Y;
	int k = 0;
	block_dir_t  blockDir = BLOCK_UP;
	int curSpeed = speed;

	//检测游戏是否结束
	failCheck();

	//持续向下降落
	while (1) {
		if (kbhit()) {
			int key = getch();
			if (key == KEY_SPACE) {
				getch();
			}
		}

		//清除当前方块
		clearBlock(x, k, blockDir);

		if (kbhit()) {
			int key = getch();

			if (key == KEY_UP) {
				block_dir_t nextDir = (block_dir_t)((blockDir + 1) % 4);
				if (rotatable(x, y + k, nextDir)) {
					blockDir = nextDir;
				}
			}
			else if (key == KEY_DOWN) {
				curSpeed = 50;
			}
			else if (key == KEY_LEFT) {
				if (moveable(x, y + k + 20, MOVE_LEFT, blockDir)) {
					x -= 20;
				}
			}
			else if (key == KEY_RIGHT) {
				if (moveable(x, y + k + 20, MOVE_RIGHT, blockDir)) {
					x += 20;  //x = x + 20;
				}
			}
		}

		k += 20;

		//绘制当前方块
		drawBlock(x, y + k, BlockIndex, blockDir);

		wait(curSpeed);	//下降速度

		//方块的“固化”处理
		if (!moveable(x, y + k, MOVE_DOWN, blockDir)) {
			mark(x, y + k, BlockIndex, blockDir);
			break;
		}
	}
}

检测结果

//消除第x行,并把上面的行都下移
void down(int x) {
	for (int i = x; i > 0; i--) {
		//消除第i行,第j列的方格消除
		for (int j = 0; j < 15; j++) {
			if (visit[i - 1][j]) {
				visit[i][j] = 1;
				markColor[i][j] = markColor[i - 1][j];
				setcolor(markColor[i][j]);
				outtextxy(20 * j + minX, 20 * i + minY, "■");
			}
			else {
				visit[i][j] = 0;
				setcolor(BLACK);
				outtextxy(20 * j + minX, 20 * i + minY, "■");
			}
		}
	}

	//清除最顶上的哪一行(就是行标为0的那一行)
	setcolor(BLACK);
	for (int j = 0; j < 15; j++) {
		visit[0][j] = 0;
		outtextxy(20 * j + minX, minY, "■");
	}
}

//更新分数,参数lines表示消除的行数
void addScore(int lines) {
	char str[32];

	setcolor(RED);
	score += lines * 10;
	sprintf(str, "%d", score);
	outtextxy(415, 310, str);
}

//更新等级的提示
void updateGrade() {
	//假设:50分一级
	rank = score / 50;
	char str[16];
	sprintf(str, "%d", rank);
	outtextxy(425, 405, str);

	//更新速度, 等级越高,速度越快,speed越小!
	//最慢:500, 最快是100
	speed = 500 - rank * 100;
	if (speed <= 100) {
		speed = 100;
	}
}

//检查是否有行可以消除
void check() {
	int i, j;
	int clearLines = 0;

	for (i = 29; i >= 0; i--) {
		//检查第i行有没有满
		for (j = 0; j < 15 && visit[i][j]; j++);

		//执行到此处时,有两种情况:
		// 1. 第i行没有满,即表示有空位 此时 j<15
		// 2. 第i行已满了,此时 j>=15
		if (j >= 15) {
			//此时,第i行已经满了,就需要消除第i行
			down(i);  //消除第i行,并把上面的行都下移
			i++;  //因为最外层的循环中有 i--, 所以我们先i++, 使得下次循环时,再把这一行检查一下
			clearLines++;
		}
	}

	//更新分数
	addScore(clearLines);

	//更新等级(更新等级提示,更新速度)
	updateGrade();
}

全部代码

info.h

#pragma once
#include "block.h"

#define BLOCK_COUNT		5
#define BLOCK_WIDTH		5
#define BLOCK_HEIGHT	5
int block[BLOCK_COUNT * 4][BLOCK_HEIGHT][BLOCK_WIDTH] = {
	// |  型方块
	{
		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 },

	   // L 形方块
	   { 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 },

	   // T 形方块
	   { 0,0,0,0,0,
	   0,1,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,0,1,0,
	   0,0,1,1,0,
	   0,0,0,1,0,
	   0,0,0,0,0 },

	   { 0,0,0,0,0,
	   0,0,1,0,0,
	   0,1,1,1,0,
	   0,0,0,0,0,
	   0,0,0,0,0 },

	   { 0,0,0,0,0,
	   0,1,0,0,0,
	   0,1,1,0,0,
	   0,1,0,0,0,
	   0,0,0,0,0 },

	   // Z 形方块
	   { 0,0,0,0,0,
	   0,1,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,0,0,
	   0,1,1,0,0,
	   0,1,0,0,0,
	   0,0,0,0,0 },

	   { 0,0,0,0,0,
	   0,1,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,0,0,
	   0,1,1,0,0,
	   0,1,0,0,0,
	   0,0,0,0,0 }
};

block.h

#pragma once
#include <stdio.h>
#include <graphics.h>
#include <time.h>
#include <conio.h> //kbhit()使用
#include "info.h"

int score = 0; //总分
int rank = 0;  //等级

#define UNIT_SIZE       20

#define START_X			130
#define START_Y			30

#define KEY_UP			72
#define	KEY_RIGHT		77
#define KEY_DOWN		80
#define KEY_LEFT		75
#define KEY_SPACE		32

int speed = 500;
int minX = 30;
int minY = 30;

typedef enum {
	BLOCK_UP,
	BLOCK_RIGHT,
	BLOCK_DOWN,
	BLOCK_LEFT
} block_dir_t;

typedef enum {
	MOVE_DOWN,
	MOVE_LEFT,
	MOVE_RIGHT
} move_dir_t;

int NextIndex = -1;  //下一个方块的种类
int BlockIndex = -1; //当前方块的种类

int color[BLOCK_COUNT] = {
	GREEN, CYAN,  MAGENTA, BROWN, YELLOW
};

int visit[30][15]; //访问数组
int markColor[30][15]; //表示对应位置的颜色

//欢迎界面
void welcome();

//初始化游戏场景
void initGameScene();

//绘制方块
void drawBlock(int x, int y);

//绘制方块:  在指定位置绘制指定方块的指定方向
void drawBlock(int x, int y, int blockIndex, block_dir_t dir);

//清除指定位置指定方向的方块
//参数x: 方块的左上角的x坐标
//参数y: 方块的左上角在游戏区域内的坐标,距离游戏区域顶部的距离
void clearBlock(int x, int y, block_dir_t dir);

//右上角提示区显示下一个方块
void nextblock();

//如果在指定位置可以向指定方向移动,就返回1, 否则就返回0
int moveable(int x0, int y0, move_dir_t moveDir, block_dir_t blockDir);

//检测游戏是否结束
void failCheck();

//判断当前方块是否可以转向到指定方向
//注意,此时还没有转到该方向!!!
int rotatable(int x, int y, block_dir_t dir);

//调整下降速度
void wait(int interval);

//标记已到位的方块
void mark(int x, int y, int blockIndex, block_dir_t dir);

//方块降落
void move();

//新方块降落
void newblock();

//消除第x行,并把上面的行都下移
void down(int x);

//更新分数,参数lines表示消除的行数
void addScore(int lines);

//更新等级的提示
void updateGrade();

//检查是否有行可以消除
void check();

block.cpp

#include "block.h"

int main() {
	welcome();
	initGameScene();

	//产生新方块
	nextblock();
	Sleep(500);

	//初始化访问数组
	memset(visit, 0, sizeof(visit));

	while (1) {
		newblock();

		//消除满行,并更新分数和速度
		check();
	}

	system("pause");
	closegraph();
	return 0;
}

//欢迎界面
void welcome() {
	//初始化画布
	initgraph(550, 660);

	//设置窗口标题
	HWND window = GetHWnd(); //获取窗口
	SetWindowText(window, _T("俄罗斯方块")); //设置窗口标题

	//设置文本的字体样式
	setfont(40, 0, _T("微软雅黑"));
	setcolor(WHITE);
	outtextxy(205, 200, _T("俄罗斯方块"));

	setfont(22, 0, _T("楷体"));
	outtextxy(150, 300, _T("适度游戏益脑,沉迷游戏伤身"));

	Sleep(3000); //睡眠(暂停)3000毫秒,3秒针
}

//初始化游戏场景
void initGameScene() {
	char str[16];

	//清除屏幕
	cleardevice();

	rectangle(27, 27, 336, 635);
	rectangle(29, 29, 334, 633);
	rectangle(370, 50, 515, 195);

	setfont(24, 0, _T("楷体"));
	setcolor(LIGHTGRAY);
	outtextxy(405, 215, _T("下一个"));

	setcolor(RED);
	outtextxy(405, 280, _T("分数"));
	sprintf(str, "%d", score);
	outtextxy(415, 310, str);

	outtextxy(405, 375, _T("等级"));
	sprintf(str, "%d", rank);
	outtextxy(425, 405, str);

	//操作说明  ↑  ↓ ← →
	setcolor(LIGHTBLUE);
	outtextxy(390, 475, "操作说明");
	outtextxy(390, 500, "↑:旋转");
	outtextxy(390, 525, "↓: 下降");
	outtextxy(390, 550, "←: 左移");
	outtextxy(390, 575, "→: 右移");
	outtextxy(390, 600, "空格:暂停");
}

//清除右上角提示区方块
void clearBlock() {
	setcolor(BLACK);
	setfont(23, 0, "楷体");

	for (int i = 0; i < BLOCK_HEIGHT; i++) {
		for (int j = 0; j < BLOCK_WIDTH; j++) {
			//"■"
			int x = 391 + j * UNIT_SIZE;
			int y = 71 + i * UNIT_SIZE;
			outtextxy(x, y, "■");
		}
	}
}

//绘制方块
void drawBlock(int x, int y) {
	setcolor(color[NextIndex]);
	setfont(23, 0, "楷体");

	for (int i = 0; i < BLOCK_HEIGHT; i++) {
		for (int j = 0; j < BLOCK_WIDTH; j++) {
			//"■"
			if (block[NextIndex * 4][i][j] == 1) {
				int x2 = x + j * UNIT_SIZE;
				int y2 = y + i * UNIT_SIZE;
				outtextxy(x2, y2, "■");
			}
		}
	}
}

//绘制方块:  在指定位置绘制指定方块的指定方向
void drawBlock(int x, int y, int blockIndex, block_dir_t dir) {
	setcolor(color[blockIndex]);
	setfont(23, 0, "楷体");
	int id = blockIndex * 4 + dir;

	for (int i = 0; i < BLOCK_HEIGHT; i++) {
		for (int j = 0; j < BLOCK_WIDTH; j++) {
			//"■"
			if (block[id][i][j] == 1) {
				int x2 = x + j * UNIT_SIZE;
				int y2 = y + i * UNIT_SIZE;
				outtextxy(x2, y2, "■");
			}
		}
	}
}

//清除指定位置指定方向的方块
//参数x: 方块的左上角的x坐标
//参数y: 方块的左上角在游戏区域内的坐标,距离游戏区域顶部的距离
void clearBlock(int x, int y, block_dir_t dir) {
	setcolor(BLACK);
	int id = BlockIndex * 4 + dir;
	y += START_Y;

	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (block[id][i][j] == 1) {
				//擦除该方块的第i行的第j列
				outtextxy(x + 20 * j, y + i * 20, "■");
			}
		}
	}
}

//右上角提示区显示下一个方块
void nextblock() {
	clearBlock(); //清除右上角区域

	//随机选择一种方块
	srand(time(NULL)); //使用时间函数的返回值,来作为随机种子
	NextIndex = rand() % BLOCK_COUNT;

	drawBlock(391, 71);
}

//如果在指定位置可以向指定方向移动,就返回1, 否则就返回0
int moveable(int x0, int y0, move_dir_t moveDir, block_dir_t blockDir) {
	//计算当前方块的左上角在30x15的游戏区中的位置(第多少行,第多少列)
	int x = (y0 - minY) / UNIT_SIZE;
	int y = (x0 - minX) / UNIT_SIZE;
	int id = BlockIndex * 4 + blockDir;
	int ret = 1;

	if (moveDir == MOVE_DOWN) {
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (block[id][i][j] == 1 &&
					(x + i + 1 >= 30 || visit[x + i + 1][y + j] == 1)) {
					ret = 0;
				}
			}
		}
	}
	else if (moveDir == MOVE_LEFT) {
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (block[id][i][j] == 1 &&
					(y + j == 0 || visit[x + i][y + j - 1] == 1)) {
					ret = 0;
				}
			}
		}

	}
	else if (moveDir == MOVE_RIGHT) {
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (block[id][i][j] == 1 &&
					(y + j + 1 >= 15 || visit[x + i][y + j + 1] == 1)) {
					ret = 0;
				}
			}
		}
	}

	return ret;
}

//检测游戏是否结束
void failCheck() {
	if (!moveable(START_X, START_Y, MOVE_DOWN, BLOCK_UP)) {
		setcolor(WHITE);
		setfont(45, 0, "隶体");
		outtextxy(75, 300, "GAME OVER!");
		Sleep(1000);
		system("pause");
		closegraph();
		exit(0);
	}
}

//判断当前方块是否可以转向到指定方向
//注意,此时还没有转到该方向!!!
int rotatable(int x, int y, block_dir_t dir) {
	int id = BlockIndex * 4 + dir;
	int xIndex = (y - minY) / UNIT_SIZE;
	int yIndex = (x - minX) / UNIT_SIZE;

	if (!moveable(x, y, MOVE_DOWN, dir)) {
		return 0;
	}

	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (block[id][i][j] == 1 &&
				(yIndex + j < 0 || yIndex + j >= 15 || visit[xIndex + i][yIndex + j] == 1)) {
				return 0;
			}
		}
	}

	return 1;
}

//调整下降速度
void wait(int interval) {
	int count = interval / 10;
	for (int i = 0; i < count; i++) {
		Sleep(10);
		if (kbhit()) {
			return;
		}
	}
}

//标记已到位的方块
void mark(int x, int y, int blockIndex, block_dir_t dir) {
	int id = blockIndex * 4 + dir;
	int x2 = (y - minY) / 20;
	int y2 = (x - minX) / 20;

	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (block[id][i][j] == 1) {
				visit[x2 + i][y2 + j] = 1;
				markColor[x2 + i][y2 + j] = color[blockIndex];
			}
		}
	}
}

//方块降落
void move() {
	int x = START_X;
	int y = START_Y;
	int k = 0;
	block_dir_t  blockDir = BLOCK_UP;
	int curSpeed = speed;

	//检测游戏是否结束
	failCheck();

	//持续向下降落
	while (1) {
		if (kbhit()) {
			int key = getch();
			if (key == KEY_SPACE) {
				getch();
			}
		}

		//清除当前方块
		clearBlock(x, k, blockDir);

		if (kbhit()) {
			int key = getch();

			if (key == KEY_UP) {
				block_dir_t nextDir = (block_dir_t)((blockDir + 1) % 4);
				if (rotatable(x, y + k, nextDir)) {
					blockDir = nextDir;
				}
			}
			else if (key == KEY_DOWN) {
				curSpeed = 50;
			}
			else if (key == KEY_LEFT) {
				if (moveable(x, y + k + 20, MOVE_LEFT, blockDir)) {
					x -= 20;
				}
			}
			else if (key == KEY_RIGHT) {
				if (moveable(x, y + k + 20, MOVE_RIGHT, blockDir)) {
					x += 20;  //x = x + 20;
				}
			}
		}

		k += 20;

		//绘制当前方块
		drawBlock(x, y + k, BlockIndex, blockDir);

		wait(curSpeed);

		//方块的“固化”处理
		if (!moveable(x, y + k, MOVE_DOWN, blockDir)) {
			mark(x, y + k, BlockIndex, blockDir);
			break;
		}
	}
}

//新方块降落
void newblock() {
	//确定即将使用的方块的类别
	BlockIndex = NextIndex;

	//绘制刚从顶部下降的方块
	drawBlock(START_X, START_Y);

	//让新出现的方块暂停一会,让用户识别到
	Sleep(100); //0.1秒

	//在右上角区域,绘制下一个方块
	nextblock();

	//方块降落
	move();
}

//消除第x行,并把上面的行都下移
void down(int x) {
	for (int i = x; i > 0; i--) {
		//消除第i行,第j列的方格消除
		for (int j = 0; j < 15; j++) {
			if (visit[i - 1][j]) {
				visit[i][j] = 1;
				markColor[i][j] = markColor[i - 1][j];
				setcolor(markColor[i][j]);
				outtextxy(20 * j + minX, 20 * i + minY, "■");
			}
			else {
				visit[i][j] = 0;
				setcolor(BLACK);
				outtextxy(20 * j + minX, 20 * i + minY, "■");
			}
		}
	}

	//清除最顶上的哪一行(就是行标为0的那一行)
	setcolor(BLACK);
	for (int j = 0; j < 15; j++) {
		visit[0][j] = 0;
		outtextxy(20 * j + minX, minY, "■");
	}
}

//更新分数,参数lines表示消除的行数
void addScore(int lines) {
	char str[32];

	setcolor(RED);
	score += lines * 10;
	sprintf(str, "%d", score);
	outtextxy(415, 310, str);
}

//更新等级的提示
void updateGrade() {
	//假设:50分一级
	rank = score / 50;
	char str[16];
	sprintf(str, "%d", rank);
	outtextxy(425, 405, str);

	//更新速度, 等级越高,速度越快,speed越小!
	//最慢:500, 最快是100
	speed = 500 - rank * 100;
	if (speed <= 100) {
		speed = 100;
	}
}

//检查是否有行可以消除
void check() {
	int i, j;
	int clearLines = 0;

	for (i = 29; i >= 0; i--) {
		//检查第i行有没有满
		for (j = 0; j < 15 && visit[i][j]; j++);

		//执行到此处时,有两种情况:
		// 1. 第i行没有满,即表示有空位 此时 j<15
		// 2. 第i行已满了,此时 j>=15
		if (j >= 15) {
			//此时,第i行已经满了,就需要消除第i行
			down(i);  //消除第i行,并把上面的行都下移
			i++;  //因为最外层的循环中有 i--, 所以我们先i++, 使得下次循环时,再把这一行检查一下
			clearLines++;
		}
	}

	//更新分数
	addScore(clearLines);

	//更新等级(更新等级提示,更新速度)
	updateGrade();
}
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值