C/C++教程

游戏俄罗斯方块(c语言)

本文主要是介绍游戏俄罗斯方块(c语言),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

游戏俄罗斯方块(c语言)

    • 一、项目背景
    • 二、设计目的
    • 三、项目功能需求
      • 1、游戏方块控制
      • 2、游戏显示更新
      • 3、游戏速度等级更新
      • 4、游戏帮助
    • 四、系统的功能结构图
    • 五、总体设计
      • 1、界面显示模块
      • 2、开始与结束模块
      • 3、移动变换模块
      • 4、判断冲突模块
      • 5、满行消除模块
    • 六、详细设计
      • 1、主函数
      • 2、界面显示模块
      • 3、开始与结束界面模块
      • 4、移动变换模块
      • 5、判断冲突模块
      • 6、满行消除模块
    • 七、代码

一、项目背景

​ 俄罗斯方块(Terris, 俄文:Тетрис)是一款电视游戏机和掌上游戏机游戏。俄罗斯方块的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。

二、设计目的

  • 如何用OpenGL编写画图程序
  • 如何用数组储存信息
  • 如何实现菜单的显示、选择和响应等功能
  • 如何实现图形的移动,变换等功能

三、项目功能需求

1、游戏方块控制

  • 通过各种条件的判断,实现对游戏方块的左移、右移、快速下落、自由下落、旋转功能,以及行满消除的功能

2、游戏显示更新

  • 当游戏方块左右移动、下落、旋转时,要先清除先前的游戏方块,用新坐标重绘游戏方块

  • 当消除满行时,要重绘游戏底板的当前状态

3、游戏速度等级更新

  • 当游戏玩家进行游戏过程中,需要按照一定的游戏规则给游戏玩家计算游戏分数

4、游戏帮助

  • 游戏玩家进入游戏后,将有对本游戏如何操作的友好提示。

四、系统的功能结构图

在这里插入图片描述

五、总体设计

功能模块介绍

1、界面显示模块

  • 创建游戏主窗口并将游戏帮助显示在游戏界面上。

2、开始与结束模块

  • 在游戏开始前创建一个欢迎进入游戏的提示窗口,玩家可选择进入游戏或退出游戏

3、移动变换模块

  • 通过对按键的判断,实现方块的移动、变换等功能

4、判断冲突模块

  • 该模块可判断移动是否合法,防止出现方块重叠、越界的情况。

5、满行消除模块

  • 该模块是几个功能的集合,包括对满行方块的消除、对分数的更新以及对下落速度、等级的变化处理。

六、详细设计

1、主函数

首先初始化OpenGL描画库资源,然后进入的是游戏 欢迎界面,由玩家选择是否进入游戏,或者直接退出。

处理流程如图

在这里插入图片描述

2、界面显示模块

功能设计

  • 实现对游戏窗口的创建,方块下落的循环操作**。**
  • 还可实现字符的显示,将游戏帮助显示在屏幕上。

3、开始与结束界面模块

功能设计

  • 在游戏开始前,创建一个游戏欢迎窗口,用户可通过自主移动光标选择是否开始。
  • 在游戏结束时创建一个游戏结束窗口,在该结束界面显示分数,等级信息。

4、移动变换模块

功能设计

  • 通过按键来实现方块的移动和旋转变形。
  • 当某一(些)行达到满行时,将该(这些)行的方块全部消除,并将上方非满方块的行下移**。**

5、判断冲突模块

功能设计

  • 检查方块移动或变形后是否超出游戏边界。
  • 如果移动或变形后超出边界,则本次变形操作无效,保持原来的形状和位置**。**

6、满行消除模块

功能设计

  • 当方块下落到底部时,会检查是否出现了满行。
  • 若方块满行,则消除该行方块,得分增加**。**

七、代码

#include<GL/glut.h>
#include<time.h>
#include<windows.h>
#include<stdlib.h>
#include<stdio.h>
#include<conio.h>

#define LEFT  'a'/*操控按键宏定义*/
#define RIGHT  'd'
#define UP     'w'
#define DOWN   's'
#define START   0
#define SIZE 	20/*图形范围定义*/
#define ESC     27/*键值定义*/
#define ENTER   13
#define MAX_CHAR   128/*输出文字显示列表数量*/

/*定义光标移动的点*/
struct Point
{
	int x;
	int y;
};
/*
初始化七个二维数组,即七个块刚开始产生时出现的位置
记录坐标的顺序为从左至右,从上至下
*/
GLfloat afShape[][4][2] =
{
	{ { -0.2f, 0.9f }, { -0.2f, 0.8f }, { -0.2f, 0.7f }, { -0.2f, 0.6f } },
	/*1、记录初始下落时长条四个坐标*/
	{ { -0.3f, 0.9f }, { -0.2f, 0.9f }, { -0.3f, 0.8f }, { -0.2f, 0.8f } },
	/*2、记录初始下落时正方形四个坐标*/
	{ { -0.3f, 0.9f }, { -0.4f, 0.8f }, { -0.3f, 0.8f }, { -0.2f, 0.8f } },
	/*3、记录初始下落时T字形四个坐标*/
	{ { -0.3f, 0.9f }, { -0.2f, 0.9f }, { -0.2f, 0.8f }, { -0.1f, 0.8f } },
	/*4、记录初始下落时Z字形四个坐标*/
	{ { -0.3f, 0.9f }, { -0.2f, 0.9f }, { -0.4f, 0.8f }, { -0.3f, 0.8f } },
	/*5、记录初始下落时倒Z字形四个坐标*/
	{ { -0.3f, 0.9f }, { -0.3f, 0.8f }, { -0.3f, 0.7f }, { -0.2f, 0.7f } },
	/*6、记录初始下落时L字形四个坐标*/
	{ { -0.2f, 0.9f }, { -0.2f, 0.8f }, { -0.3f, 0.7f }, { -0.2f, 0.7f } },
	/*7、记录初始下落时倒L字形四个坐标*/
};
GLfloat afShapeNext[][4][2] =
{
	{ { 0.7f, 0.7f }, { 0.7f, 0.6f }, { 0.7f, 0.5f }, { 0.7f, 0.4f } },
	/*1、记录预览下一长条四个坐标*/
	{ { 0.6f, 0.7f }, { 0.7f, 0.7f }, { 0.6f, 0.6f }, { 0.7f, 0.6f } },
	/*2、记录预览下一正方形四个坐标*/
	{ { 0.7f, 0.7f }, { 0.6f, 0.6f }, { 0.7f, 0.6f }, { 0.8f, 0.6f } },
	/*3、记录预览下一T字形四个坐标*/
	{ { 0.6f, 0.7f }, { 0.7f, 0.7f }, { 0.7f, 0.6f }, { 0.8f, 0.6f } },
	/*4、记录预览下一Z字形四个坐标*/
	{ { 0.7f, 0.7f }, { 0.8f, 0.7f }, { 0.6f, 0.6f }, { 0.7f, 0.6f } },
	/*5、记录预览下一倒Z字形四个坐标*/
	{ { 0.6f, 0.7f }, { 0.6f, 0.6f }, { 0.6f, 0.5f }, { 0.7f, 0.5f } },
	/*6、记录预览下一L字形四个坐标*/
	{ { 0.7f, 0.7f }, { 0.7f, 0.6f }, { 0.6f, 0.5f }, { 0.7f, 0.5f } },
	/*7、记录预览下一倒L字形四个坐标*/
};
/*这里定义的over是用来判断方块是否到达了不能再往下降的地方,到了
则置其为1,否则就修改为0。
其中有这样几种情况需要修改over:
1、重新生成了一个方块,修改over=0
2、方块到大底部,修改over=1*/
GLint aiBlock[SIZE][SIZE] = { 0 };/*记录游戏区域方块状态*/
GLfloat afCurLoc[4][2] = { 0 };/*记录当前正在下落的方块的四个坐标*/
GLfloat afNextLoc[4][2] = { 0 };/*记录接下来下落的方块的四个坐标*/
GLint iCurrentBlock = 1;/*记录当前正在下落的是第1种图形,顺序如上面所示*/
GLint iNextBlock = 1;/*记录接下来下落的是第1种图形,顺序如上面所示*/
GLint aiTurn[7] = { 0 };/*应该变换的形态*/
GLfloat xd = 0.0f, yd = 0.0f;
GLuint uiTextFont;/*定义文字输出函数时需用到*/
int iLevel = 0;
int iOver = 0;
int iEndGame = 0;/*记录游戏是否结束*/
int iScore = 0;
int iLefRig = 0;
int iTimeS = 1000;
int iStart = -1;/*0表示退出游戏,1表示开始游戏*/
char cButton;/*键盘敲下的键值*/
struct Point stPoint;

/*函数声明*/
void Down(int id);
void InitBlock(void);
void Change(void);
void CheckDelete(void);
int CheckConflict(int iLefRig);
void CreateBlocks(void);
void BlockDisplay(void);
void Key(unsigned char k, int x, int y);
void Delete(int *empty);
void PrintString(char *s);/*字符打印函数*/
void MenuDisplay(void);/*显示帮助菜单*/
void InitString(void);
void GotoXY(int x, int y);/*移动坐标函数*/
void Choose(void);
void EndScreen(void);
void WelcomeScreen(void);

void PrintString(char *s)
{
	if (s == NULL)	return;
	glPushAttrib(GL_LIST_BIT);
	/*调用每个字符对应的显示列表,绘制每个字符*/
	for (; *s != '\0'; ++s)
		glCallList(uiTextFont + *s);
	glPopAttrib();
}
void MenuDisplay(void)
{
	int i, j;
	glClear(GL_COLOR_BUFFER_BIT);
	/*显示Level信息*/
	glColor3f(1.0, 1.0, 1.0);
	glRasterPos3f(0.6, 0.1, 0.0);/*设置显示位置*/
	PrintString("Level:");
	glRasterPos3f(0.6, 0.0, 0.0);
	switch (iLevel)
	{
	case 0:
		PrintString("0");
		break;
	case 1:
		PrintString("1");
		break;
	case 2:
		PrintString("2");
		break;
	case 3:
		PrintString("3");
		break;
	case 4:
		PrintString("4");
		break;
	default:
		PrintString("5");
	}
	/*显示Help信息*/
	glRasterPos3f(0.6, -0.25, 0.0);
	PrintString("Help:");
	glRasterPos3f(0.6, -0.4, 0.0);
	PrintString("W----Roll");
	glRasterPos3f(0.6, -0.5, 0.0);
	PrintString("S----Downwards");
	glRasterPos3f(0.6, -0.6, 0.0);
	PrintString("A----Turn Left");
	glRasterPos3f(0.6, -0.7, 0.0);
	PrintString("D----Turn Right");
	glRasterPos3f(0.6, -0.8, 0.0);
	PrintString("ESC----EXIT");
	/*显示预览方块信息*/
	glRasterPos3f(0.6, 0.9, 0.0);
	PrintString("NextBlock:");
	/*重置预览下一方块的四个坐标*/
	for (i = 0; i<4; i++)
	{
		for (j = 0; j<2; j++)
		{
			afNextLoc[i][j] = afShapeNext[iNextBlock][i][j];
		}
	}
	/*将预览方块涂色*/
	for (i = 0; i < 4; i++)
	{
		glColor3f(1.0, 1.0, 0.0);
		glRectf(afNextLoc[i][0], afNextLoc[i][1], afNextLoc[i][0] + 0.1f, afNextLoc[i][1] + 0.1f);
		glLineWidth(2.0f);
		glBegin(GL_LINE_LOOP);
		glColor3f(0.0f, 0.0f, 0.0f);
		glVertex2f(afNextLoc[i][0], afNextLoc[i][1]);
		glVertex2f(afNextLoc[i][0] + 0.1f, afNextLoc[i][1]);
		glVertex2f(afNextLoc[i][0] + 0.1f, afNextLoc[i][1] + 0.1f);
		glVertex2f(afNextLoc[i][0], afNextLoc[i][1] + 0.1f);
		glEnd();
		glFlush();
	}
}

void GotoXY(int x, int y)
{
	COORD c;
	c.X = 2 * x;
	c.Y = y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}
/*选择键面操作*/
void Choose()
{
	/*若是按下退出或开始游戏就退出循环*/
	while (iStart != 0 && iStart != 1)
	{
		cButton = getch();
		/*若是up和down就进行光标移动操作*/
		if (cButton == 72 || cButton == 80)
		{
			if (stPoint.y == 13)
			{
				stPoint.y = 16;
				GotoXY(10, 13);
				printf("     ");
				GotoXY(10, 16);
				printf("——>");
				GotoXY(12, 16);
			}
			else if (stPoint.y == 16)
			{
				stPoint.y = 13;
				GotoXY(10, 16);
				printf("     ");
				GotoXY(10, 13);
				printf("——>");
				GotoXY(12, 13);
			}
		}
		/*按下esc键或enter退出游戏*/
		if ((cButton == ENTER && stPoint.y == 16) || cButton == ESC)
		{
			iStart = 0;
			break;
		}
		/*开始游戏*/
		if (cButton == ENTER && stPoint.y == 13)
		{
			iStart = 1;
			break;
		}
	}
}
void WelcomeScreen()
{
	system("color 0F");
	GotoXY(14, 1);
	printf("Welcome to play Tetris");
	GotoXY(17, 7);
	printf("MAIN MENU");
	GotoXY(15, 13);
	printf("***START GAME***");
	GotoXY(15, 16);
	printf("***QUIT  GAME***");
	GotoXY(10, 13);
	printf("——>");
	stPoint.x = 15;
	stPoint.y = 13;
	GotoXY(12, 13);
	Choose();
}
void EndScreen()
{
	system("cls");/*清屏函数*/
	system("color 0F");
	GotoXY(16, 3);
	printf("GAME OVER!!!\n");
	GotoXY(15, 10);
	printf("YOU SCORE IS: %d\n", iScore * 100);
	GotoXY(15, 12);
	printf("YOU LEVEL IS: %d\n", iLevel);

	//GotoXY(15, 14);
	//printf("Quit choose ESC!\n");
#if 0 //重玩功能的实现,后续完善
	cButton = getch();
	if (cButton == 89)
	{
		iTimeS = 1000;
		iEndGame = 0;
		iLevel = 0;
		iOver = 0;
		iScore = 0;
		iLefRig = 0;
		InitBlock();
		CreateBlocks();
		glutPostRedisplay();
		glutTimerFunc(iTimeS, Down, 1);
	}
	else
	{
		exit(1);
	}
#endif	
}
/*初始化字符绘制显示列表*/
void InitString(void)
{
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
	/*申请MAX_CHAR个连续的显示列表编号*/
	uiTextFont = glGenLists(MAX_CHAR);
	/*把每个字符的绘制命令都装到对应的显示列表中*/
	wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, uiTextFont);
}
/*初始化方块矩阵,方块是一个上端开口的长方形*/
void InitBlock()
{
	int i, j;
	for (i = 0; i < SIZE - 5; i++)
		for (j = 0; j < SIZE; j++)
			aiBlock[i][j] = 0;
	for (i = 0; i < SIZE - 5; i++)
		aiBlock[0][i] = 1;
	for (i = 0; i < SIZE; i++)
	{
		aiBlock[i][0] = 1;
		aiBlock[i][SIZE - 6] = 1;
	}
	for (i = 0; i<4; i++)
		for (j = 0; j<2; j++)
			afCurLoc[i][j] = afShape[iCurrentBlock][i][j];
}
/*将图形做变换,采用顺时针旋转的规律*/
void Change()
{
	int ret;
	/*用临时变量储存当前方块中四个小方块的坐标*/
	GLfloat temp00 = afCurLoc[0][0];
	GLfloat temp01 = afCurLoc[0][1];
	GLfloat temp10 = afCurLoc[1][0];
	GLfloat temp11 = afCurLoc[1][1];
	GLfloat temp20 = afCurLoc[2][0];
	GLfloat temp21 = afCurLoc[2][1];
	GLfloat temp30 = afCurLoc[3][0];
	GLfloat temp31 = afCurLoc[3][1];
	int tempTurn = aiTurn[iCurrentBlock];

	switch (iCurrentBlock)
	{
	case 0:/*长条*/
		if (aiTurn[0] == 0)/*长条第1种形态*/
		{
			afCurLoc[0][0] = temp10 - 0.1f;
			afCurLoc[0][1] = temp11;
			afCurLoc[2][0] = temp10 + 0.1f;
			afCurLoc[2][1] = temp11;
			afCurLoc[3][0] = temp10 + 0.2f;
			afCurLoc[3][1] = temp11;
		}
		else if (aiTurn[0] == 1)/*长条第2种形态*/
		{
			afCurLoc[0][0] = temp10;
			afCurLoc[0][1] = temp11 + 0.1f;
			afCurLoc[2][0] = temp10;
			afCurLoc[2][1] = temp11 - 0.1f;
			afCurLoc[3][0] = temp10;
			afCurLoc[3][1] = temp11 - 0.2f;
		}
		/*使长条变到第2种形态后按'w'还可变成第1种形态*/
		aiTurn[0] = (aiTurn[0] + 1) % 2;
		break;
	case 1:/*正方形*/
		break;
	case 2:/*T字形*/
		if (aiTurn[2] == 0)
		{
			afCurLoc[1][0] = temp20;
			afCurLoc[1][1] = temp21;
			afCurLoc[2][0] = temp30;
			afCurLoc[2][1] = temp31;
			afCurLoc[3][0] = temp20;
			afCurLoc[3][1] = temp21 - 0.1f;
		}
		else if (aiTurn[2] == 1)
		{
			afCurLoc[0][0] = temp10 - 0.1f;
			afCurLoc[0][1] = temp11;
		}
		else if (aiTurn[2] == 2)
		{
			afCurLoc[0][0] = temp10;
			afCurLoc[0][1] = temp11 + 0.1f;
			afCurLoc[1][0] = temp00;
			afCurLoc[1][1] = temp01;
			afCurLoc[2][0] = temp10;
			afCurLoc[2][1] = temp11;
		}
		else if (aiTurn[2] == 3)
		{
			afCurLoc[3][0] = temp20 + 0.1f;
			afCurLoc[3][1] = temp21;
		}
		aiTurn[2] = (aiTurn[2] + 1) % 4;
		break;
	case 3:/*Z字形*/
		if (aiTurn[3] == 0)
		{
			afCurLoc[0][0] = temp10 + 0.1f;
			afCurLoc[0][1] = temp11 + 0.1f;
			afCurLoc[2][0] = temp10 + 0.1f;
			afCurLoc[2][1] = temp11;
			afCurLoc[3][0] = temp20;
			afCurLoc[3][1] = temp21;
		}
		else if (aiTurn[3] == 1)
		{
			afCurLoc[0][0] = temp10 - 0.1f;
			afCurLoc[0][1] = temp11;
			afCurLoc[2][0] = temp30;
			afCurLoc[2][1] = temp31;
			afCurLoc[3][0] = temp30 + 0.1f;
			afCurLoc[3][1] = temp31;
		}
		aiTurn[3] = (aiTurn[3] + 1) % 2;
		break;
	case 4:/*反Z字形*/
		if (aiTurn[4] == 0)
		{
			afCurLoc[0][0] = temp00 - 0.1f;
			afCurLoc[0][1] = temp01 + 0.1f;
			afCurLoc[1][0] = temp00 - 0.1f;
			afCurLoc[1][1] = temp01;
			afCurLoc[2][0] = temp00;
			afCurLoc[2][1] = temp01;
			afCurLoc[3][0] = temp00;
			afCurLoc[3][1] = temp01 - 0.1f;
		}
		else if (aiTurn[4] == 1)
		{
			afCurLoc[0][0] = temp20;
			afCurLoc[0][1] = temp21;
			afCurLoc[1][0] = temp20 + 0.1f;
			afCurLoc[1][1] = temp21;
			afCurLoc[2][0] = temp10;
			afCurLoc[2][1] = temp11 - 0.1f;
		}
		aiTurn[4] = (aiTurn[4] + 1) % 2;
		break;
	case 5:/*L字形*/
		if (aiTurn[5] == 0)
		{
			afCurLoc[0][0] = temp10;
			afCurLoc[0][1] = temp11;
			afCurLoc[1][0] = temp10 + 0.1f;
			afCurLoc[1][1] = temp11;
			afCurLoc[2][0] = temp10 + 0.2f;
			afCurLoc[2][1] = temp11;
			afCurLoc[3][0] = temp20;
			afCurLoc[3][1] = temp21;
		}
		else if (aiTurn[5] == 1)
		{
			afCurLoc[0][0] = temp00;
			afCurLoc[0][1] = temp01 + 0.1f;
			afCurLoc[1][0] = temp10;
			afCurLoc[1][1] = temp11 + 0.1f;
			afCurLoc[2][0] = temp10;
			afCurLoc[2][1] = temp11;
			afCurLoc[3][0] = temp10;
			afCurLoc[3][1] = temp11 - 0.1f;
		}
		else if (aiTurn[5] == 2)
		{
			afCurLoc[0][0] = temp20 + 0.1f;
			afCurLoc[0][1] = temp21;
			afCurLoc[1][0] = temp20 - 0.1f;
			afCurLoc[1][1] = temp21 - 0.1f;
			afCurLoc[2][0] = temp20;
			afCurLoc[2][1] = temp21 - 0.1f;
			afCurLoc[3][0] = temp20 + 0.1f;
			afCurLoc[3][1] = temp21 - 0.1f;
		}
		else if (aiTurn[5] == 3)
		{
			afCurLoc[0][0] = temp10;
			afCurLoc[0][1] = temp11 + 0.2f;
			afCurLoc[1][0] = temp10;
			afCurLoc[1][1] = temp11 + 0.1f;
			afCurLoc[2][0] = temp10;
			afCurLoc[2][1] = temp11;
			afCurLoc[3][0] = temp20;
			afCurLoc[3][1] = temp21;
		}
		aiTurn[5] = (aiTurn[5] + 1) % 4;
		break;
	case 6:/*反L字形*/
		if (aiTurn[6] == 0)
		{
			afCurLoc[0][0] = temp20 - 0.1f;
			afCurLoc[0][1] = temp21 + 0.1f;
			afCurLoc[1][0] = temp20 - 0.1f;
			afCurLoc[1][1] = temp21;
		}
		else if (aiTurn[6] == 1)
		{
			afCurLoc[0][0] = temp00 + 0.1f;
			afCurLoc[0][1] = temp01 + 0.1f;
			afCurLoc[1][0] = temp30;
			afCurLoc[1][1] = temp31 + 0.2f;
			afCurLoc[2][0] = temp00 + 0.1f;
			afCurLoc[2][1] = temp01;
			afCurLoc[3][0] = temp20;
			afCurLoc[3][1] = temp21;
		}
		else if (aiTurn[6] == 2)
		{
			afCurLoc[0][0] = temp00 - 0.1f;
			afCurLoc[0][1] = temp01 - 0.1f;
			afCurLoc[1][0] = temp20;
			afCurLoc[1][1] = temp21;
			afCurLoc[2][0] = temp20 + 0.1f;
			afCurLoc[2][1] = temp21;
			afCurLoc[3][0] = temp30 + 0.1f;
			afCurLoc[3][1] = temp31;
		}
		else if (aiTurn[6] == 3)
		{
			afCurLoc[0][0] = temp20;
			afCurLoc[0][1] = temp21 + 0.1f;
			afCurLoc[1][0] = temp20;
			afCurLoc[1][1] = temp21;
			afCurLoc[2][0] = temp30 - 0.1f;
			afCurLoc[2][1] = temp31;
			afCurLoc[3][0] = temp30;
			afCurLoc[3][1] = temp31;
		}
		aiTurn[6] = (aiTurn[6] + 1) % 4;
		break;
	default:
		break;
	}

	/*如果旋转非法(即旋转时碰到墙壁),则恢复原状态*/
	ret = CheckConflict(iLefRig);
	if (ret == 1)
	{
		afCurLoc[0][0] = temp00;
		afCurLoc[0][1] = temp01;
		afCurLoc[1][0] = temp10;
		afCurLoc[1][1] = temp11;
		afCurLoc[2][0] = temp20;
		afCurLoc[2][1] = temp21;
		afCurLoc[3][0] = temp30;
		afCurLoc[3][1] = temp31;
		aiTurn[iCurrentBlock] = tempTurn;
	}
}
/*
消除满格的一行,在每次over被修改为1的时候都要检查一遍
算法思想是从第0行开始依次判断,如果empty为1则将下面的向上,
并不是判断一次就移动所有的,而是只移动最近的,将空出来的
那一行的empty标记为1
*/
/*
empty[i]=1表示第i行空
empty[i]=0表示第i行满
empty[i]=-1表示第i行部分空
*/
void Delete(int *empty)
{
	int i, j;
	int pos;
	while (1)	/*将上面满行移动到非空行之下*/
	{
		i = 1;
		/*若第i行的状态为“部分空”,则i++;否则状态为满行,需要将上面的行移下来填充*/
		while (i < SIZE && empty[i] == 0)
			i++;
		if (i >= SIZE)	break;

		j = i + 1;   /*i行为满行*/
		while (j < SIZE && empty[j] == -1)
			j++;	

		if (j >= SIZE)	break;
		else if(empty[j] != -1)
		{
			for (pos = 1; pos < 15; pos++)
				aiBlock[i][pos] = aiBlock[j][pos];
			empty[i] = empty[j];/*将第j行与第i行的状态交换*/
			empty[j] = -1;      /*j行变为空行*/
		}
	}
	/*将空行和满行的中的所有方块都置为0*/
	for (i = 1; i < SIZE; i++)
	{
		if (empty[i] != 0)/*-1为空行,1为满行*/
		{
			for (j = 1; j < 14; j++)
				aiBlock[i][j] = 0;
		}
	}
}
void CheckDelete()
{
	int i, j;
	int empty[SIZE];
	int is_needed = 0;
	int count;
	for (i = 1; i<SIZE; i++)
		empty[i] = -1;/*初始均为空行,置为-1*/
	for (i = 0; i<4; i++)
	{
		/*将坐标(x,y)转化为边框中对应的小格数*/
		double x = (afCurLoc[i][0] + 1) * 10 + 0.5;
		double y = (afCurLoc[i][1] + 1) * 10 + 0.5;
		aiBlock[(int)y][(int)x] = 1;
	}
	for (i = 1; i< SIZE; i++)
	{
		count = 0;
		for (j = 1; j<14; j++)
		if (aiBlock[i][j] == 1)
			count++;
		if (count == 13)
		{
			empty[i] = 1;	/*满行,置为1*/
			iScore++;   /*此处计分*/
			iTimeS -= 50;	/*下落速度加快*/
			is_needed = 1;	/*满行,需要消除,置为1*/
		}
		else if (count > 0 && count < 13)
		{
			empty[i] = 0;/*非满行,也非空行,称作“部分空”,置为0*/
		}
	}
	if (is_needed == 1)/*如果有满行则去删除*/
		Delete(empty);
}
int CheckConflict(int iLefRig)
{
	int i, tmpx;
	for (i = 0; i<4; i++)
	{
		double x = (afCurLoc[i][0] + 1) * 10;
		double y = (afCurLoc[i][1] + 1) * 10 + 0.5;
		x = x>0 ? (x + 0.5) : (x - 0.5);
		if (iLefRig == 1)
		{
			tmpx = (int)x;
			if (tmpx > 13 || tmpx < 1)	break;
		}
		if (aiBlock[(int)y][(int)x] == 1) /*判断是否发生冲突*/
		{
			break;
		}
	}
	if (i < 4)
		return 1;
	return 0;
}
void Key(unsigned char k, int x, int y)
{
	int i, ret;
	if (iOver == 0)
	{
		switch (k)
		{
		case UP:/*若按'w',方块变换*/
			Change();
			break;
		case DOWN:/*若按's',方块下移*/
			for (i = 0; i < 4; i++)
			{
				afCurLoc[i][1] -= 0.1f;
			}
			ret = CheckConflict(1);
			if (ret == 1)/*发生冲突,则将修改复原*/
			{
				for (i = 0; i < 4; i++)
					afCurLoc[i][1] += 0.1f;
				iOver = 1;/*并且可以生成下一个方块了*/
			}
			break;
		case RIGHT:/*若按'd',方块右移*/
			for (i = 0; i < 4; i++)
			{
				afCurLoc[i][0] += 0.1f;
			}
			ret = CheckConflict(1);
			if (ret == 1)/*发生冲突,则将修改复原*/
			{
				for (i = 0; i<4; i++)
					afCurLoc[i][0] -= 0.1f;
			}
			break;
		case LEFT:/*若按'a',方块左移*/
			for (i = 0; i < 4; i++)
			{
				afCurLoc[i][0] -= 0.1f;
			}
			ret = CheckConflict(1);
			if (ret == 1)/*发生冲突,则将修改复原*/
			{
				for (i = 0; i<4; i++)
					afCurLoc[i][0] += 0.1f;
			}
			break;
		case ESC:/*若按Esc,游戏退出*/
			exit(1);
			break;
		}
	}
	if (iOver == 1)
		CheckDelete();
	/*调用这个函数可以重新绘图,每次相应消息之后,所有全部重绘*/
	glutPostRedisplay();
}
/*让方块定时下降*/
void Down(int id)
{
	int i, ret;
	if (iOver == 0)
	{
		/*将每个方块纵坐标下移0.1个单位长度*/
		for (i = 0; i<4; i++)
		{
			afCurLoc[i][1] -= 0.1f;
		}
		ret = CheckConflict(iLefRig);
		if (ret == 1)/*发生冲突,则将修改复原*/
		{
			for (i = 0; i<4; i++)
				afCurLoc[i][1] += 0.1f;
			/*若方块生成初始位置超出屏幕,则游戏结束*/
			if (afCurLoc[0][1] >= afShape[iCurrentBlock][0][1])
			{
				iEndGame = 1;
				EndScreen();
				return;
			}
			iOver = 1;/*并且可以生成下一个方块了*/
		}
		/*根据下落速度提升等级*/
		if (iTimeS >= 1000) iLevel = 0;
		else if (iTimeS >= 900) iLevel = 1;
		else if (iTimeS >= 700) iLevel = 2;
		else if (iTimeS >= 500) iLevel = 3;
		else if (iTimeS >= 300) iLevel = 4;
		else iLevel = 5;
	}
	if (iOver == 1)
		CheckDelete();
	glutPostRedisplay();
	glutTimerFunc(iTimeS, Down, 1);
}
/*画图函数,用aiBlock数组绘图*/
void BlockDisplay()
{
	int i, j;
	static int s = 0;
	glClear(GL_COLOR_BUFFER_BIT);
	MenuDisplay();
	for (i = 0; i<20; i++)/*将游戏区域边框涂为灰色*/
	{
		for (j = 0; j<15; j++)
		{
			if (aiBlock[i][j] == 1)
			{
				glColor3f(0.7f, 0.7f, 0.7f);
				glRectf(j / 10.0f - 1.0f, i / 10.0f - 1.0f, j / 10.0f - 1.0f + 0.1f, i / 10.0f - 1.0f + 0.1f);
				glLineWidth(2.0f);
				glBegin(GL_LINE_LOOP);
				glColor3f(0.0f, 0.0f, 0.0f);
				glVertex2f(j / 10.0f - 1.0f, i / 10.0f - 1.0f);
				glVertex2f(j / 10.0f - 1.0f + 0.1f, i / 10.0f - 1.0f);
				glVertex2f(j / 10.0f - 1.0f + 0.1f, i / 10.0f - 1.0f + 0.1f);
				glVertex2f(j / 10.0f - 1.0f, i / 10.0f - 1.0f + 0.1f);
				glEnd();
				glFlush();
			}
			s++;
		}
	}
	for (i = 1; i<20; i++)/*将已落到底部的方块涂为白色*/
	{
		for (j = 1; j<14; j++)
		{
			if (aiBlock[i][j] == 1)
			{
				glColor3f(1.0f, 1.0f, 1.0f);
				glRectf(j / 10.0f - 1.0f, i / 10.0f - 1.0f, j / 10.0f - 1.0f + 0.1f, i / 10.0f - 1.0f + 0.1f);
				glLineWidth(2.0f);
				glBegin(GL_LINE_LOOP);
				glColor3f(0.0f, 0.0f, 0.0f);
				glVertex2f(j / 10.0f - 1.0f, i / 10.0f - 1.0f);
				glVertex2f(j / 10.0f - 1.0f + 0.1f, i / 10.0f - 1.0f);
				glVertex2f(j / 10.0f - 1.0f + 0.1f, i / 10.0f - 1.0f + 0.1f);
				glVertex2f(j / 10.0f - 1.0f, i / 10.0f - 1.0f + 0.1f);
				glEnd();
				glFlush();
			}
			s++;
		}
	}
	if (iOver == 0)
	{
		for (i = 0; i < 4; i++)
		{
			/*使方块在下落中可在三种颜色中变化*/
			if (s % 3 == 0)
				glColor3f(1.0, 0.0, 0.0);
			else if (s % 3 == 1)
				glColor3f(0.0, 1.0, 0.0);
			else if (s % 3 == 2)
				glColor3f(0.0, 0.0, 1.0);
			glRectf(afCurLoc[i][0], afCurLoc[i][1], afCurLoc[i][0] + 0.1f, afCurLoc[i][1] + 0.1f);
			glLineWidth(2.0f);
			glBegin(GL_LINE_LOOP);
			glColor3f(0.0f, 0.0f, 0.0f);
			glVertex2f(afCurLoc[i][0], afCurLoc[i][1]);
			glVertex2f(afCurLoc[i][0] + 0.1f, afCurLoc[i][1]);
			glVertex2f(afCurLoc[i][0] + 0.1f, afCurLoc[i][1] + 0.1f);
			glVertex2f(afCurLoc[i][0], afCurLoc[i][1] + 0.1f);
			glEnd();
			glFlush();
		}
	}
	s++;
	glutSwapBuffers();
}
/*随机生成方块,原理即生成一个7以内的随机数,
对应的二维数组即为下一个产生的方块*/
void CreateBlocks(void)
{
	int i, j;
	/*若游戏未结束,则生成下一方块*/
	if (iEndGame == 0)
	{
		//srand(time(NULL));
		BlockDisplay();
		/*若方块落到底部,则将原预览方块信息赋给当前下落方块,
		并随机生成预览方块*/
		if (iOver)
		{
			srand(time(NULL));
			iCurrentBlock = iNextBlock;
			iNextBlock = rand() % 7;
			/*每次创建一个新的方块后要将变形的记录清空*/
			for (i = 0; i < 7; i++)
				aiTurn[i] = 0;
			for (i = 0; i < 4; i++)
				for (j = 0; j < 2; j++)
				{
					afCurLoc[i][j] = afShape[iCurrentBlock][i][j];
				}
			iOver = 0;
			glutPostRedisplay();
		}
	}
}
int main(int argc, char *argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
	glutInitWindowPosition(400, 0);/*设置窗口在屏幕中的位置*/
	glutInitWindowSize(750, 720);/*设置窗口的大小*/
	WelcomeScreen();
	if (iStart)
	{
		InitBlock();/*图形界面绘制*/
		glutCreateWindow("俄罗斯方块");/*创建窗口。参数作为窗口的标题*/
		InitString();/*显示帮助*/
		glutDisplayFunc(&CreateBlocks);/*当需要进行画图时,调用该函数*/
		glutTimerFunc(iTimeS, Down, 1);/*定时下落方块*/
		glutKeyboardFunc(Key);
		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);/*用黑色清除屏幕*/
		glutMainLoop();/*进行一个消息循环*/
	}
	return 0;
}
这篇关于游戏俄罗斯方块(c语言)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!