当前位置:   article > 正文

计算机图形学 期中大作业 俄罗斯方块_计算机图形学 俄罗斯方块

计算机图形学 俄罗斯方块

期中大作业 俄罗斯方块

一、 作业内容

设计一个简化版的俄罗斯方块游戏 。游戏窗口大小是20×10的方形棋盘格阵列(下图左),游戏中出现的方块共有7种(下图右),每种方块的旋转中心由黑点标出 。留空代码“tetris.cpp”文件中针对L形状记录方块布局的数组vec2 allRotationsLshape[4][4]的设定请参看(下图右)中的第五行图示(老师上课的时候也会简单说明一下)。
在这里插入图片描述
具体实现内容和步骤包括:

  1. 方块/棋盘格的渲染和方块向下移动
    创建OpenGL绘制窗口,然后绘制网格线来完成对棋盘格的渲染。随机选择方块并赋上颜色,从窗口最上方中间开始往下自动移动,每次移动一个格子。初始的方块类型和方向也必须随机选择,另外可以通过键盘控制方块向下移动的速度,在方块移动到窗口底部的时候,新的方块出现并重复上述移动过程。
  2. 方块叠加
    不断下落的方块需要能够相互叠加在一起,即不同的方块之间不能相互碰撞和叠加。另外,所有方块移动不能超出窗口的边界。
  3. 键盘控制方块的移动
    通过方向键(上/下/左/右)来控制方块的移动。按“上”键使方块以旋转中心顺(逆)时针旋转,每次旋转90°,按“左”和“右”键分别将方块向左/右方向移动一格,按“下”键加速方块移动。
  4. 游戏逻辑
    当游戏窗口中的任意一行被方块占满,该行即被消除,所有上面的方块向下移动一格子。当整个窗口被占满而不能再出现新的方块时,游戏结束。通过按下“q”键结束游戏,和按下“r”键重新开始游戏。

代码解析

为了实现方块形状和颜色的随机生成,方块的自动下落,以及额外添加的积分和暂停功能所增加的变量:
在这里插入图片描述
根据教程图写出来的方块的形状以及旋转后的位置()
在这里插入图片描述
根据教程图写出来的方块的形状以及旋转后的位置()
在这里插入图片描述
在这里插入图片描述
新增加的颜色:

在这里插入图片描述
我们需要新增一个全局变量二维数组记录每个格子的颜色:
在这里插入图片描述
当改变颜色的时候,同时修改这个格子的颜色

在这里插入图片描述
生成方块时需要做的事:
在这里插入图片描述
初始化游戏界面时,在另一个窗口增添游戏指引:
在这里插入图片描述
初始化时,增添了一步:将所有方块的颜色初始化为黑色
在这里插入图片描述
方块的碰撞检测:
在这里插入图片描述
判断某行是否被填满,如果被填满需要进行一系列的操作:
在这里插入图片描述
当物体运行到窗口底部或者是碰到物体则需要执行以下函数:
在这里插入图片描述
重新启动游戏
在这里插入图片描述
注意键盘响应时,如果处于暂停状态则不触发功能
在这里插入图片描述
在键盘响应部分添加P键的操作:暂停功能
在这里插入图片描述
在这里插入图片描述
在idle函数内增加物块下落的操作
在这里插入图片描述

实验效果

(具体实际效果可以看视频
随机生成方块的形状及颜色,每隔一段时间,都会自然往下掉落,当有一行被填满时,则会自动消除,上面一行的方块将会往下掉一行
在这里插入图片描述

在这里插入图片描述
暂停或者恢复时也会有提示,暂停时按下暂停键则会恢复
在这里插入图片描述
重启游戏后,界面也会全部清空,下落速度恢复到初始状态

完整代码

/*
 * - 本代码仅仅是参考代码,具体要求请参考作业说明,按照顺序逐步完成。
 * - 关于配置OpenGL开发环境、编译运行,请参考第一周实验课程相关文档。
 *
 * - 已实现功能如下:
 * - 1) 绘制棋盘格和‘L’型方块
 * - 2) 键盘左/右/下键控制方块的移动,上键旋转方块
 *
 * - 未实现功能如下:
 * - 1) 随机生成方块并赋上不同的颜色
 * - 2) 方块的自动向下移动
 * - 3) 方块之间的碰撞检测
 * - 4) 棋盘格中每一行填充满之后自动消除
 * - 5) 其他
 *

 * 致谢:参考代码来自于西蒙弗雷泽大学CMPT361-Assignment
 */

#include "include/Angel.h"

#pragma comment(lib, "glew32.lib")

#include <cstdlib>
#include <iostream>

using namespace std;

int starttime;			// 控制方块向下移动时间
int rotation = 0;		// 控制当前窗口中的方块旋转
vec2 tile[4];			// 表示当前窗口中的方块
bool gameover = false;	// 游戏结束控制变量
int xsize = 400;		// 窗口大小(尽量不要变动窗口大小!)
int ysize = 720;
int ColorNum;//颜色数量
int ShapeNum;//形状的数量
int DownTime = 1000;//下落的时间间隔
int nowTime;
int DownNum = 1;//下落了多少个方块
int Point = 0;//积分数量 每消除一行积分加10
bool pause = false;//用来表示游戏是否暂停的变量
int Rank = 1;//游戏难度

// 一个二维数组表示所有可能出现的方块和方向。
//这个数组的设置比较难理解,是根据word文档里面的(下图右)里面的图示而来,老师上课会解释一下,请注意听
vec2 allRotationsLshape[7][4][4] =
{
	//以下按照实验指导的图形顺序
	//1:O型方块
{	{vec2(0, 0), vec2(-1,0), vec2(0, -1), vec2(-1,-1)},	
   {vec2(0, 0), vec2(-1,0), vec2(0, -1), vec2(-1,-1)},   
   {vec2(0, 0), vec2(-1,0), vec2(0, -1), vec2(-1,-1)},    
   {vec2(0, 0), vec2(-1,0), vec2(0, -1), vec2(-1,-1)}  },

   //2:I型方块
{	{vec2(-2, 0), vec2(-1,0), vec2(1, 0), vec2(0,0)},	
	{vec2(0, 1), vec2(0, 0), vec2(0,-1), vec2(0, -2)},   
	{vec2(-2, 0), vec2(-1,0), vec2(1, 0), vec2(0,0)},     
	{vec2(0, 1), vec2(0, 0), vec2(0,-1), vec2(0, -2)}  },

   //3:S型方块
{	{vec2(0, 0), vec2(0,-1), vec2(-1, -1), vec2(1,0)},	
	{vec2(0, 1), vec2(0, 0), vec2(1,0), vec2(1, -1)},    
	{vec2(0, 0), vec2(0,-1), vec2(-1, -1), vec2(1,0)},    
	{vec2(0, 1), vec2(0, 0), vec2(1,0), vec2(1, -1)}  },

   //4:Z型方块
{	{vec2(-1, 0), vec2(0,0), vec2(0, -1), vec2(1,-1)},	
	{vec2(0, 0), vec2(0, -1), vec2(1,0), vec2(1, 1)},    
	{vec2(-1, 0), vec2(0,0), vec2(0, -1), vec2(1,-1)},  
	{vec2(0, 0), vec2(0, -1), vec2(1,0), vec2(1, 1)}  },

   //5:L型方块
{	{vec2(0, 0), vec2(-1,0), vec2(1, 0), vec2(-1,-1)},	
	{vec2(0, 1), vec2(0, 0), vec2(0,-1), vec2(1, -1)},     
	{vec2(1, 1), vec2(-1,0), vec2(0, 0), vec2(1,  0)},   
	{vec2(-1,1), vec2(0, 1), vec2(0, 0), vec2(0, -1)}  },

   //6:J型方块
{	{vec2(-1, 0), vec2(0,0), vec2(1, 0), vec2(1,-1)},	
	{vec2(0, 1), vec2(0, 0), vec2(0,-1), vec2(1, 1)},  
	{vec2(-1, 0), vec2(0,0), vec2(1, 0), vec2(-1,  1)},     
	{vec2(-1,-1), vec2(0, -1), vec2(0, 0), vec2(0, 1)} },

   //7:T型方块
{	{vec2(-1, 0), vec2(0,0), vec2(1, 0), vec2(0,-1)},	
	{vec2(0, -1), vec2(0, 0), vec2(0,1), vec2(1, 0)},    
	{vec2(-1, 0), vec2(0,0), vec2(1, 0), vec2(0,  1)},      
	{vec2(-1,0), vec2(0, -1), vec2(0, 0), vec2(0, 1)}  },

};

// 绘制窗口的颜色变量

vec4 white  = vec4(1.0, 1.0, 1.0, 1.0);
vec4 black  = vec4(0.0, 0.0, 0.0, 1.0); 
vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
vec4 orange = vec4(1.0, 0.5, 0.0, 1.0);
vec4 pink = vec4(1.0, 0.75, 0.8, 1.0);
vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);
vec4 purple = vec4(0.8, 0.0, 0.8, 1.0);
vec4 bluegreen = vec4(0.0, 1.0, 1.0, 1.0);
vec4 CubeColor[] = { red,green,blue,orange,pink,yellow,purple,bluegreen };
// 当前方块的位置(以棋盘格的左下角为原点的坐标系)
vec2 tilepos = vec2(5, 19);

// 布尔数组表示棋盘格的某位置是否被方块填充,即board[x][y] = true表示(x,y)处格子被填充。
// (以棋盘格的左下角为原点的坐标系)
bool board[10][20]; 

//用来记录每个格子的颜色
vec4 BoardColor[10][20];

// 当棋盘格某些位置被方块填充之后,记录这些位置上被填充的颜色
vec4 boardcolours[1200];  //棋盘格 10*20*2 = 400 个三角形,每个三角形3个点,3*400=1200个点的颜色值需要记录

GLuint locxsize;
GLuint locysize;

GLuint vaoIDs[3];
GLuint vboIDs[6];


//
// 修改棋盘格在pos位置的颜色为colour,并且更新对应的VBO
void changecellcolour(vec2 pos, vec4 colour)
{
	BoardColor[int(pos.x)][int(pos.y)] = colour;
	// 每个格子是个正方形,包含两个三角形,总共6个顶点,并在特定的位置赋上适当的颜色
	for (int i = 0; i < 6; i++)
		boardcolours[(int)(6*(10*pos.y + pos.x) + i)] = colour;

	vec4 newcolours[6] = {colour, colour, colour, colour, colour, colour};

	glBindBuffer(GL_ARRAY_BUFFER, vboIDs[3]);

	// 计算偏移量,在适当的位置赋上颜色
	int offset = 6 * sizeof(vec4) * (int)(10*pos.y + pos.x);
	glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(newcolours), newcolours);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}


//
// 当前方块移动或者旋转时,更新VBO
void updatetile()
{
	glBindBuffer(GL_ARRAY_BUFFER, vboIDs[4]);

	// 每个方块包含四个格子
	for (int i = 0; i < 4; i++)
	{
		// 计算格子的坐标值
		GLfloat x = tilepos.x + tile[i].x;
		GLfloat y = tilepos.y + tile[i].y;

		vec4 p1 = vec4(33.0 + (x * 33.0), 33.0 + (y * 33.0), .4, 1); 
		vec4 p2 = vec4(33.0 + (x * 33.0), 66.0 + (y * 33.0), .4, 1);
		vec4 p3 = vec4(66.0 + (x * 33.0), 33.0 + (y * 33.0), .4, 1);
		vec4 p4 = vec4(66.0 + (x * 33.0), 66.0 + (y * 33.0), .4, 1);
		
		// 每个格子包含两个三角形,所以有6个顶点坐标
		vec4 newpoints[6] = {p1, p2, p3, p2, p3, p4};
		glBufferSubData(GL_ARRAY_BUFFER, i*6*sizeof(vec4), 6*sizeof(vec4), newpoints);
	}
	glBindVertexArray(0);
}

//
// 设置当前方块为下一个即将出现的方块。在游戏开始的时候调用来创建一个初始的方块,
// 在游戏结束的时候判断,没有足够的空间来生成新的方块。
void newtile()
{
	//判断最上面两行的中间是否被填满,如果被填满说明无法再生成方块,游戏结束
	int flag = 0;
	for (int i = 18; i < 20; i++) {
		for (int j = 4; j < 7; j++) {
			if (board[j][i] == true)flag = 1;
		}
	}
	if (flag == 1) {
		cout << "游戏结束,请按下R键重新开始游戏,或者按下Q或Esc键退出游戏。"<<endl;
		gameover = true;
		return;
	}
	// 将新方块放于棋盘格的最上行中间位置并设置默认的旋转方向
	tilepos = vec2(5 , 19);
	rotation = 0;
	ShapeNum = (starttime+rand()) % 7;
	for (int i = 0; i < 4; i++)
	{
		tile[i] = allRotationsLshape[ShapeNum][0][i];
	}

	updatetile();

	// 给新方块赋上颜色
	vec4 newcolours[24];
	ColorNum = (starttime+rand()) % 8;
	for (int i = 0; i < 24; i++)
		newcolours[i] = CubeColor[ColorNum];

	glBindBuffer(GL_ARRAY_BUFFER, vboIDs[5]);
	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(newcolours), newcolours);
	glBindBuffer(GL_ARRAY_BUFFER, 0);


	glBindVertexArray(0);
}

//
// 游戏和OpenGL初始化
void init()
{
	std::cout << "欢迎来到俄罗斯方块游戏,接下来我来给您介绍游戏规则:" << endl;
	std::cout << "由小方块组成的不同形状的板块陆续从屏幕上方落下来,它们会自动下落,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。" << endl;
	std::cout << "按键操作:↑键旋转,↓键往下落,←键往左移,→键往右移,P键暂停或恢复游戏,R键重启游戏,Q或ESC键退出游戏。" << endl;
	std::cout << "接下来请操控小方块来获取更多的分数吧!" << endl;
	std::cout << "当前难度:" << Rank << endl;
	std::cout << "积分:" << Point << endl;
	// 初始化棋盘格,包含64个顶点坐标(总共32条线),并且每个顶点一个颜色值
	vec4 gridpoints[64];
	vec4 gridcolours[64];

	// 纵向线(10个格子所以11条线)
	for (int i = 0; i < 11; i++)
	{
		gridpoints[2*i] = vec4((33.0 + (33.0 * i)), 33.0, 0, 1);
		gridpoints[2*i + 1] = vec4((33.0 + (33.0 * i)), 693.0, 0, 1);
		
	}

	// 水平线
	for (int i = 0; i < 21; i++)
	{
		gridpoints[22 + 2*i] = vec4(33.0, (33.0 + (33.0 * i)), 0, 1);
		gridpoints[22 + 2*i + 1] = vec4(363.0, (33.0 + (33.0 * i)), 0, 1);
	}

	// 将所有线赋成白色
	for (int i = 0; i < 64; i++)
		gridcolours[i] = white;
	
	// 初始化棋盘格,并将没有被填充的格子设置成黑色
	vec4 boardpoints[1200];
	for (int i = 0; i < 1200; i++)
		boardcolours[i] = black;

	// 对每个格子,初始化6个顶点,表示两个三角形,绘制一个正方形格子
	for (int i = 0; i < 20; i++)
		for (int j = 0; j < 10; j++)
		{
			vec4 p1 = vec4(33.0 + (j * 33.0), 33.0 + (i * 33.0), .5, 1);
			vec4 p2 = vec4(33.0 + (j * 33.0), 66.0 + (i * 33.0), .5, 1);
			vec4 p3 = vec4(66.0 + (j * 33.0), 33.0 + (i * 33.0), .5, 1);
			vec4 p4 = vec4(66.0 + (j * 33.0), 66.0 + (i * 33.0), .5, 1);

			boardpoints[6*(10*i + j)    ] = p1;
			boardpoints[6*(10*i + j) + 1] = p2;
			boardpoints[6*(10*i + j) + 2] = p3;
			boardpoints[6*(10*i + j) + 3] = p2;
			boardpoints[6*(10*i + j) + 4] = p3;
			boardpoints[6*(10*i + j) + 5] = p4;
		}

	// 将棋盘格所有位置的填充与否都设置为false(没有被填充)
	for (int i = 0; i < 10; i++)
		for (int j = 0; j < 20; j++) {
			board[i][j] = false;
			BoardColor[i][j] = black;
			//将所有方块的颜色初始化为黑色
		}



	// 载入着色器
	GLuint program = InitShader("vshader.glsl", "fshader.glsl");
	glUseProgram(program);

	locxsize = glGetUniformLocation(program, "xsize");
	locysize = glGetUniformLocation(program, "ysize");

	GLuint vPosition = glGetAttribLocation(program, "vPosition");
	GLuint vColor = glGetAttribLocation(program, "vColor");

	glGenVertexArrays(3, &vaoIDs[0]);
	
	// 棋盘格顶点
	glBindVertexArray(vaoIDs[0]);
	glGenBuffers(2, vboIDs);

	// 棋盘格顶点位置
	glBindBuffer(GL_ARRAY_BUFFER, vboIDs[0]);
	glBufferData(GL_ARRAY_BUFFER, 64*sizeof(vec4), gridpoints, GL_STATIC_DRAW);
	glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, 0); 
	glEnableVertexAttribArray(vPosition);
	
	// 棋盘格顶点颜色
	glBindBuffer(GL_ARRAY_BUFFER, vboIDs[1]);
	glBufferData(GL_ARRAY_BUFFER, 64*sizeof(vec4), gridcolours, GL_STATIC_DRAW);
	glVertexAttribPointer(vColor, 4, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(vColor);
	
	// 棋盘格每个格子	
	glBindVertexArray(vaoIDs[1]);
	glGenBuffers(2, &vboIDs[2]);

	// 棋盘格每个格子顶点位置
	glBindBuffer(GL_ARRAY_BUFFER, vboIDs[2]);
	glBufferData(GL_ARRAY_BUFFER, 1200*sizeof(vec4), boardpoints, GL_STATIC_DRAW);
	glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(vPosition);

	// 棋盘格每个格子顶点颜色
	glBindBuffer(GL_ARRAY_BUFFER, vboIDs[3]);
	glBufferData(GL_ARRAY_BUFFER, 1200*sizeof(vec4), boardcolours, GL_DYNAMIC_DRAW);
	glVertexAttribPointer(vColor, 4, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(vColor);
	
	// 当前方块
	glBindVertexArray(vaoIDs[2]);
	glGenBuffers(2, &vboIDs[4]);

	// 当前方块顶点位置
	glBindBuffer(GL_ARRAY_BUFFER, vboIDs[4]);
	glBufferData(GL_ARRAY_BUFFER, 24*sizeof(vec4), NULL, GL_DYNAMIC_DRAW);
	glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(vPosition);

	// 当前方块顶点颜色
	glBindBuffer(GL_ARRAY_BUFFER, vboIDs[5]);
	glBufferData(GL_ARRAY_BUFFER, 24*sizeof(vec4), NULL, GL_DYNAMIC_DRAW);
	glVertexAttribPointer(vColor, 4, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(vColor);

	glBindVertexArray(0);
	glClearColor(0, 0, 0, 0);

	// 游戏初始化
	newtile();
	starttime = glutGet(GLUT_ELAPSED_TIME);
}

//
// 检查在cellpos位置的格子是否被填充或者是否在棋盘格的边界范围内。
bool checkvalid(vec2 cellpos)
{
	if((cellpos.x >=0) && (cellpos.x < 10) && (cellpos.y >= 0)		 //判断是否在窗口范围内
		&& (cellpos.y < 20)&& (board[(int)cellpos.x][(int)cellpos.y]==false))//判断该位置是否已被填充
		return true;
	else
		return false;
}

//
// 判断在棋盘上有足够空间的情况下旋转当前方块
void rotate()
{      
	// 计算得到下一个旋转方向
	int nextrotation = (rotation + 1) % 4;

	// 检查当前旋转之后的位置的有效性
	if (checkvalid((allRotationsLshape[ShapeNum][nextrotation][0]) + tilepos)
		&& checkvalid((allRotationsLshape[ShapeNum][nextrotation][1]) + tilepos)
		&& checkvalid((allRotationsLshape[ShapeNum][nextrotation][2]) + tilepos)
		&& checkvalid((allRotationsLshape[ShapeNum][nextrotation][3]) + tilepos))
	{
		// 更新旋转,将当前方块设置为旋转之后的方块
		rotation = nextrotation;
		for (int i = 0; i < 4; i++)
			tile[i] = allRotationsLshape[ShapeNum][rotation][i];

		updatetile();
	}
}

//
// 检查棋盘格在row行有没有被填充满

//传入某一行,判断这一行是否被填满,如果被填满需要:消除这行,上一行往下掉,增加积分
void checkfullrow(int row)
{
	int flag = 1;
	int i = row;
	for (int j = 0; j < 10; j++) {
		if (board[j][i] == false) {
			flag = 0;//这一行有格子没被填满则说明不能消行
		}
	}
	if (flag == 1) {
		Point += 10;
		std::cout << "积分:" << Point << endl;
		//积分数量每增加50,则难度自动增加,使得方块下落时间变长
		if (Point >= Rank * 20) {
			Rank++;
			DownTime /= 2;//调整下落时间间隔
			DownNum*= 2;//这里让DoneNum*=2是和判断是否下落的方式有关,
			//为了让下落时间间隔和下落数量的乘积与之前相同
			cout << "当前难度:" << Rank<<endl;
		}
		for (int j = 0; j < 10; j++) {
			changecellcolour(vec2(j, i), black);//颜色全部变黑
			board[j][i] = false;//这一行全部变成未填满状态
		}
		for (int k = row + 1; k < 20; k++) {
			for (int j = 0; j < 10; j++) {
				if (board[j][k] == true) {//如果上一行有方块,则需要往下掉一行
					board[j][k] = false;//将上一行置为未填满状态
					changecellcolour(vec2(j, k - 1), BoardColor[j][k]);
					//下一行对应位置的颜色变成上一行对应位置的颜色
					changecellcolour(vec2(j, k), black);//上一行清空为黑色
					board[j][k-1] = true;//下一行对应位置设置为填满状态
				}
			}

		}
	}


}

//
// 放置当前方块,并且更新棋盘格对应位置顶点的颜色VBO

void settile()
{
	// 每个格子
	for (int i = 0; i < 4; i++)
	{
		// 获取格子在棋盘格上的坐标
		int x = (tile[i] + tilepos).x;
		int y = (tile[i] + tilepos).y;
		// 将格子对应在棋盘格上的位置设置为填充
		board[x][y] = true;
		// 并将相应位置的颜色修改
		changecellcolour(vec2(x,y), CubeColor[ColorNum]);
	}
	for (int i = 0; i < 4; i++) {
		int y = (tile[i] + tilepos).y;
		checkfullrow(y);
		//这个物体的四个方块所处的y轴都需要判断是否被填满
	}

}

//
// 给定位置(x,y),移动方块。有效的移动值为(-1,0),(1,0),(0,-1),分别对应于向
// 左,向右和向下移动。如果移动成功,返回值为true,反之为false。
bool movetile(vec2 direction)
{
	// 计算移动之后的方块的位置坐标
	vec2 newtilepos[4];
	for (int i = 0; i < 4; i++)
		newtilepos[i] = tile[i] + tilepos + direction;

	// 检查移动之后的有效性
	if (checkvalid(newtilepos[0]) 
		&& checkvalid(newtilepos[1])
		&& checkvalid(newtilepos[2])
		&& checkvalid(newtilepos[3]))
		{
			// 有效:移动该方块
			tilepos.x = tilepos.x + direction.x;
			tilepos.y = tilepos.y + direction.y;

			updatetile();

			return true;
		}

	return false;
}


//
// 重新启动游戏
void restart()
{
	//先将所有格子清空
	for (int i = 0; i < 10; i++) {
		for (int j = 0; j < 20; j++) {
			board[i][j] = false;
			BoardColor[i][j] = black;
			changecellcolour(vec2(i, j), black);
		}	
	}
	starttime= glutGet(GLUT_ELAPSED_TIME);
	std::cout << "您已重启游戏,游戏还原成初始模样,积分已清零。"<<endl;
	Point = 0;
	std::cout << "积分:" << Point << endl;
	Rank = 1;
	DownTime = 1000;//难度和下落时间重新设立
	DownNum = 0;
	pause = false;//设置游戏为运行状态
	gameover = false;//结束游戏的标志置为false
	newtile();
}

//
// 游戏渲染部分
void display()
{
	glClear(GL_COLOR_BUFFER_BIT);

	glUniform1i(locxsize, xsize);
	glUniform1i(locysize, ysize);

	glBindVertexArray(vaoIDs[1]);
	glDrawArrays(GL_TRIANGLES, 0, 1200); // 绘制棋盘格 (10*20*2 = 400 个三角形)

	glBindVertexArray(vaoIDs[2]);
	glDrawArrays(GL_TRIANGLES, 0, 24);	 // 绘制当前方块 (8 个三角形)

	glBindVertexArray(vaoIDs[0]);
	glDrawArrays(GL_LINES, 0, 64);		 // 绘制棋盘格的线


	glutSwapBuffers();
}


//
// 在窗口被拉伸的时候,控制棋盘格的大小,使之保持固定的比例。
void reshape(GLsizei w, GLsizei h)
{
	xsize = w;
	ysize = h;
	glViewport(0, 0, w, h);
}


//
// 键盘响应事件中的特殊按键响应
void special(int key, int x, int y)
{
	if(!gameover&&pause==false)
	{
		switch(key)
		{
			case GLUT_KEY_UP:	// 向上按键旋转方块
				rotate();
				break;
			case GLUT_KEY_DOWN: // 向下按键移动方块
				if (!movetile(vec2(0, -1)))
				{
					settile();
					newtile();
				}
				break;
			case GLUT_KEY_LEFT:  // 向左按键移动方块
				movetile(vec2(-1, 0));
				break;
			case GLUT_KEY_RIGHT: // 向右按键移动方块
				movetile(vec2(1, 0));
				break;
		}
	}
}


//
// 键盘响应时间中的普通按键响应
void keyboard(unsigned char key, int x, int y)
{
	switch(key) 
	{
		case 'p'://  'p' 键暂停游戏
			if (pause == false) {
				pause = true;
				std::cout << "您已暂停游戏" << endl;
			}
			else if (pause == true) {
				pause = false;
				std::cout << "您已恢复游戏" << endl;
			}
			break;
		case 033: // ESC键 和 'q' 键退出游戏
			std::cout << "您已退出游戏,欢迎下次再来玩。" << endl;
			exit(EXIT_SUCCESS);
			break;
		case 'q':
			std::cout << "您已退出游戏,欢迎下次再来玩。" << endl;
			exit (EXIT_SUCCESS);
			break;
		case 'r': // 'r' 键重启游戏
			restart();
			break;

	}
	glutPostRedisplay();
}

//

void idle(void)
{
	//idle函数会不断地执行,时间也会不断地增加,
	//因此在这里加入物块下落的操作
	nowTime = glutGet(GLUT_ELAPSED_TIME);

	//由于时间不断的增加,我们用时间是否大于下落数量乘以
	//下落间隔来判断是否要进行下落操作
	if (nowTime-starttime> DownNum * DownTime) {
		DownNum++;
		if (gameover == false&&pause==false) {
			if (!movetile(vec2(0, -1)))
			{
				settile();
				newtile();
			}
		}
	}
	glutPostRedisplay();
}

//

int main(int argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
	glutInitWindowSize(xsize, ysize);
	glutInitWindowPosition(680, 178);
	glutCreateWindow("Mid-Term-Skeleton-Code");
	glewInit();
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutSpecialFunc(special);
	glutKeyboardFunc(keyboard);
	glutIdleFunc(idle);

	glutMainLoop();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536
  • 537
  • 538
  • 539
  • 540
  • 541
  • 542
  • 543
  • 544
  • 545
  • 546
  • 547
  • 548
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • 556
  • 557
  • 558
  • 559
  • 560
  • 561
  • 562
  • 563
  • 564
  • 565
  • 566
  • 567
  • 568
  • 569
  • 570
  • 571
  • 572
  • 573
  • 574
  • 575
  • 576
  • 577
  • 578
  • 579
  • 580
  • 581
  • 582
  • 583
  • 584
  • 585
  • 586
  • 587
  • 588
  • 589
  • 590
  • 591
  • 592
  • 593
  • 594
  • 595
  • 596
  • 597
  • 598
  • 599
  • 600
  • 601
  • 602
  • 603
  • 604
  • 605
  • 606
  • 607
  • 608
  • 609
  • 610
  • 611
  • 612
  • 613
  • 614
  • 615
  • 616
  • 617
  • 618
  • 619
  • 620
  • 621
  • 622
  • 623
  • 624
  • 625
  • 626
  • 627
  • 628
  • 629
  • 630
  • 631
  • 632
  • 633
  • 634
  • 635
  • 636
  • 637
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/605085
推荐阅读
相关标签
  

闽ICP备14008679号