c语言——俄罗斯方块

这篇具有很好参考价值的文章主要介绍了c语言——俄罗斯方块。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、游戏效果


c语言俄罗斯方块,c语言小游戏,c语言,开发语言,c++,linux


二. 游戏背景

俄罗斯方块是久负盛名的游戏,它也和贪吃蛇,扫雷等游戏位列经典游戏的⾏列。

《俄罗斯方块》(Tetris,俄文:Тетрис)是一款由俄罗斯人阿列克谢·帕基特诺夫于1984年6月发明的休闲游戏。

该游戏曾经被多家公司代理过。经过多轮诉讼后,该游戏的代理权最终被任天堂获得。 任天堂对于俄罗斯方块来说意义重大,因为将它与GB搭配在一起后,获得了巨大的成功。 

《俄罗斯方块》的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。

三、游戏开发日志

c语言俄罗斯方块,c语言小游戏,c语言,开发语言,c++,linux

基本逻辑结构与贪吃蛇一致 

四、游戏实现

我们有了贪吃蛇的知识储备及了解WIN32 API,理解下面内容也就不难了。

4.1  游戏逻辑主体

#include"tetris.h"

int main()
{
	//修改当前地区为本地模式,为了⽀持中⽂宽字符的打印
	setlocale(LC_ALL, "");
	srand((unsigned int)time(NULL));
	char ch=0;
	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 cols=60 lines=30");
	//设置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是0,y从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);

	

}

c语言俄罗斯方块,c语言小游戏,c语言,开发语言,c++,linux

注解: 

1)一个宽字符占2个字节,我们设置 cols=60 lines=30,实际就是长宽各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;//初始化方块状态
		}
	}
	
}

c语言俄罗斯方块,c语言小游戏,c语言,开发语言,c++,linux

注解: 

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种基本形状方块,然后基本形状方块通过顺时针旋转依次得到各自剩下的三种形态。

c语言俄罗斯方块,c语言小游戏,c语言,开发语言,c++,linux

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(x+2*n,y+m); //光标跳转到指定位置
				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[x+n*2][y+m] = 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,当sign=0时,表示全部方块没有满行了

4.4.5   判断游戏失败

void JudeIsOver()
{
	for (int i = 2; i < 38; i+=2)
	{
		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].speed>50)//避免连续按,变成负数,会出现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].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;
	}
	
}

注:

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)对游戏的三个状态分别进行描述。文章来源地址https://www.toymoban.com/news/detail-754845.html

五、完整代码

5.1   tetris.h

#include<Windows.h>
#include <locale.h>
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.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

#include"tetris.h"

//设置光标的坐标
void SetPos(short x, short y)
{
	COORD pos = { x, y };
	HANDLE hOutput = NULL;
	//获取标准输出的句柄(⽤来标识不同设备的数值)
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	//设置标准输出上光标的位置为pos
	SetConsoleCursorPosition(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是0,y从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 cols=60 lines=30");
	//设置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[x+n*2][y+m] = 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(x+2*n,y+m); //光标跳转到指定位置
				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; i+=2)
	{
		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].speed>50)//避免连续按,变成负数,会出现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].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;
	}
	
}

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

#include"tetris.h"

int main()
{
	//修改当前地区为本地模式,为了⽀持中⽂宽字符的打印
	setlocale(LC_ALL, "");
	srand((unsigned int)time(NULL));
	char ch=0;
	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;
}

到了这里,关于c语言——俄罗斯方块的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【用EXCEL编写俄罗斯方块小游戏(基于VBA)】

    工作属性原因,工作中使用excel办公是常态。前一阵子因工作业务需求,需要用到VBA。研究了一阵子VBA,解决了当时的需求。 后来想想,VBA可以如此彻底的控制excel,那么可不可以编个小游戏呢。 说干就干,先拿与表格最像的俄罗斯方块试试手。 首先,俄罗斯方块游戏需要完

    2023年04月08日
    浏览(24)
  • Python课程设计 基于python的俄罗斯方块小游戏

    目录 基于python的俄罗斯方块小游戏 1.概述 1.1 摘要 1.2 开发背景 1.3 开发环境 1.4 实现功能 2.代码描述 2.1 模块导入 2.2 初始化变量 2.3 播放音乐 2.4 创建方块类 2.5 绘制游戏地图 2.6 游戏初始化 2.7 绘制有边框矩形 2.8 绘制我的文字 2.9 游戏主体 2.10 主程序运行 3.运行效果 4.注

    2024年02月22日
    浏览(42)
  • Java实现俄罗斯方块小游戏。(附完整源代码)

    大家好,我是百思不得小赵。 创作时间:2022 年 5 月 12 日 博客主页: 🔍点此进入博客主页 —— 新时代的农民工 🙊 —— 换一种思维逻辑去看待这个世界 👀 今天是加入CSDN的第1167天。觉得有帮助麻烦👏点赞、🍀评论、❤️收藏 目录 一、游戏背景 二、功能实现 三、效果

    2024年02月03日
    浏览(45)
  • 基于Python+Pygame实现一个俄罗斯方块小游戏【完整代码】

    俄罗斯方块,一款起源于上世纪80年代的经典电子游戏,凭借简单的规则和独特的魅力,一跃成为全球家喻户晓的经典。你知道其实只需要一些基础的编程知识,就可以自己实现它吗?今天,我们将使用Python的Pygame库,一步步带你构建属于自己的俄罗斯方块小游戏! 游戏初始

    2024年02月04日
    浏览(31)
  • python毕设分享 俄罗斯方块小游戏设计与实现 (源码)

    🔥 Hi,各位同学好呀,这里是L学长! 🥇今天向大家分享一个今年(2022)最新完成的毕业设计项目作品 python小游戏毕设 俄罗斯方块小游戏设计与实现 (源码) 🥇 学长根据实现的难度和等级对项目进行评分(最低0分,满分5分) 难度系数:3分 工作量:3分 创新点:4分 项目获取:

    2024年02月03日
    浏览(40)
  • 基于STM32F407的俄罗斯方块小游戏的设计

        本文讲述的是基于STM32F407的俄罗斯方块小游戏的设计思路和测试结果,具体的代码分析见文章 基于STM32F407的俄罗斯方块游戏代码分析_钻仰弥坚的博客-CSDN博客 1.1 可行性分析 可行性分析能够对新系统进行各方面的分析与研究,确定新系统是否具有开发的可行性和必要性

    2024年02月11日
    浏览(33)
  • 基于STM32 LCD屏实现的俄罗斯方块小游戏(20220522完成 第一个综合类项目)

    本项目基于 正点原子mini(stm32f103RCT6) 2.8 寸的 ALIENTEK TFTLCD 模块 二轴摇杆模块 的俄罗斯方块小游戏。(学习过程中的项目自测) 1.随机数生成我是用RTC的时钟自己写的,用srand+rand 只能生成一次。 2.并行程序(有什么更好的方法)。 3.觉得我哪里需要改进或者有什么见解可以评论

    2024年02月08日
    浏览(49)
  • 录有手就行1、吃金币2、打乒乓3、滑雪4、并夕夕版飞机大战5、打地鼠简简单单6、小恐龙7、消消乐8、俄罗斯方块9、贪吃蛇普普通通10、24点小游戏

    1、吃金币 【有手就行系列不介绍玩法了+附源码】 源码分享:   import os import cfg import sys import pygame import random from modules import *     \\\'\\\'\\\'游戏初始化\\\'\\\'\\\' def initGame():     # 初始化pygame, 设置展示窗口     pygame.init()     screen = pygame.display.set_mode(cfg.SCREENSIZE)     pygame.display.set_capt

    2024年03月21日
    浏览(35)
  • 俄罗斯方块游戏(C语言)

    简介:俄罗斯方块(Tetris)是一款经典的游戏,下面是用C语言实现俄罗斯方块的示例代码: code 这是一个非常简单的俄罗斯方块游戏,只有基本的方块形状和控制操作。如果想要更加完整的游戏体验,可以添加更多的方块形状、音效、背景音乐、计分系统等等。 分析 这份代

    2024年02月07日
    浏览(30)
  • pygame俄罗斯方块游戏

    1.安装python 2.引入游戏库pygame 3.引入随机数 俄罗斯方块初始形状 这里使用一个二维数组 用来标记俄罗斯相对应的方块形状 代码如下: 游戏移动方向是否可能判断 这里为了不让他出现穿墙,跨过方块下落 都做对应的碰撞判断 具体代码如下: 俄罗斯方块旋转变形代码实现 俄

    2024年02月08日
    浏览(24)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包