俄罗斯方块项目-详细过程

目录

前言

一、项目源代码

1.1 开发环境

1.2 俄罗斯方块源码

二、打印静态背景边框

2.1 初始化静态背景边框

2.2 打印静态背景边框

三、顶部显示动态图形

3.1 画一个动态图形

3.2 打印动态图形

四、向下移动、添加静态背景图、产生新的图形

4.1 线下移动

4.2 动态图形添加到静态背景图中

4.3 产生新的动态图形

五、添加按键处理、控制左右下移动

5.1 添加按键处理、控制左右下移动

5.2 随机产生不同图形

六、消行、增加分数和难度

6.1 消行、增加分数

6.2 增加游戏难度水平

七、变化图形和游戏结束判断

7.1 图形变形功能

7.2 游戏结束判断功能

八、项目过程的问题

8.1 问题一:坐标问题

8.2 问题二:碰撞判断问题

总结


前言

例如:本篇文章将记录俄罗斯方块的详细制作过程及所遇到的问题、解决方法。步骤分为:1、打印静态背景边框;2、显示动态图形、控制光标位置及隐藏光标;3、将到第的动态图形添加到静态背景图中,并且画新的动态图形;4、添加键盘按键,控制动态图形向左向右、向下移动;5、消行、增加分数及游戏难度水平控制功能;6、动态功能变换及游戏结束判断功能。


提示:以下是本篇文章正文内容,下面案例可供参考

一、项目源代码

1.1 开发环境

Visual Studio 2012 开发语言是C语言

1.2 俄罗斯方块源码

#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>

struct coord 
{
	int x;//x坐标
	int y;//y坐标
};

void initMap();//初始化静态背景边框
void showStaticMap();//打印静态背景图
void gotoxy(int x,int y);//控制光标的位置
void hideCursor();//隐藏光标
void drawDynamicShape();//画动态图形
void showDynamicShape();//打印动态图形
void moveDown();//向下移动
void addStaticMap();//添加到静态背景图中
void moveLeft();//向左移动
void moveRight();//向右移动
void clearFullLine();//清除满行
int isFullOneLine(int line);//判断line行是不是满行
void clearOneLine(int line);//清除当前的line满行
void addLevel();//增加游戏难度水平
void changeShape();//变形
int isCrash(int n1,int x1,int y1,int n2, int x2,int y2,int n3, int x3,int y3);
int isGameOver();//判断游戏是否结束
//全局变量
int score = 0;//游戏分数
int level = 1;//游戏难度水平
int speed = 20;//用来控制下落速度
int currentShape;//用来保存当前图形的编号
int map[21][12];//保存静态背景图
struct coord shape[4];//保存了图形的四个坐标点

int main()
{
	int i;
	char input;//用来保存输入的按键
	srand(time(NULL));//初始化随机种子
	initMap();//初始化边框
	drawDynamicShape();//画动态图形,对四个坐标点赋值
	while(1)
	{
		if(isGameOver())
		{
			return;//将main函数结束,整个程序结束
		}
		showStaticMap();//打印静态图
		showDynamicShape();//打印动态图形
		moveDown();//向下移动 
		for(i = 0; i < speed; i++)
		{
			if(_kbhit())//检测是否有按键产生
			{
				input = _getch();
				if(input == 'a')
				{
					/*printf("向左移动");*/
					moveLeft();
					input = 0;
				}
				else if(input == 'd')
				{
					/*printf("向右移动");*/
					moveRight();
					input = 0;
				}
				else if(input == 'w')
				{
					/*printf("变形");*/
					changeShape();
					input = 0;
				}
				else if(input == 's')
				{
					/*printf("向下移动");*/
					moveDown();
					input = 0;
				}
			}
			Sleep(50);
		}
	}
	getchar();
	return 0;
}

//判断游戏是否结束,结束返回1,不结束返回0
int isGameOver()
{
	int j;
	if(isFullOneLine(0))//如果第0行,是满行,清空
	{
		for(j = 1; j <= 10; j++)
		{
			map[0][j] = 0;
		}
	}
	for(j = 1; j <= 10; j++)
	{
		if(map[0][j] != 0)//说明出现2,有障碍
			return 1;//游戏结束
	}
	return 0;//游戏未结束
}


//碰撞检测函数
//int center //代表的是中心点,在结构体数组中的下标
//三对x y,代表的除了中心点以外,其他点的变化值
//如果碰撞返回值是1,不碰撞返回值是0
int isCrash(int n1,int x1,int y1,int n2, int x2,int y2,int n3, int x3,int y3)
{
	//(0,-1) (0,1) (0,-2)
	if(map[shape[n1].y+y1][shape[n1].x+x1] != 0 || map[shape[n2].y+y2][shape[n2].x+x2] || map[shape[n3].y+y3][shape[n3].x+x3])
	{
		return 1;
	}
	return 0;
}
//变形函数
void changeShape()
{
	//在变形之前,一定要判断当前是什么图形
	switch(currentShape)//先判断当前图形
	{
	case 1://横条
		if(isCrash(0,0,-1,2,0,1,3,0,2))//发生碰撞
			return;
		shape[0].x = shape[1].x;
		shape[0].y = shape[1].y-1;
		shape[2].x = shape[1].x;
		shape[2].y = shape[1].y+1;
		shape[3].x = shape[1].x;
		shape[3].y = shape[1].y+2;
		currentShape = 2;//改为2,因为变形后,此时的图形是竖条
		break;
	case 2://竖条
		if(isCrash(0,-1,0,2,1,0,3,2,0))//如果碰撞,函数结束
			return;
		shape[0].x = shape[1].x-1;
		shape[0].y = shape[1].y;
		shape[2].x = shape[1].x+1;
		shape[2].y = shape[1].y;
		shape[3].x = shape[1].x+2;
		shape[3].y = shape[1].y;
		currentShape = 1;//改为1,因为变形后,此时的图形是横条
		break;
	}
}
//增加游戏难度水平的函数
void addLevel()
{
	if(score >= 500 && level == 1)
	{
		speed -= 5;
		level = 2;
	}
	else if(score >= 2000 && level == 2)
	{
		speed -= 5;
		level = 3;
	}
	else if(score >= 5000 && level == 3)
	{
		speed -= 5;
		level = 4;
	}
}
//判断第line行是不是满行,是满行返回1,不是返回0
int isFullOneLine(int line)
{
	int j;
	for(j = 1; j <= 10; j++)
	{
		if(map[line][j] == 0)//说明line不是满行
			return 0;//0代表不是满行
	}
	//程序如果能执行到这,说明没有执行过return 0
	return 1;//代表是满行
}
//清除单行满行
void clearOneLine(int line)
{
	int i,j;
	for(i = line; i > 0; i--)
	{
		for(j = 1; j <= 10; j++)
		{
			map[i][j] = map[i-1][j];
		}
	}
}
//消除满行
void clearFullLine()
{
	int i;
	for(i = 19; i > 0; i--)
	{
		//1.判断每一行i是不是满行
		if(isFullOneLine(i))//返回值是1,说明是满行 1代表真
		{
			//2.消除当前的满行i
			clearOneLine(i);
			score += 100;
			//3.增加游戏难度水平
			addLevel();
			i++;//避免出现连续的两个满行有bug
		}
		
	}
}
//向左移动 
void moveLeft()
{
	int i;
	//先要进行条件判断,判断是否能够向左移动
	for(i = 0; i < 4; i++)
	{
		if(map[shape[i].y][shape[i].x-1] != 0)//碰到障碍物或边框
		{
			return;//结束函数
		}
	}
	//向左移动
	for(i = 0; i < 4; i++)
	{
		shape[i].x--;
	}
}
//向右移动
void moveRight()
{
	int i;
	//先要进行条件判断,判断是否能够向右移动
	for(i = 0; i < 4; i++)
	{
		if(map[shape[i].y][shape[i].x+1] != 0)//碰到障碍物或边框
		{
			return;//结束函数
		}
	}
	//向右移动
	for(i = 0; i < 4; i++)
	{
		shape[i].x++;
	}
}
//将动态图形添加到静态背景图中
void addStaticMap()
{
	int i;
	for(i = 0; i < 4; i++)
	{
		map[shape[i].y][shape[i].x] = 2;
	}
}
//向下移动
void moveDown()
{
	int i;
	//先判断能否向下移动
	for(i = 0; i < 4; i++)
	{//把四个坐标点,当做二维数组的行和列的下标
	 //x --->列
     //y --->行
		if(map[shape[i].y+1][shape[i].x] != 0)//y+1判断下一个位置是不是碰到障碍
		{
			//将动态图形添加到静态背景图中
			addStaticMap();
			//进行消除满行的操作
			clearFullLine();
			//再次生成一个新的图形,再画一个图形
			drawDynamicShape();
			return;//函数结束,说明落到了最底下
		}
	}
	//实现让图形向下移动一个位置
	for(i = 0; i < 4; i++)
	{
		shape[i].y++;
	}
}
//打印动态图形
void showDynamicShape()
{
	int i;
	for(i = 0; i < 4; i++)
	{
		gotoxy(shape[i].x*2,shape[i].y);
		printf("■");
	}
}
//画动态图形
void drawDynamicShape()
{
	currentShape = rand()%3;//生成一个随机图形,%几,取决于你的图形库的个数
	switch(currentShape)
	{
	case 0:
		shape[0].x = 5;//田字格
		shape[0].y = 0;
		shape[1].x = 6;
		shape[1].y = 0;
		shape[2].x = 5;
		shape[2].y = 1;
		shape[3].x = 6;
		shape[3].y = 1;
		break;
	case 1:
		shape[0].x = 4;//横条
		shape[0].y = 0;
		shape[1].x = 5;
		shape[1].y = 0;
		shape[2].x = 6;
		shape[2].y = 0;
		shape[3].x = 7;
		shape[3].y = 0;
		break;
	case 2:
		shape[0].x = 5;//竖条
		shape[0].y = 0;
		shape[1].x = 5;
		shape[1].y = 1;
		shape[2].x = 5;
		shape[2].y = 2;
		shape[3].x = 5;
		shape[3].y = 3;
		break;
	}
}
//初始化静态背景边框
void initMap()
{
	int i,j;
	hideCursor();//隐藏光标
	for(i = 0; i < 21; i++)
	{
		for(j = 0; j < 12; j++)
		{
			if(j == 0 || j == 11 || i == 20)
				map[i][j] = 1;
			else
				map[i][j] = 0;
		}
	}
}
//打印静态背景图
void showStaticMap()
{
	int i,j;
	gotoxy(0,0);//每次打印边框前,将光标移动到坐标原点
	for(i = 0; i < 21; i++)
	{
		for(j = 0; j < 12; j++)
		{
			if(map[i][j] == 0) //打印空格
			{
				printf("  ");//两个空格,是因一个星星的宽度占两个位置
			}
			else if(map[i][j] == 1)//打印边框
			{
				printf("★");
			}
			else if(map[i][j] == 2)//打印图形
			{
				printf("■");
			}
		}
		printf("\n");
	}
	printf("score:%d         level:%d\n",score,level);
}
//控制光标位置
void gotoxy(int x,int y)
{   //让printf,回到x, y点,x, y是坐标,如果打印位置到原点,x 0,y 0,,gotoxy(0, 0);
	int z=0x0b; 
	HANDLE  hOutput; 
	COORD loc; 
	loc.X = x; loc.Y=y;  
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE); 
	SetConsoleCursorPosition(hOutput, loc); 
}
//隐藏光标
void hideCursor()
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = 0; //隐藏控制台光标
	SetConsoleCursorInfo(handle, &CursorInfo);//设置控制台光标状态
}

二、打印静态背景边框

2.1 初始化静态背景边框

定义一个全局的二维数组map[21][12] ,两层for循环嵌套将第0列、第11列和第20行赋值为1,其余初始化为0

2.2 打印静态背景边框

将数组map[21][12]中为0的 printf("  ") 两个空格,为1的打印一个星星"★"  分别将初始化背景边框与显示背景边框封装成一个函数,并且在主函数main中调用

#include <stdio.h>
void initMap();//初始化静态背景边框
void showStaticMap();//打印静态背景图
//全局变量
int map[21][12];//保存静态背景图

int main()
{
	initMap();
	showStaticMap();
	getchar();
	return 0;
}
//初始化静态背景边框
void initMap()
{
	int i,j;
	for(i = 0; i < 21; i++)
	{
		for(j = 0; j < 12; j++)
		{
			if(j == 0 || j == 11 || i == 20)
				map[i][j] = 1;
			else
				map[i][j] = 0;
		}
	}
}
//打印静态背景图
void showStaticMap()
{
	int i,j;
	for(i = 0; i < 21; i++)
	{
		for(j = 0; j < 12; j++)
		{
			if(map[i][j] == 0)
			{
				printf("  ");//两个空格,是因一个星星的宽度占两个位置
			}
			else if(map[i][j] == 1)
			{
				printf("★");
			}
		}
		printf("\n");
	}
}

三、顶部显示动态图形

3.1 画一个动态图形

全局定义一个结构体,用来保存x坐标、y坐标,并且定义一个结构体数组,用来保存动态图形的4个坐标点,根据数组map[21][12] 在设置动态图形的位置

3.2 打印动态图形

添加windows.h 头文件,调用gotoxy控制光标位置、hideCursor隐藏光标,使用for循环,每次依次移动4个坐标点

#include <stdio.h>
#include <windows.h>

struct coord 
{
	int x;//x坐标
	int y;//y坐标
};

void initMap();//初始化静态背景边框
void showStaticMap();//打印静态背景图
void gotoxy(int x,int y);//控制光标的位置
void hideCursor();//隐藏光标
void drawDynamicShape();//画动态图形
void showDynamicShape();//打印动态图形

//全局变量
int map[21][12];//保存静态背景图
struct coord shape[4];//保存了图形的四个坐标点

int main()
{
	initMap();//初始化边框
	drawDynamicShape();//画动态推行
	showStaticMap();//打印静态图
	showDynamicShape();//打印动态图形
	/*
	gotoxy(8,0);//光标移动到10,10
	printf("■");
	gotoxy(10,0);
	printf("■");
	gotoxy(12,0);
	printf("■");
	gotoxy(14,0);
	printf("■");
	*/
	getchar();
	return 0;
}
//打印动态图形
void showDynamicShape()
{
	int i;
	for(i = 0; i < 4; i++)
	{
		gotoxy(shape[i].x*2,shape[i].y);
		printf("■");
	}
}
//画动态图形
void drawDynamicShape()
{
	shape[0].x = 4;
	shape[0].y = 0;
	shape[1].x = 5;
	shape[1].y = 0;
	shape[2].x = 6;
	shape[2].y = 0;
	shape[3].x = 7;
	shape[3].y = 0;
}
//初始化静态背景边框
//打印静态背景图
//控制光标位置
void gotoxy(int x,int y)
{   //让printf,回到x, y点,x, y是坐标,如果打印位置到原点,x 0,y 0,,gotoxy(0, 0);
	int z=0x0b; 
	HANDLE  hOutput; 
	COORD loc; 
	loc.X = x; loc.Y=y;  
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE); 
	SetConsoleCursorPosition(hOutput, loc); 
}
//隐藏光标
void hideCursor()
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = 0; //隐藏控制台光标
	SetConsoleCursorInfo(handle, &CursorInfo);//设置控制台光标状态
}

四、向下移动、添加静态背景图、产生新的图形

4.1 线下移动

因为map[21][12] 中21为行数、12为列数,所以人行数变化就何以了

4.2 动态图形添加到静态背景图中

在向下移动中,要先判断下一行的是否为0,如果不为0则不再向下移动,并且把结构体的4个点赋值为2,在打印静态背景图时将为2的图形显示出来

4.3 产生新的动态图形

在向下移动图形时,如果判断下一行不为0时,将调用打印动态图形的函数进行调用

#include <stdio.h>
#include <windows.h>

struct coord 
{
	int x;//x坐标
	int y;//y坐标
};

void initMap();//初始化静态背景边框
void showStaticMap();//打印静态背景图
void gotoxy(int x,int y);//控制光标的位置
void hideCursor();//隐藏光标
void drawDynamicShape();//画动态图形
void showDynamicShape();//打印动态图形
void moveDown();//向下移动

//全局变量
int map[21][12];//保存静态背景图
struct coord shape[4];//保存了图形的四个坐标点

int main()
{
	initMap();//初始化边框
	drawDynamicShape();//画动态图形,对四个坐标点赋值
	while(1)
	{
		showStaticMap();//打印静态图
		showDynamicShape();//打印动态图形
		moveDown();//向下移动 
		Sleep(1000);//延时1000毫秒
	}
	getchar();
	return 0;
}
//向下移动
void moveDown()
{
	int i;
	//先判断能否向下移动
	for(i = 0; i < 4; i++)
	{//把四个坐标点,当做二维数组的行和列的下标   x --->列  y --->行
		if(map[shape[i].y+1][shape[i].x] != 0)//y+1判断下一个位置是不是碰到障碍
		{
			return;//函数结束
		}
	}
	//实现让图形向下移动一个位置
	for(i = 0; i < 4; i++)
	{
		shape[i].y++;
	}
}
//打印动态图形
void showDynamicShape()
{
	int i;
	for(i = 0; i < 4; i++)
	{
		gotoxy(shape[i].x*2,shape[i].y);
		printf("■");
	}
}
//画动态图形
void drawDynamicShape()
{
	shape[0].x = 5;
	shape[0].y = 0;
	shape[1].x = 6;
	shape[1].y = 0;
	shape[2].x = 5;
	shape[2].y = 1;
	shape[3].x = 6;
	shape[3].y = 1;
}
//初始化静态背景边框
void initMap()
{
	int i,j;
	hideCursor();//隐藏光标
	for(i = 0; i < 21; i++)
	{
		for(j = 0; j < 12; j++)
		{
			if(j == 0 || j == 11 || i == 20)
				map[i][j] = 1;
			else
				map[i][j] = 0;
		}
	}
}
//打印静态背景图
void showStaticMap()
{
	int i,j;
	gotoxy(0,0);//每次打印边框前,将光标移动到坐标原点
	for(i = 0; i < 21; i++)
	{
		for(j = 0; j < 12; j++)
		{
			if(map[i][j] == 0) //打印空格
			{
				printf("  ");//两个空格,是因一个星星的宽度占两个位置
			}
			else if(map[i][j] == 1)//打印边框
			{
				printf("★");
			}
			else if(map[i][j] == 2)//打印图形
			{
				printf("■");
			}
		}
		printf("\n");
	}
}
//控制光标位置
//隐藏光标

五、添加按键处理、控制左右下移动

5.1 添加按键处理、控制左右下移动

加上头文件conio.h ,在主函数中 if 条件判断是否有按键产生if(_kbhit()),在判断是哪一个按键,根据按键设定去执行相应的函数;左右移动是列数的移动,在移动前需要判断是否为0,如果不为0则无法移动,提前结束控制左右移动函数。

5.2 随机产生不同图形

添加time.h函数,srand(time(NULL))初始化随机种子,在画动态图形的函数中使用rand()生成一个随机数,使用switch()函数,根据随机数判断条件分支,画出相应图形

#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>

struct coord 
{
	int x;//x坐标
	int y;//y坐标
};

void initMap();//初始化静态背景边框
void showStaticMap();//打印静态背景图
void gotoxy(int x,int y);//控制光标的位置
void hideCursor();//隐藏光标
void drawDynamicShape();//画动态图形
void showDynamicShape();//打印动态图形
void moveDown();//向下移动
void addStaticMap();//添加到静态背景图中
void moveLeft();//向左移动
void moveRight();//向右移动

//全局变量
int currentShape;//用来保存当前图形的编号
int map[21][12];//保存静态背景图
struct coord shape[4];//保存了图形的四个坐标点

int main()
{
	int i;
	char input;//用来保存输入的按键
	srand(time(NULL));//初始化随机种子
	initMap();//初始化边框
	drawDynamicShape();//画动态图形,对四个坐标点赋值
	while(1)
	{
		showStaticMap();//打印静态图
		showDynamicShape();//打印动态图形
		moveDown();//向下移动 
		for(i = 0; i < 20; i++)
		{
			if(_kbhit())//检测是否有按键产生
			{
				input = _getch();
				if(input == 'a')
				{
					/*printf("向左移动");*/
					moveLeft();
					input = 0;
				}
				else if(input == 'd')
				{
					/*printf("向右移动");*/
					moveRight();
					input = 0;
				}
				else if(input == 'w')
				{
					/*printf("变形");*/
					//changeShape();
					input = 0;
				}
				else if(input == 's')
				{
					/*printf("向下移动");*/
					moveDown();
					input = 0;
				}
			}
			Sleep(50);
		}
	//	Sleep(1000);//延时1000毫秒
	}
	getchar();
	return 0;
}
//向左移动 
void moveLeft()
{
	int i;
	//先要进行条件判断,判断是否能够向左移动
	for(i = 0; i < 4; i++)
	{
		if(map[shape[i].y][shape[i].x-1] != 0)//碰到障碍物或边框
		{
			return;//结束函数
		}
	}
	//向左移动
	for(i = 0; i < 4; i++)
	{
		shape[i].x--;
	}
}
//向右移动
void moveRight()
{
	int i;
	//先要进行条件判断,判断是否能够向右移动
	for(i = 0; i < 4; i++)
	{
		if(map[shape[i].y][shape[i].x+1] != 0)//碰到障碍物或边框
		{
			return;//结束函数
		}
	}
	//向右移动
	for(i = 0; i < 4; i++)
	{
		shape[i].x++;
	}
}
//将动态图形添加到静态背景图中
void addStaticMap()
{
	int i;
	for(i = 0; i < 4; i++)
	{
		map[shape[i].y][shape[i].x] = 2;
	}
}
//向下移动
void moveDown()
{
	int i;
	//先判断能否向下移动
	for(i = 0; i < 4; i++)
	{//把四个坐标点,当做二维数组的行和列的下标
	 //x --->列
     //y --->行
		if(map[shape[i].y+1][shape[i].x] != 0)//y+1判断下一个位置是不是碰到障碍
		{
			//将动态图形添加到静态背景图中
			addStaticMap();
			//再次生成一个新的图形,再画一个图形
			drawDynamicShape();
			return;//函数结束,说明落到了最底下
		}
	}
	//实现让图形向下移动一个位置
	for(i = 0; i < 4; i++)
	{
		shape[i].y++;
	}
}
//打印动态图形
void showDynamicShape()
{
	int i;
	for(i = 0; i < 4; i++)
	{
		gotoxy(shape[i].x*2,shape[i].y);
		printf("■");
	}
}
//画动态图形
void drawDynamicShape()
{
	currentShape = rand()%3;//生成一个随机图形
	switch(currentShape)
	{
	case 0:
		shape[0].x = 5;//田字格
		shape[0].y = 0;
		shape[1].x = 6;
		shape[1].y = 0;
		shape[2].x = 5;
		shape[2].y = 1;
		shape[3].x = 6;
		shape[3].y = 1;
		break;
	case 1:
		shape[0].x = 4;//横条
		shape[0].y = 0;
		shape[1].x = 5;
		shape[1].y = 0;
		shape[2].x = 6;
		shape[2].y = 0;
		shape[3].x = 7;
		shape[3].y = 0;
		break;
	case 2:
		shape[0].x = 5;//竖条
		shape[0].y = 0;
		shape[1].x = 5;
		shape[1].y = 1;
		shape[2].x = 5;
		shape[2].y = 2;
		shape[3].x = 5;
		shape[3].y = 3;
		break;
	}

}

六、消行、增加分数和难度

6.1 消行、增加分数

判断第 n 行是不是满行,是满行返回1,不是返回0,如果满行则调用清除单行满行函数,使用上一行覆盖掉当前的单行满行,同时再进行一次判断,避免出现连续的两个满行有bug

6.2 增加游戏难度水平

定义3个全局变量,分别进行初始化,游戏分数为0 难度水平为1 下降速度为20;if 条件判断分数、难度水平,根据当前分数和难度水平进行下降速度的调控;在消除单行函数中没消除一行就加多少分,由自己设定

#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>

struct coord 
{
	int x;//x坐标
	int y;//y坐标
};

void initMap();//初始化静态背景边框
void showStaticMap();//打印静态背景图
void gotoxy(int x,int y);//控制光标的位置
void hideCursor();//隐藏光标
void drawDynamicShape();//画动态图形
void showDynamicShape();//打印动态图形
void moveDown();//向下移动
void addStaticMap();//添加到静态背景图中
void moveLeft();//向左移动
void moveRight();//向右移动
void clearFullLine();//清除满行
int isFullOneLine(int line);//判断line行是不是满行
void clearOneLine(int line);//清除当前的line满行
void addLevel();//增加游戏难度水平

//全局变量
int score = 0;//游戏分数
int level = 1;//游戏难度水平
int speed = 20;//用来控制下落速度
int currentShape;//用来保存当前图形的编号
int map[21][12];//保存静态背景图
struct coord shape[4];//保存了图形的四个坐标点

int main()
{
	int i;
	char input;//用来保存输入的按键
	srand(time(NULL));//初始化随机种子
	initMap();//初始化边框
	drawDynamicShape();//画动态图形,对四个坐标点赋值
	while(1)
	{
		showStaticMap();//打印静态图
		showDynamicShape();//打印动态图形
		moveDown();//向下移动 
		for(i = 0; i < speed; i++)
		{
			if(_kbhit())//检测是否有按键产生
			{
				input = _getch();
				if(input == 'a')
				{
					/*printf("向左移动");*/
					moveLeft();
					input = 0;
				}
				else if(input == 'd')
				{
					/*printf("向右移动");*/
					moveRight();
					input = 0;
				}
				else if(input == 'w')
				{
					/*printf("变形");*/
					//changeShape();
					input = 0;
				}
				else if(input == 's')
				{
					/*printf("向下移动");*/
					moveDown();
					input = 0;
				}
			}
			Sleep(50);
		}
	}
	getchar();
	return 0;
}

//增加游戏难度水平的函数
void addLevel()
{
	if(score >= 500 && level == 1)
	{
		speed -= 5;
		level = 2;
	}
	else if(score >= 2000 && level == 2)
	{
		speed -= 5;
		level = 3;
	}
	else if(score >= 5000 && level == 3)
	{
		speed -= 5;
		level = 4;
	}
}
//判断第line行是不是满行,是满行返回1,不是返回0
int isFullOneLine(int line)
{
	int j;
	for(j = 1; j <= 10; j++)
	{
		if(map[line][j] == 0)//说明line不是满行
			return 0;//0代表不是满行
	}
	//程序如果能执行到这,说明没有执行过return 0
	return 1;//代表是满行
}
//清除单行满行
void clearOneLine(int line)
{
	int i,j;
	for(i = line; i > 0; i--)
	{
		for(j = 1; j <= 10; j++)
		{
			map[i][j] = map[i-1][j];
		}
	}
}
//消除满行
void clearFullLine()
{
	int i;
	for(i = 19; i > 0; i--)
	{
		//1.判断每一行i是不是满行
		if(isFullOneLine(i))//返回值是1,说明是满行 1代表真
		{
			//2.消除当前的满行i
			clearOneLine(i);
			score += 100;
			//3.增加游戏难度水平
			addLevel();
			i++;//避免出现连续的两个满行有bug
		}
		
	}

}

七、变化图形和游戏结束判断

7.1 图形变形功能

先确定图形是什么类型的图形,在变形前,先判断是否碰撞,除了中心点以外的三对坐标点是否碰撞,如果碰撞则无法变形  ;以中心点为坐标旋转90°后的坐标表示变形后的坐标

7.2 游戏结束判断功能

判断第0行是否为满,如果为满调用消行函数,如果第0行不为空则游戏结束,将难度等级和分数显示出来

八、项目过程的问题

8.1 问题一:坐标问题

map[21][12]  我们建立的坐标系x表示列数  y表示行数 ;因为图形 "■"占用两个字符位置所以要*2,在动态图形向下移动时,只要y变化就可以了,y才是行数

8.2 问题二:碰撞判断问题

在变形、左右移动、向下移动中都需要进行碰撞判断,碰撞检测函数int center :代表的是中心点,在结构体数组中的下标;三对x y,代表的除了中心点以外,其他点的变化值,如果碰撞返回值是1,不碰撞返回值0

总结

这里对文章进行总结:按照逻辑思维进行操作,步骤如下:

1、打印静态背景边框

       先定义全局二维数组,将第0列11列和第20行赋值为1,其余赋值为0,遍历二维数组,将为1的打印星星,为0的打印两个空格,封装成初始化背景边框函数、打印静态背景边框函数

2、画一个动态图形,显示在顶部

      全局定义一个结构体保存xy坐标点,全局定义一个结构体数组,保存动态图形的4个指标点,将动态图形打印封装成函数来显示动态图形,同时调用gotoxy函数(打印静态背景边框)和光标隐藏函数(初始化静态边框)

3、控制动态图形下落、动态图形添加到静态背景

     图形下落,是行数的变化,即y++ ;添加静态背景图时,在下落前先判断,下一行是否为0,如果不为0,则停止下落,同时将结构体的4个坐标点赋值为2(非0非1即可),调用打印静态背景边框函数中添加一个条件,以此打印被添加到静态背景边框的动态图形。

4、产生新的动态图形并添加按键处理

    在图形向下移动判断下一行不为0时,调用画动态图形函数,结束下落; 控制动态图形的向左、向右、向下移动,根据自己设定的按键,判断哪一个按键被触发,if条件去执行相应函数,以此控制动态图形的移动

5、消行、增加分数和难度水平

判断当前行是否满行,如果满行这清空当前行,同时在进行一次判断,防止bug两行都满行,但是只清除一行的情况;全局定义分数、难度等级和下降速度,根据消除一行添加多少分数,根据分数和难度等级分成不同的下降速度,以此来控制下降速度的快慢

6、图形变换和游戏结束判断功能

图形变化先先进行是否碰撞,除中心点外的其他3个坐标点在变化后是否会碰到左右背景边框、以及添加的静态背景图,如果不为0 即没有碰撞则可以变形,否则结束变形函数;判断第0行是否为空,如果不为空为满,这调用消行,如果不为空也不为满,这结束游戏。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* *游戏明:此为我边学边做的,但有很多的BUG,参照了我从网上下载的游戏方块设计 * 一、游戏的BUG * 1、提示分数那地方,玩过游戏后,长了分数后,再玩时,后面的数据没清掉 * 2、重级BUG:当在游戏过程中,切换了一个旁边的后,再切换回来后, * 刚才的方块不显了,而且游戏区的数据也不见了。 * 3、我在刚开始做时,没有要窗口上的关闭,现在想要,但不知道怎么加。 * 4、如果用鼠标点了菜单后,再回到游戏区,数据方块又不见了。 * * 二、此游戏没有版权,可以乱改,反正我也是在学习,谢谢那些无私的网友们,不过 * 请你们下次提交上来的源程序有个明好不好,看得我头都大了,流程图也没得, * 设计明也没有,完全看源代码,很费力的!谢谢对新人的支持。 * * 三、如果那位高手愿意,请收我为徒弟吧,我学过c/c++、数据结构、编译原理、操作系统等( * 计算机专业的),但对于VC这个大东西来,,好像一点用也没有,现在在边学边做,门不好入呀! * * 四、请高手们帮帮我,请给分析一下问题在哪,谢谢,我对VC还不是很清楚,在文件目录下 * 有设计时的流程图。设计明我没有写,我是针对每一个流程图模块来设计和编码的。 * * 五、在游戏中,我加了很多注释,以方便理解,主要的代码都在CChileView.h、CChileView.CPP中 * 我想的是,如果新人也想看看的话,可能理解起来快点。不过有点乱,编码中有些冗余。没来 * 得及改。如果你改好了,请给我一份,我想学学。谢谢! * *================================================================================================ * *编译环境: * 1、操作系统 :WindowsXP SP2 * 2、编译器 :Visual C++ 8.0 * *包含文件:所有源文件都在此。 * * *编译参考:此目录下有一个文件名为:Russia.sln的文件,用Visual C++ 8.0 打开,直接就可编译 * 此游戏是我编译通过后,才压缩的。如果编译不了,请联系我,下面有我的QQ和email。 * * *其它事宜:如果还有什么问题我没有提到的,请联系我,愿向你学习。 * *================================================================================================ * *Version :BUG Edtion * *Aauthor :lin_liu60 * *E-mail :[email protected] * *QQ :994165 (网名:刘羽峰) * *Date :2006/9/27 * */
以下是俄罗斯方块项目需求明书: 1. 项目概述: 俄罗斯方块游戏是一款经典的益智游戏,玩家需要控制方块的下落位置,使其与已有的方块拼接在一起,从而消除方块并获得分数。本项目旨在开发一款可供用户在线玩的俄罗斯方块游戏。 2. 功能需求: - 游戏开始界面:包括游戏名称、开始游戏游戏规则、排行榜等选项。 - 游戏界面:包括方块下落区域、当前方块、下一个方块、得分、等级、暂停、重新开始等选项。 - 方块控制:玩家可以通过键盘控制方块的移动、旋转和加速下落。 - 方块生成:游戏需要生成一系列不同形状的方块,包括I、J、L、O、S、T、Z七种形状,每种形状包含不同的方块组合。 - 方块下落:方块从顶部开始下落,下落速度逐渐加快,当方块到达底部或与已有的方块拼接在一起时停止下落。 - 方块消除:当一行方块被填满时,该行方块将被消除并得到相应的分数,上方的方块将下落填补空缺。 - 得分和等级:玩家在游戏中获得分数,随着分数的增加,游戏难度会逐渐提高。 - 排行榜:游戏需要记录最高得分和排名,方便用户比较自己的成绩。 - 界面美观:游戏需要有简洁美观的界面设计,包括背景、字体、颜色等。 3. 非功能需求: - 稳定性:游戏需要保证稳定性,避免出现异常情况。 - 平台兼容性:游戏需要支持不同平台,包括PC、手机等。 - 响应速度:游戏需要响应速度快,避免卡顿现象。 - 安全性:游戏需要保证用户隐私和信息安全。 以上是俄罗斯方块项目的需求明书,希望对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值