目录
实验目的与要求
实验过程及内容
实验结论
实验代码
1.创建多种方块类型
2.添加变量记录方块类型。
3. 更改newtile函数使得每次随机选取方块进行生成,同时更改allRotationsLshape数组。
4.因为更改了allRotationsLshape数组,因此同样需要更改旋转的rotate函数,此时可以看出shapeLike变量的作用是保持旋转前后方块类型不变。
5.运行代码看一下效果,发现确实可以随机生成各种类型的方块。
6.更改标题栏,将窗口标题设为:“学号_姓名_期中大作业”。
7.接下来为每一个类型的方块都赋上不同的颜色,首先添加颜色的种类并将名字存储到数组中。
8.因为是每个方块类型对应一种颜色,因此通过colors数组和shapeLike变量既可以确定一种颜色。
9.运行代码,发现每种类型的方块都对应了一种颜色。
10.在main函数的while循环中添加时钟记录时间,使得每隔一秒钟方块下落一格。
11.完善restart函数,使得按r键可以重新开始游戏。
12. 添加空格键,能够使得方块直接下落到底部。
13. 添加方块间的检测判断,判断是否已经有方块放置。
14.运行代码,此时已经可以叠加堆放了,如图所示。
15.接下来需要实现当一行的格子被放满后,消除一整行格子的操作,首先需要创建一个数组记录每个格子的颜色。
16.在settile函数中对每一个格子所在的行进行检测,看该行是否已满。
17.接下来完善checkfullrow函数,检查棋盘格在row行有没有被填充满,如果没有填满row行则返回函数,否则消去那一行,并将其上的格子下移一格。
从被消除的行开始向上判断,若上面一行有格子,则复制到当前行,否则填充黑色。
18.运行代码,发现其成功消除满了的行。
19.其实到这里游戏的基础功能就已经基本做好了,加下来需要考虑的是优化人机交互界面即UI设计,以及游戏难易度调整,记分以及按键功能提示等等。
首先先加上一些欢迎语以及按键功能提示。
20.添加记分板功能。
21.补充上一个游戏结束判断条件,即在生成方块时判断是否可以成功生成,如果不能则游戏结束。
注意坐标转换:
22.添加激励机制,即记录历史最高得分,以提高玩家游玩时长。
21. 添加梯度难度设计,消除方块后下落速度提高。
通过俄罗斯方块的实验,学到了一些与编程和游戏开发相关的重要知识和技能。
游戏开发技能:通过编写俄罗斯方块游戏,熟悉了游戏开发的基本概念,包括游戏循环、用户输入处理、图形渲染等。了解了如何将理论知识转化为实际的游戏项目。
图形编程:俄罗斯方块游戏需要绘制图形,包括方块的渲染和窗口的管理。加深了对OpenGL和图形编程的基本原理的理解。
游戏设计思维:俄罗斯方块实验提高了设计游戏的思维。了解了游戏难度平衡、用户体验、界面设计等方面的原则。
在实验中也有遇到一些小bug:
1.伪随机生成
每次运行生成的图案顺序都一样。后来发现是因为随机数发生器的种子没有被设置,导致它使用了默认种子,从而产生相同的随机序列。
添加srand(time(0));设置随机数种子后即可实现真正的伪随机。
2.遇到标题栏中文乱码问题
产生以下乱码
添加#pragma execution_character_set("utf-8"),这是用于告诉编译器源代码文件中使用的字符集是UTF-8。可以确保你的源代码中的字符串文字被正确解释为UTF-8 编码。在编译程序时,编译器将使用UTF-8字符集来解释字符串文字,以便在程序运行时以UTF-8编码的形式进行处理。
添加后可正常输出中文,如图所示
3.方块瞬间落至底部
本来是想着每隔一秒让方块下落一格,结果却是每过一秒方块直接瞬闪到最底部。
打上断点进行测试,发现单步测试的时候方块是一格一格下落的。那么方块瞬移的原因应该是在这一秒内瞬间叠加了多次下移操作,即多次运行了movetile(glm::vec2(0, -1)。仔细分析movetile函数后发现,它是到底部才会返回false,所以这个if就成了相判断其是否到了底部,只有到了底部才会更新时间,而不是我所设想的下移一格更新一次时间。既然如此,将更新时间的语句移到if语句外即可。
期中大作业——main.cpp
/*
* Computer Graphics Course - Shenzhen University
* Mid-term Assignment - Tetris implementation sample code
* ============================================================
*
* - 本代码仅仅是参考代码,具体要求请参考作业说明,按照顺序逐步完成。
* - 关于配置OpenGL开发环境、编译运行,请参考第一周实验课程相关文档。
*
* - 已实现功能如下:
* - 1) 绘制棋盘格和‘L’型方块
* - 2) 键盘左/右/下键控制方块的移动,上键旋转方块
*
* - 未实现功能如下:
* - 1) 绘制‘J’、‘Z’等形状的方块
* - 2) 随机生成方块并赋上不同的颜色
* - 3) 方块的自动向下移动
* - 4) 方块之间、方块与边界之间的碰撞检测
* - 5) 棋盘格中每一行填充满之后自动消除
* - 6) 其他
*
* 实现功能:
* 1.添加了多种方块类型
* 2.为每种方块颜色添加了颜色
* 3.实现了每次随机生成方块类型
* 4.更改了标题栏,处理了中文乱码问题
* 5.使得每隔一秒方块下落一格
* 6.完善restart函数,使得按r键可以重新开始游戏
* 7.添加空格键,能够使得方块直接下落到底部
* 8.消除满了的行并下移上面的格子
* 9.优化UI设计,添加游玩说明、按键说明等
* 10.添加记分板,消除后实时更新分数
* 11.添加梯度难度设计,消除方块后下落速度提高
*/
#include "Angel.h"
#include
#include
#include
#include
#include
using namespace std;
int starttime; // 控制方块向下移动时间
int rotation = 0; // 控制当前窗口中的方块旋转
glm::vec2 tile[4]; // 表示当前窗口中的方块
bool gameover = false; // 游戏结束控制变量
int xsize = 400; // 窗口大小(尽量不要变动窗口大小!)
int ysize = 720;
int shapeLike = 0; //方块类型
int score = 0; //记分板
float lowSpeed = -1; //设置下落速度
int maxScore = 0; //记录历史最高得分
// 单个网格大小
int tile_width = 33;
// 网格布大小
const int board_width = 10;
const int board_height = 20;
// 网格三角面片的顶点数量
const int points_num = board_height * board_width * 6;
// 我们用画直线的方法绘制网格
// 包含竖线 board_width+1 条
// 包含横线 board_height+1 条
// 一条线2个顶点坐标
// 网格线的数量
const int board_line_num = (board_width + 1) + (board_height + 1);
//存储方块类型的数组
glm::vec2 allRotationsLshape[7][4][4] = {
{ // O型
{glm::vec2(0, 0), glm::vec2(-1, 0), glm::vec2(0, -1), glm::vec2(-1, -1)},
{glm::vec2(0, 0), glm::vec2(-1, 0), glm::vec2(0, -1), glm::vec2(-1, -1)},
{glm::vec2(0, 0), glm::vec2(-1, 0), glm::vec2(0, -1), glm::vec2(-1, -1)},
{glm::vec2(0, 0), glm::vec2(-1, 0), glm::vec2(0, -1), glm::vec2(-1, -1)}},
{ // I型
{glm::vec2(-2, 0), glm::vec2(-1, 0), glm::vec2(1, 0), glm::vec2(0, 0)},
{glm::vec2(0, 1), glm::vec2(0, 0), glm::vec2(0, -1), glm::vec2(0, -2)},
{glm::vec2(-2, 0), glm::vec2(-1, 0), glm::vec2(1, 0), glm::vec2(0, 0)},
{glm::vec2(0, 1), glm::vec2(0, 0), glm::vec2(0, -1), glm::vec2(0, -2)}},
{ // S型
{glm::vec2(0, 0), glm::vec2(0, -1), glm::vec2(-1, -1), glm::vec2(1, 0)},
{glm::vec2(0, 1), glm::vec2(0, 0), glm::vec2(1, 0), glm::vec2(1, -1)},
{glm::vec2(0, 0), glm::vec2(0, -1), glm::vec2(-1, -1), glm::vec2(1, 0)},
{glm::vec2(0, 1), glm::vec2(0, 0), glm::vec2(1, 0), glm::vec2(1, -1)}},
{ // Z型
{glm::vec2(-1, 0), glm::vec2(0, 0), glm::vec2(0, -1), glm::vec2(1, -1)},
{glm::vec2(0, 0), glm::vec2(0, -1), glm::vec2(1, 0), glm::vec2(1, 1)},
{glm::vec2(-1, 0), glm::vec2(0, 0), glm::vec2(0, -1), glm::vec2(1, -1)},
{glm::vec2(0, 0), glm::vec2(0, -1), glm::vec2(1, 0), glm::vec2(1, 1)}},
{ // L型
{glm::vec2(0, 0), glm::vec2(-1, 0), glm::vec2(1, 0), glm::vec2(-1, -1)},
{glm::vec2(0, 1), glm::vec2(0, 0), glm::vec2(0, -1), glm::vec2(1, -1)},
{glm::vec2(1, 1), glm::vec2(-1, 0), glm::vec2(0, 0), glm::vec2(1, 0)},
{glm::vec2(-1, 1), glm::vec2(0, 1), glm::vec2(0, 0), glm::vec2(0, -1)}},
{ // J型
{glm::vec2(-1, 0), glm::vec2(0, 0), glm::vec2(1, 0), glm::vec2(1, -1)},
{glm::vec2(0, 1), glm::vec2(0, 0), glm::vec2(0, -1), glm::vec2(1, 1)},
{glm::vec2(-1, 0), glm::vec2(0, 0), glm::vec2(1, 0), glm::vec2(-1, 1)},
{glm::vec2(-1, -1), glm::vec2(0, -1), glm::vec2(0, 0), glm::vec2(0, 1)}},
{ // T型
{glm::vec2(-1, 0), glm::vec2(0, 0), glm::vec2(1, 0), glm::vec2(0, -1)},
{glm::vec2(0, -1), glm::vec2(0, 0), glm::vec2(0, 1), glm::vec2(1, 0)},
{glm::vec2(-1, 0), glm::vec2(0, 0), glm::vec2(1, 0), glm::vec2(0, 1)},
{glm::vec2(-1, 0), glm::vec2(0, -1), glm::vec2(0, 0), glm::vec2(0, 1)}},
};
// 绘制窗口的颜色变量
glm::vec4 white = glm::vec4(1.0, 1.0, 1.0, 1.0);
glm::vec4 black = glm::vec4(0.0, 0.0, 0.0, 1.0);
glm::vec4 orange = glm::vec4(1.0, 0.5, 0.0, 1.0);
glm::vec4 red = glm::vec4(1.0, 0.0, 0.0, 1.0);
glm::vec4 green = glm::vec4(0.0, 1.0, 0.0, 1.0);
glm::vec4 blue = glm::vec4(0.0, 0.0, 1.0, 1.0);
glm::vec4 yellow = glm::vec4(1.0, 1.0, 0.0, 1.0);
glm::vec4 purple = glm::vec4(0.5, 0.0, 0.5, 1.0);
glm::vec4 cyan = glm::vec4(0.0, 1.0, 1.0, 1.0);
glm::vec4 colors[] = { orange,red,green,blue,yellow,purple,cyan };
// 当前方块的位置(以棋盘格的左下角为原点的坐标系)
glm::vec2 tilepos = glm::vec2(5, 19);
// 布尔数组表示棋盘格的某位置是否被方块填充,即board[x][y] = true表示(x,y)处格子被填充。
// (以棋盘格的左下角为原点的坐标系)
bool board[board_width][board_height];
// 当棋盘格某些位置被方块填充之后,记录这些位置上被填充的颜色
glm::vec4 board_colours[points_num];
//记录每个格子的颜色
glm::vec4 posColors[board_width][board_height];
GLuint locxsize;
GLuint locysize;
GLuint vao[3];
GLuint vbo[6];
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// 修改棋盘格在pos位置的颜色为colour,并且更新对应的VBO
void changecellcolour(glm::vec2 pos, glm::vec4 colour)
{
// 每个格子是个正方形,包含两个三角形,总共6个定点,并在特定的位置赋上适当的颜色
for (int i = 0; i < 6; i++)
board_colours[(int)(6 * (board_width * pos.y + pos.x) + i)] = colour;
glm::vec4 newcolours[6] = { colour, colour, colour, colour, colour, colour };
glBindBuffer(GL_ARRAY_BUFFER, vbo[3]);
// 计算偏移量,在适当的位置赋上颜色
int offset = 6 * sizeof(glm::vec4) * (int)(board_width * pos.y + pos.x);
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(newcolours), newcolours);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//记录每个格子的颜色
posColors[(int)pos.x][(int)pos.y] = colour;
}
// 当前方块移动或者旋转时,更新VBO
void updatetile()
{
glBindBuffer(GL_ARRAY_BUFFER, vbo[4]);
// 每个方块包含四个格子
for (int i = 0; i < 4; i++)
{
// 计算格子的坐标值
GLfloat x = tilepos.x + tile[i].x;
GLfloat y = tilepos.y + tile[i].y;
glm::vec4 p1 = glm::vec4(tile_width + (x * tile_width), tile_width + (y * tile_width), .4, 1);
glm::vec4 p2 = glm::vec4(tile_width + (x * tile_width), tile_width * 2 + (y * tile_width), .4, 1);
glm::vec4 p3 = glm::vec4(tile_width * 2 + (x * tile_width), tile_width + (y * tile_width), .4, 1);
glm::vec4 p4 = glm::vec4(tile_width * 2 + (x * tile_width), tile_width * 2 + (y * tile_width), .4, 1);
// 每个格子包含两个三角形,所以有6个顶点坐标
glm::vec4 newpoints[6] = { p1, p2, p3, p2, p3, p4 };
glBufferSubData(GL_ARRAY_BUFFER, i * 6 * sizeof(glm::vec4), 6 * sizeof(glm::vec4), newpoints);
}
glBindVertexArray(0);
}
// 检查在cellpos位置的格子是否被填充或者是否在棋盘格的边界范围内
bool checkvalid(glm::vec2 cellpos)
{
if ((cellpos.x >= 0) && (cellpos.x < board_width) && (cellpos.y >= 0) && (cellpos.y < board_height)
&& board[(int)cellpos.x][(int)cellpos.y] == false) //添加方块间的检测判断,判断是否已经有方块放置
return true;
else
return false;
}
// 设置当前方块为下一个即将出现的方块。在游戏开始的时候调用来创建一个初始的方块,
// 在游戏结束的时候判断,没有足够的空间来生成新的方块。
void newtile()
{
// 将新方块放于棋盘格的最上行中间位置并设置默认的旋转方向
tilepos = glm::vec2(5, 19);
rotation = 0;
srand(time(0));
shapeLike = rand() % 7;
for (int i = 0; i < 4; i++)
{
tile[i] = allRotationsLshape[shapeLike][0][i];
// 检测是否有位置放置新方块
if (!checkvalid(glm::vec2((int)tile[i].x + board_width / 2, (int)tile[i].y + board_height - 1))) {
if (score > maxScore)
maxScore = score;
system("cls");
cout << "游戏结束!\n你的分数为:" << score << endl;
cout << "当前最高分为:" << maxScore << endl;
cout << "请按q退出游戏或按r重新开始游戏" << endl;
return;
}
}
updatetile();
// 给新方块赋上颜色
glm::vec4 newcolours[24];
for (int i = 0; i < 24; i++)
newcolours[i] = colors[shapeLike];
glBindBuffer(GL_ARRAY_BUFFER, vbo[5]);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(newcolours), newcolours);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void welcome() {
cout << "欢迎来到俄罗斯方块,祝您玩的愉快!" << endl << endl;
cout << "游戏玩法:通过调整下落的随机方块,使其填充满一行后消除获得分数,分数越高下落速度越快哦" << endl << endl;
cout << "按键说明:使用上键旋转方块\n 使用左或右键移动方块\n 使用下键加速方块下落\n 使用空格键下落方块至底部\n";
cout << " 使用q键或Esc键退出游戏\n 使用r键重新开始游戏\n" << endl;
cout << "当前下落速度:" << lowSpeed << endl;
cout << "当前分数:" << score << endl;
}
// 游戏和OpenGL初始化
void init()
{
//分数初始化为0
score = 0;
//下降速度初始化为-1
lowSpeed = -1;
//输出提示语
welcome();
// 初始化棋盘格,这里用画直线的方法绘制网格
// 包含竖线 board_width+1 条
// 包含横线 board_height+1 条
// 一条线2个顶点坐标,并且每个顶点一个颜色值
glm::vec4 gridpoints[board_line_num * 2];
glm::vec4 gridcolours[board_line_num * 2];
// 绘制网格线
// 纵向线
for (int i = 0; i < (board_width + 1); i++)
{
gridpoints[2 * i] = glm::vec4((tile_width + (tile_width * i)), tile_width, 0, 1);
gridpoints[2 * i + 1] = glm::vec4((tile_width + (tile_width * i)), (board_height + 1) * tile_width, 0, 1);
}
// 水平线
for (int i = 0; i < (board_height + 1); i++)
{
gridpoints[2 * (board_width + 1) + 2 * i] = glm::vec4(tile_width, (tile_width + (tile_width * i)), 0, 1);
gridpoints[2 * (board_width + 1) + 2 * i + 1] = glm::vec4((board_width + 1) * tile_width, (tile_width + (tile_width * i)), 0, 1);
}
// 将所有线赋成白色
for (int i = 0; i < (board_line_num * 2); i++)
gridcolours[i] = white;
// 初始化棋盘格,并将没有被填充的格子设置成黑色
glm::vec4 boardpoints[points_num];
for (int i = 0; i < points_num; i++)
board_colours[i] = black;
// 对每个格子,初始化6个顶点,表示两个三角形,绘制一个正方形格子
for (int i = 0; i < board_height; i++)
for (int j = 0; j < board_width; j++)
{
glm::vec4 p1 = glm::vec4(tile_width + (j * tile_width), tile_width + (i * tile_width), .5, 1);
glm::vec4 p2 = glm::vec4(tile_width + (j * tile_width), tile_width * 2 + (i * tile_width), .5, 1);
glm::vec4 p3 = glm::vec4(tile_width * 2 + (j * tile_width), tile_width + (i * tile_width), .5, 1);
glm::vec4 p4 = glm::vec4(tile_width * 2 + (j * tile_width), tile_width * 2 + (i * tile_width), .5, 1);
boardpoints[6 * (board_width * i + j) + 0] = p1;
boardpoints[6 * (board_width * i + j) + 1] = p2;
boardpoints[6 * (board_width * i + j) + 2] = p3;
boardpoints[6 * (board_width * i + j) + 3] = p2;
boardpoints[6 * (board_width * i + j) + 4] = p3;
boardpoints[6 * (board_width * i + j) + 5] = p4;
}
// 将棋盘格所有位置的填充与否都设置为false(没有被填充)
for (int i = 0; i < board_width; i++)
for (int j = 0; j < board_height; j++) {
board[i][j] = false;
posColors[i][j] = black;
}
// 载入着色器
std::string vshader, fshader;
vshader = "shaders/vshader.glsl";
fshader = "shaders/fshader.glsl";
GLuint program = InitShader(vshader.c_str(), fshader.c_str());
glUseProgram(program);
locxsize = glGetUniformLocation(program, "xsize");
locysize = glGetUniformLocation(program, "ysize");
GLuint vPosition = glGetAttribLocation(program, "vPosition");
GLuint vColor = glGetAttribLocation(program, "vColor");
glGenVertexArrays(3, &vao[0]);
glBindVertexArray(vao[0]); // 棋盘格顶点
glGenBuffers(2, vbo);
// 棋盘格顶点位置
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, (board_line_num * 2) * sizeof(glm::vec4), gridpoints, GL_STATIC_DRAW);
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vPosition);
// 棋盘格顶点颜色
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, (board_line_num * 2) * sizeof(glm::vec4), gridcolours, GL_STATIC_DRAW);
glVertexAttribPointer(vColor, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vColor);
glBindVertexArray(vao[1]); // 棋盘格每个格子
glGenBuffers(2, &vbo[2]);
// 棋盘格每个格子顶点位置
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, points_num * sizeof(glm::vec4), boardpoints, GL_STATIC_DRAW);
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vPosition);
// 棋盘格每个格子顶点颜色
glBindBuffer(GL_ARRAY_BUFFER, vbo[3]);
glBufferData(GL_ARRAY_BUFFER, points_num * sizeof(glm::vec4), board_colours, GL_DYNAMIC_DRAW);
glVertexAttribPointer(vColor, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vColor);
glBindVertexArray(vao[2]); // 当前方块
glGenBuffers(2, &vbo[4]);
// 当前方块顶点位置
glBindBuffer(GL_ARRAY_BUFFER, vbo[4]);
glBufferData(GL_ARRAY_BUFFER, 24 * sizeof(glm::vec4), NULL, GL_DYNAMIC_DRAW);
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vPosition);
// 当前方块顶点颜色
glBindBuffer(GL_ARRAY_BUFFER, vbo[5]);
glBufferData(GL_ARRAY_BUFFER, 24 * sizeof(glm::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);
}
// 在棋盘上有足够空间的情况下旋转当前方块
void rotate()
{
// 计算得到下一个旋转方向
int nextrotation = (rotation + 1) % 4;
// 检查当前旋转之后的位置的有效性
if (checkvalid((allRotationsLshape[shapeLike][nextrotation][0]) + tilepos)
&& checkvalid((allRotationsLshape[shapeLike][nextrotation][1]) + tilepos)
&& checkvalid((allRotationsLshape[shapeLike][nextrotation][2]) + tilepos)
&& checkvalid((allRotationsLshape[shapeLike][nextrotation][3]) + tilepos))
{
// 更新旋转,将当前方块设置为旋转之后的方块
rotation = nextrotation;
for (int i = 0; i < 4; i++)
tile[i] = allRotationsLshape[shapeLike][rotation][i];
updatetile();
}
}
// 检查棋盘格在row行有没有被填充满
void checkfullrow(int row)
{
//如果没有填满row行则返回函数
int i;
for (i = 0; i < board_width; i++) {
if (board[i][row] == false)
return;
}
//消除成功,进行加分
score += 10;
lowSpeed -= 0.05; //加快降落速度
system("cls");
welcome();
//否则消去那一行,并将其上的格子下移一格
for (int j = row; j < board_height; j++) { //从被消除的行开始向上判断
for (i = 0; i < board_width; i++) {
if (j + 1 < board_height && board[i][j + 1] == true) {
//若上面一行有格子,则复制到当前行
changecellcolour(glm::vec2(i, j), posColors[i][j + 1]);
board[i][j] = true;
}
else { //否则填充黑色
changecellcolour(glm::vec2(i, j), black);
board[i][j] = false;
}
}
}
}
// 放置当前方块,并且更新棋盘格对应位置顶点的颜色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(glm::vec2(x, y), colors[shapeLike]);
}
//对每一个格子所在的行进行检测,看该行是否已满
for (int i = 0; i < 4; i++)
{
// 获取格子在棋盘格上的纵坐标并进行检测
int y = (tile[i] + tilepos).y;
checkfullrow(y);
}
}
// 给定位置(x,y),移动方块。有效的移动值为(-1,0),(1,0),(0,-1),分别对应于向
// 左,向下和向右移动。如果移动成功,返回值为true,反之为false
bool movetile(glm::vec2 direction)
{
// 计算移动之后的方块的位置坐标
glm::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()
{
system("cls"); //清除终端内容
cout << "游戏已重新开始!\n期待您再次大放异彩!!!" << endl << endl;
init();
}
// 游戏渲染部分
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glUniform1i(locxsize, xsize);
glUniform1i(locysize, ysize);
glBindVertexArray(vao[1]);
glDrawArrays(GL_TRIANGLES, 0, points_num); // 绘制棋盘格 (width * height * 2 个三角形)
glBindVertexArray(vao[2]);
glDrawArrays(GL_TRIANGLES, 0, 24); // 绘制当前方块 (8 个三角形)
glBindVertexArray(vao[0]);
glDrawArrays(GL_LINES, 0, board_line_num * 2); // 绘制棋盘格的线
}
// 在窗口被拉伸的时候,控制棋盘格的大小,使之保持固定的比例。
void reshape(GLsizei w, GLsizei h)
{
xsize = w;
ysize = h;
glViewport(0, 0, w, h);
}
// 键盘响应事件中的特殊按键响应
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (!gameover)
{
switch (key)
{
// 控制方块的移动方向,更改形态
case GLFW_KEY_UP: // 向上按键旋转方块
if (action == GLFW_PRESS || action == GLFW_REPEAT)
{
rotate();
break;
}
else
{
break;
}
case GLFW_KEY_DOWN: // 向下按键移动方块
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
if (!movetile(glm::vec2(0, -1)))
{
settile();
newtile();
break;
}
else
{
break;
}
}
case GLFW_KEY_LEFT: // 向左按键移动方块
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
movetile(glm::vec2(-1, 0));
break;
}
else
{
break;
}
case GLFW_KEY_RIGHT: // 向右按键移动方块
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
movetile(glm::vec2(1, 0));
break;
}
else
{
break;
}
// 游戏设置。
case GLFW_KEY_ESCAPE:
if (action == GLFW_PRESS) {
exit(EXIT_SUCCESS);
break;
}
else
{
break;
}
case GLFW_KEY_Q:
if (action == GLFW_PRESS) {
exit(EXIT_SUCCESS);
break;
}
else
{
break;
}
case GLFW_KEY_R:
if (action == GLFW_PRESS) {
restart();
break;
}
else
{
break;
}
case GLFW_KEY_SPACE: // 空格键,快速下降到底部
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
while (movetile(glm::vec2(0, -1))) {
// 继续下移方块
}
// 下移到底部后,设置方块并生成新方块
settile();
newtile();
}
break;
}
}
}
int main(int argc, char** argv)
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// 创建窗口
//设置字符格式
#pragma execution_character_set("utf-8");
GLFWwindow* window = glfwCreateWindow(500, 900, "2021150047_hyf_期中大作业", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window!" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetKeyCallback(window, key_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
init();
int flag = clock();
while (!glfwWindowShouldClose(window))
{
display();
glfwSwapBuffers(window);
glfwPollEvents();
//使得每隔一秒方块下落一格
int now = clock(); //记录当前时间
if (now - flag >= 1000) //相差1s
{
if (!movetile(glm::vec2(0, lowSpeed))) //向下移动,若下移失败则执行下面部分
{
settile(); //放置在底部
newtile(); //新建方块
}
flag = now; //更新当前时间
}
}
glfwTerminate();
return 0;
}
(by 归忆)