为了方便在其它程序中测试,将方块的功能包装成了一个类,下面是头文件Tetris.h的内容
欢迎测试并提出宝贵意见,其中有个bug不知到怎么回事,有时落下的方块没显示
//俄罗斯方块C++语言源代码,vs2019编译通过
#pragma once
#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <string.h>
#include <math.h>
//游戏区大小
#define ROW 29 //游戏区行数,建议不小于29,否则乱屏
#define COL 20 //游戏区列数
//键码
#define SPACE 32 //空格键
#define ESC 27 //Esc键
//四个方向键控制方块姿态,方便习惯右手操作的用户
#define LEFT 75
#define RIGHT 77
#define DOWN 80
#define UP 72 //用于旋转方块
//其它
#define LenofName 11
struct Face
{
int data[ROW][COL + 20] = { 0 }; //用于标记指定位置是否有方块(1为有,0为无)
int color[ROW][COL + 20] = { 0 }; //用于记录指定位置的方块颜色编码
};
struct Block//用于存储7种基本形状方块的各自的4种形态的信息,共28种
{
int space[4][4] = { 0 };
};
//游戏得分数据
typedef struct Data {
char user[LenofName];
int hisScore;
int hisGrade;//写完了发现这个数没意义,因为可以通过hisScore计算得到
int max;
Face history;
}DATA;
class Tetris {
private:
int lines;//记录消去的行数,控制得分权值
int disScore;//屏显分数
int forceGrade;//强制等级
int grade;//等级
int difficulty;//下落刷新时间,单位ms
Face face;
Block block[7][4];
DATA data;
//设置方块大小
void SetFont(int size = 20);
//重新开始的清理工作
void Restart();
//配置运行环境
void ConfigEnvironment();
//光标跳转
void CursorJump(int x, int y);
//初始化界面
void InitInterface();
//初始化方块信息
void InitBlockInfo();
//颜色设置
void color(int num);
//画出方块
void DrawBlock(int shape, int form, int x, int y);
//空格覆盖
void DrawSpace(int shape, int form, int x, int y);
//合法性判断
int IsLegal(int shape, int form, int x, int y);
//判断得分与结束
int JudeFunc();
//游戏记录
DATA ReadGrade();
//更新最高分到文件
int WriteGrade(DATA);
//根据得分调整下落速度
int AdjustDifficulty();
//刷新游戏灯牌
void Display();
public:
Tetris();
int StartGame();
};
类中函数的实现部分,文件名Tetris.app
#include "Tetris.h"
#pragma warning (disable:4996) //消除警告,兼容旧版本库文件
Tetris::Tetris() {//通过构造函数完成初始化
lines = 1;
disScore =0;
forceGrade = 0;
data = ReadGrade();
grade = data.hisGrade;//延续上次的等级
difficulty = AdjustDifficulty();//重新计算下落时间
ConfigEnvironment();
SetFont();
InitInterface();
InitBlockInfo();
}
//配置运行窗口
void Tetris:: ConfigEnvironment() {
system("chcp 936");//设置utf-8字符集65001,GBK字符集936,如果乱码才需要更改
//设置标题
char cmd[60];
sprintf(cmd, "title 俄罗斯方块:%s", data.user);//含变量的DOS命令,序列化到字符中再传递
system(cmd);
//设置cmd窗口的大小
sprintf(cmd,"mode con lines=%d cols=%d" ,ROW, 2*COL+40);//改变ROW,COL时窗口自动调整大小
system(cmd);
//设置随机数起点,每次启动,游戏会有不同的开局
srand((unsigned int)time(NULL));
//隐藏光标
CONSOLE_CURSOR_INFO curInfo; //光标结构体变量
curInfo.dwSize = 1; //如果没赋值的话,隐藏光标无效
curInfo.bVisible = FALSE; //可见为TRUE
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出句柄
SetConsoleCursorInfo(handle, &curInfo); //设置光标信息
//游戏界面最大化会破坏布局,禁用最大化
HWND hwnd = GetConsoleWindow();
HMENU hmenu = GetSystemMenu(hwnd, false); // 复制或修改而访问窗口菜单
RemoveMenu(hmenu, SC_MAXIMIZE, MF_BYCOMMAND); // 从指定菜单删除一个菜单项或分离一个子菜单
DrawMenuBar(hwnd);//去除最大化菜单
}
//设置字体,一是美观,二是控制方块大小,太小的方块玩起来太累
//虽然可以游戏启动后手动调节字体,但是为了具有良好游戏体验,直接设好吧
void Tetris::SetFont(int size) {//默认大小为20号字,传入参数可以改变字体及方块大小
CONSOLE_FONT_INFOEX cfi;//控制台字体信息的结构体
cfi.cbSize = sizeof(CONSOLE_FONT_INFOEX);
cfi.nFont = 0;
cfi.dwFontSize.X = 0;
cfi.dwFontSize.Y = size; //设置字体大小
cfi.FontFamily = FF_DONTCARE;
cfi.FontWeight = FW_NORMAL; //字体粗细 FW_BOLD
wcscpy_s(cfi.FaceName, L"隶书"); //设置字体,必须是控制台已有的
SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);//根据结构体内容改变字体
}
//重新开始
void Tetris::Restart() {
//清空游戏区方块
for (int i = 0; i < ROW-1; i++) {
for (int j = 2; j < 2*COL - 2; j+=2) {
CursorJump(j, i);
printf(" ");//两个空格填充一个方块
}
}
//清除当前分数
disScore = 0;
forceGrade = 0;
grade = 0;//游戏速度降到最低,不再延续以前的速度继续
CursorJump(2 * COL + 22, 16);
printf(" ");//这里可能写了字,清除掉
Display();//刷新游戏灯牌
//清除已落下的方块
for (int i = 0; i < ROW-1; i++) {
for (int j = 1; j < COL - 1; j++) {
face.data[i][j] = 0;
face.color[i][j] = 0;
}
}
return;
}
//光标跳转
void Tetris::CursorJump(int x, int y)
{
COORD pos; //光标位置的结构体变量
pos.X = x; //横坐标
pos.Y = y; //纵坐标
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台句柄
SetConsoleCursorPosition(handle, pos); //设置光标位置
}
//刷新游戏灯牌
void Tetris:: Display() {
color(7);//白色
int g = grade < forceGrade ? forceGrade:grade ;//高等级优先
CursorJump(2 * COL + 31, 6);//光标要跳到正确位置再修改相应内容
printf("%d", g);
CursorJump(2 * COL + 23, 8);
int i=0;
for (; i <= g; i++) {//显示一个看似酷炫的速度进度条
color(0xae);//被景绿色,前景黄色
printf(">");
}
for (; i <= 9; i++) {
color(0x80);//灰色背景
printf(" ");
}
color(7);//改回白色
CursorJump(2 * COL + 31, 12);
printf("%-5d", disScore);
CursorJump(2 * COL + 31, 14);
difficulty = AdjustDifficulty();
printf("%-4dms", difficulty);
}
//初始化界面
void Tetris::InitInterface()
{
color(0x38); //边框颜色
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL + 20; j++)
{
if (j == 0 || j == COL - 1 || j == COL + 9||j==COL+19)//竖线位置
{
face.data[i][j] = 1; //标记该位置有方块
CursorJump(2 * j, i);
printf("◆");
}
else if (i == ROW - 1)
{
face.data[i][j] = 1;
printf("◆");
}
else
face.data[i][j] = 0;
}
}
for (int i = COL; i < COL + 10; i++)//左栏横线
{
face.data[8][i] = 1;
CursorJump(2 * i, 8);
printf("◆");
}
for (int i = COL+10; i < COL + 20; i++)//右栏横线
{
face.data[18][i] = 1;
CursorJump(2 * i, 18);
printf("◆");
}
color(7);//操作说明文字设置为白色
CursorJump(2 * COL+4, 1);
printf("下一个方块");
CursorJump(2 * COL + 4, ROW - 19);
printf("左移:A←");
CursorJump(2 * COL + 4, ROW - 17);
printf("右移:F→");
CursorJump(2 * COL + 4, ROW - 15);
printf("加速:D ↓");
CursorJump(2 * COL + 4, ROW - 13);
printf("旋转:空格↑");
CursorJump(2 * COL + 4, ROW - 11);
printf("暂停: S");
CursorJump(2 * COL + 4, ROW - 9);
printf("退出: esc");
CursorJump(2 * COL + 4, ROW - 7);
printf("重新开始:R");
CursorJump(2 * COL + 2, ROW-5);
printf("继续上次游戏:C");
CursorJump(2 * COL + 2, ROW-3);
printf("更换新用户名:U");
//游戏灯牌
CursorJump(2 * COL + 22, 2);
printf("直接跳级提速");
CursorJump(2 * COL + 22, 4);
printf("请选择:0--9");
CursorJump(2 * COL + 22, 6);
printf("当前等级:%d",grade);
CursorJump(2 * COL + 22, 10);
printf("最高纪录:%-5d", data.max);//估计玩到大于10万分就觉得没意思了,所以5位数宽度就够了
CursorJump(2 * COL + 22, 12);
printf("当前分数:%-5d", disScore);
CursorJump(2 * COL + 22, 14);
difficulty = AdjustDifficulty();
printf("下落速度:%-4dms", difficulty);
Display();//画一下进度条
//打印游戏说明
CursorJump(2 * COL + 21, 20);
printf("将方块垒整齐得分");
CursorJump(2 * COL + 21, 21);
printf("连续消多行多得分");
CursorJump(2 * COL + 21, 23);
printf("中途保存可按esc");
CursorJump(2 * COL + 21, 24);
printf("游戏结束不会保存");
CursorJump(2 * COL + 21, 25);
printf("根据你的分数速度");
CursorJump(2 * COL + 21, 26);
printf("只能提速不能降速");
}
//初始化方块信息
void Tetris::InitBlockInfo()//7种方块28种形态预先存入数组,供显示使用
{
//“T”形
for (int i = 0; i <= 2; i++)
block[0][0].space[1][i] = 1;
block[0][0].space[2][1] = 1;
//“L”形
for (int i = 1; i <= 3; i++)
block[1][0].space[i][1] = 1;
block[1][0].space[3][2] = 1;
//“J”形
for (int i = 1; i <= 3; i++)
block[2][0].space[i][2] = 1;
block[2][0].space[3][1] = 1;
for (int i = 0; i <= 1; i++)
{
//“Z”形
block[3][0].space[1][i] = 1;
block[3][0].space[2][i + 1] = 1;
//“S”形
block[4][0].space[1][i + 1] = 1;
block[4][0].space[2][i] = 1;
//“O”形
block[5][0].space[1][i + 1] = 1;
block[5][0].space[2][i + 1] = 1;
}
//“I”形
for (int i = 0; i <= 3; i++)
block[6][0].space[i][1] = 1;
int temp[4][4];
for (int shape = 0; shape < 7; shape++) //7种形状
{
for (int form = 0; form < 3; form++) //4种形态,已经有了一种,每个还需增加3种
{
//获取第form种形态
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
temp[i][j] = block[shape][form].space[i][j];
}
}
//将第form种形态顺时针旋转,得到第form+1种形态
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
block[shape][form + 1].space[i][j] = temp[3 - j][i];
}
}
}
}
}
//颜色设置
void Tetris::color(int c)
{//c可以用两位十六进制数表示,高位为背景色,低位为前景色,也可以写转换后的10进制数
switch (c)
{
case 0:
c = 13; //“T”形方块设置为紫色
break;
case 1:
case 2:
c = 12; //“L”形和“J”形方块设置为红色
break;
case 3:
case 4:
c = 10; //“Z”形和“S”形方块设置为绿色
break;
case 5:
c = 14; //“O”形方块设置为黄色
break;
case 6:
c = 11; //“I”形方块设置为浅蓝色
break;
case 7:
c = 7; //文字设置为白色
break;
default:
c=c;//默认根据色码设定
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //调用API设置颜色
}
//画出方块
void Tetris::DrawBlock(int shape, int form, int x, int y)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (block[shape][form].space[i][j] == 1) //如果该位置有方块
{
CursorJump(2 * (x + j), y + i); //光标跳转到指定位置
printf("■"); //输出方块
}
}
}
}
//空格覆盖
void Tetris::DrawSpace(int shape, int form, int x, int y)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (block[shape][form].space[i][j] == 1) //如果该位置有方块
{
CursorJump(2 * (x + j), y + i);
printf(" "); //两个空格占用一个方块的位置
}
}
}
}
//合法性判断
int Tetris::IsLegal(int shape, int form, int x, int y)
{
for (int i = 3; i >=0; i--)//从方块下沿开始判断,可能少循环几次,提高一点点性能
{
for (int j = 0; j <4; j++)
{
//如果方块落下的位置本来就已经有方块了,则不合法
if ((block[shape][form].space[i][j] == 1) && (face.data[y + i][x + j] == 1))
return 0; //不合法
}
}
return 1; //合法
}
//调整游戏难度
int Tetris::AdjustDifficulty() {
int t;//这里t越小,方块下落越快,根据得分调整游戏难度
int score;
if (forceGrade > grade) {//有强制提速
score = disScore % 1000 + forceGrade * 1000;
}
else {//根据等级设置速度,防止速度正反馈
score = disScore % 1000 + grade * 1000;
}
if (score < 9000) {
t = 1000 - sqrt(score)*9.4;//非线性调整
}
else {
t = 100;//极限挑战,只加分不提速,如果感觉难度不算极限,可以改小一点
}
return t;
}
//从文件读取数据
DATA Tetris::ReadGrade() {
DATA d;
FILE* pf = fopen("record.dat", "r"); //以只读方式打开文件
if (pf == NULL) //第一次运行无此文件
{//创建一个具有初始数据的新文件
strcpy(d.user, "古老的经典\0");
d.hisScore = 0;
d.hisGrade = 0;
d.max = 0;
for (int i = 0; i < ROW; i++)//初始化face再保存
{
for (int j = 0; j < COL + 20; j++)
{
if (j == 0 || j == COL - 1 || j == COL + 9 || j == COL + 19)
{
face.data[i][j] = 1; //标记该位置有方块
}
else if (i == ROW - 1)
{
face.data[i][j] = 1;//最下面一行全是方块
}
else
face.data[i][j] = 0; //一个方块都没落下
}
}
d.history = face;//防止第一次运行就试图继续上次游戏,会造成方块掉入无底深渊
pf = fopen("record.dat", "w"); //自动创建该文件
if (pf == NULL) {//创建文件失败
CursorJump(COL, ROW / 2);
printf("你的存储介质为只读类型,无法创建游戏数据文件");
CursorJump(COL, ROW / 2+2);
printf("按任意键继续进入游戏……");
system("pause>nul");
}
else {
fwrite(&d, sizeof(DATA), 1, pf);
}
}
else {
fseek(pf, 0, SEEK_SET); //使文件指针pf指向文件开头
fread(&d, sizeof(DATA), 1, pf); //读取文件中的数据
fclose(pf); //关闭文件
pf = NULL; //防止内存泄露
}
return d;//读取失败游戏也可以玩,因为d被初始化了
}
//保存得分记录
int Tetris::WriteGrade(DATA d) {
FILE* pf = fopen("record.dat", "w");
if (pf == NULL) {
CursorJump(COL, ROW / 2 + 4);
printf("保存游戏数据失败,record.dat文件为只读或存储介质为只读");
CursorJump(COL, ROW / 2 + 6);
printf("按任意键直接退出……");
system("pause>nul");
return 1;
}
else
{
fwrite(&d, sizeof(DATA), 1, pf);
fclose(pf);
pf = NULL;
}
return 0;
}
//判断是否得分
int Tetris::JudeFunc()
{
for (int i = ROW - 2; i > 4; i--)//最底层开始判断
{
int sum = 0; //记录第i行的方块个数
for (int j = 1; j < COL - 1; j++)
{
sum += face.data[i][j]; //统计第i行的方块个数
}
if (sum == 0) {
break;//最后一行没有方块,无需再判断其上的层次
}
else if (sum == COL - 2) //该行全是方块,可得分
{
disScore += 10 * lines * lines; //根据一次性得分行数加分
for (int j = 1; j < COL - 1; j++) //清除整行的方块
{
face.data[i][j] = 0; //标记为无方块既被清除
}
//把被清除行上面的行整体向下挪一格
for (int m = i; m > 1; m--)
{
sum = 0; //记录上一行的方块个数
for (int n = 1; n < COL - 1; n++)
{
sum += face.data[m - 1][n]; //统计上一行的方块个数
face.data[m][n] = face.data[m - 1][n]; //将上一行方块的标识移到下一行
face.color[m][n] = face.color[m - 1][n]; //将上一行方块的颜色编号移到下一行
if (face.data[m][n] == 1) //上一行移下来的是方块,打印方块
{
CursorJump(2 * n, m); //光标跳转到该位置
color(face.color[m][n]); //颜色设置为原方块的颜色
printf("■"); //打印方块
}
else //上一行移下来的是空格,打印空格
{
CursorJump(2 * n, m); //光标跳转到该位置
printf(" "); //打印空格(两个空格)
}
}
//上一行移下来的全是空格,无需再将上层的方块向下移动(移动结束)
if (sum == 0)return 1; //移动下来的可能还有满行,还需调用该函数进行判断
}
}
}
lines = 1;//重置连续消掉的行数
return 0; //返回值用于结束调用者的死循环
}
//游戏主体逻辑函数
int Tetris::StartGame(){
clock_t start, end=clock();//用于计时
char ch;
int gameover = 0;
int nextShape,nextForm;
int x, y;//方块坐标
int shape = rand() % 7, form = rand() % 4; //随机获取方块的形状和形态
while (1){//主循环,用于不断产生新的方块
nextShape = rand() % 7, nextForm = rand() % 4; //随机获取下一个方块的形状和形态
x = COL / 2 - 2,y = 0;//初始位置
color(nextShape); //颜色设置为下一个方块的颜色
DrawBlock(nextShape, nextForm, COL + 3, 3); //将下一个方块显示在右上角
//下落循环,控制方块不断下落
while (1){
color(shape); //颜色设置为当前正在下落的方块
DrawBlock(shape, form, x, y); //将该方块显示在初始下落位置
start = clock();//计时开始
while (kbhit()) {//下落过程中不断检测键盘是否有动作,用于控制方块
ch = getch(); //读取keycode
switch (ch)
{
case 'd':
case 'D':
case DOWN://方向键:下
if (IsLegal(shape, form, x, y + 1)==1) //判断方块向下移动一位后是否合法
{
DrawSpace(shape, form, x, y); //用空格覆盖当前方块所在位置
y++; //下一次显示方块时就相当于下落了一格了
DrawBlock(shape, form, x, y);//及时重画该方块
}
break;
case 'a': //方便左手操作
case 'A':
case LEFT://方向键:左,方便右手操作
if (IsLegal(shape, form, x - 1, y) == 1)
{
DrawSpace(shape, form, x, y);
x--;
DrawBlock(shape, form, x, y);
}
break;
case 'f':
case 'F':
case RIGHT://方向键:右
if (IsLegal(shape, form, x + 1, y) == 1)
{
DrawSpace(shape, form, x, y);
x++;
DrawBlock(shape, form, x, y);
}
break;
case SPACE: //空格键
case UP://方向,上
if (IsLegal(shape, (form + 1) % 4, x, y) == 1)
{
DrawSpace(shape, form, x, y);
form = (form + 1) % 4;
DrawBlock(shape, form, x, y);
}
break;
case 's'://暂停
case 'S':
CursorJump(2 * COL + 4, ROW - 11);
printf("%-10s", "任意键继续");//让用户知道自己暂停了
system("pause>nul"); //按任意键继续
color(7);
CursorJump(2 * COL + 4, ROW - 11);
printf("%-10s","暂停: S");//恢复运行后,将操作提示写回去
break;
case 'r'://重新开始
case 'R':
Restart();
break;
case ESC: //esc键
system("cls"); //清空屏幕
color(7);
CursorJump(COL, ROW / 2);
printf("确认要保存游戏并退出吗?(y/n):");
CursorJump(COL, ROW / 2-4);
printf("不想保存直接退出请再次按esc");
while (1) {//必须做出正确选择
ch = getch();
if (ch == 'y' ||ch== 'Y') {//同意保存退出
if (disScore > data.max) { data.max = disScore; }
data.hisScore = disScore;
data.hisGrade = grade;
data.history = face;//保存现场
WriteGrade(data);
return 1;//结束所有循环,返回主程序
}
else if (ch == 'n' || ch == 'N') {
system("cls");
//保护现场
Face tempFace=face;
InitInterface();//重画界面会清除数据
face = tempFace;
//恢复现场
for (int i = 1; i < ROW - 1; i++) {
for (int j = 1; j < COL - 1; j++) {
if (face.data[i][j] == 1) {
CursorJump(2*j,i);
color(face.color[i][j]); //颜色设置为原方块的颜色
printf("■"); //打印方块
}
}
}
DrawBlock(nextShape, nextForm, COL + 3, 3);//画出下一个方块
break;//结束读取循环
}
else if (ch == ESC) {
return 1;//相当于双击esc直接退出游戏
}
else {
Beep(500,500);//输入错误发出提示音
}
}
break;
case 'U'://改名操作
case 'u': {
CursorJump(2 * COL + 22, 16);
printf("姓名:");
CursorJump(2 * COL + 27, 16);
color(8);
printf("最多五个字");
color(7);
CursorJump(2 * COL + 27, 16);
//显示光标
CONSOLE_CURSOR_INFO curInfo;
curInfo.dwSize = 1;
curInfo.bVisible = true;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(handle, &curInfo);
char cmd[600];//如果非要输入大于这个数的字符串也溢出
scanf("%s", cmd);
if (strlen(cmd)<LenofName) {//处理一下用户名溢出
strcpy(data.user, cmd);
sprintf(cmd, "title 俄罗斯方块当前用户:%s", data.user);
system(cmd);//把你的名字写在标题栏,装酷
WriteGrade(data);
}
else {
CursorJump(13, ROW/2);
printf("你的名字太长了");
}
//隐藏光标
curInfo.bVisible = false;
SetConsoleCursorInfo(handle, &curInfo);
CursorJump(2 * COL + 22, 16);//回到起始位置,擦掉刚才写的字
printf(" ");
break;
}
case 'C'://继续上次游戏
case 'c':
Face temFace=face;
system("cls"); //清空屏幕
CursorJump(COL, ROW / 2);
color(7);
printf("确认要恢复上次游戏继续玩吗?(y/任意键继续当前游戏):");
ch = getch();
if (ch == 'y' || ch == 'Y') {
system("cls");
data=ReadGrade();
disScore = data.hisScore;
grade = data.hisGrade;
forceGrade = 0;//恢复时没有强制提速
InitInterface();
face = data.history;
difficulty = AdjustDifficulty();//恢复游戏速度
Display();
}
else {//误操作时继续
system("cls");
InitInterface();
face = temFace;
}
//恢复现场
for (int i = 1; i < ROW - 1; i++) {
for (int j = 1; j < COL - 1; j++) {
if (face.data[i][j] == 1) {
CursorJump(2 * j, i);
color(face.color[i][j]); //颜色设置为原方块的颜色
printf("■"); //打印方块
}
}
}
DrawBlock(nextShape, nextForm, COL + 3, 3);//画出下一个方块
break;
}
//越级调整游戏难度,既方块下落速度
if (ch >= '0' && ch <= '9') {
int g=ch-'0';
//起步速度为上次最高得分的速度,想降速,根据本局得分确定可以将到的最低速度
if (g>=grade) {//速度选择正确
forceGrade = g;
difficulty = AdjustDifficulty();
Display();
}
else{//给点提示,为什么不让降速
CursorJump(2 * COL + 22, 16);
printf("坚持!别走回头路");
Beep(500, 500);
}
}
}
if (start-end>=difficulty) //下落时间到达
{
if (IsLegal(shape, form, x, y + 1)==1) //可以下落
{
DrawSpace(shape, form, x, y);
y++;
DrawBlock(shape, form, x, y);
}
else if(IsLegal(shape, form, x, y + 1) == 0)//不再下落
{
//face:记录界面的每个位置是否有方块,若有方块还需记录该位置方块的颜色。
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (block[shape][form].space[i][j]==1)
{
face.data[y + i][x + j] = 1; //将该位置标记为有方块
face.color[y + i][x + j] = shape; //记录该方块的颜色数值
}
}
}
//判断此次方块下落是否得分
while (JudeFunc()) {//有得分情况下
lines++;//有连续满行
//根据分数调整等级
if (disScore / 1000 > 9) {//防止得分过万显示大于9的等级
grade = 9;
}
else if (disScore / 1000 < data.hisGrade) {//延续历史速度
grade = data.hisGrade;
}
else {
grade = disScore / 1000;
}
difficulty = AdjustDifficulty();//重新计算下落时间
Display();
//擦掉试图降速时写的字
CursorJump(2 * COL + 22, 16);
//printf("坚持!别走回头路");
printf(" ");
}
break; //跳出当前死循环,准备进行下一个方块的下落
}
end = start;//重新开始计时
}
Sleep(20);//休眠20ms,减少无用循环次数,降低CPU占用率,否则CPU30%消耗在这个死循环
}
//判断游戏是否结束
for (int j = 1; j < COL - 1; j++)
{
if (face.data[1][j] == 1) {//最上面一行有方块就OVER
gameover = 1;//用于后面处理GAME OVER事项
break;
}
}
//游戏结束放在循环外面,这样方便退出死循环
if (gameover)
{//弄点动静
Beep(523, 400);//do
Beep(578, 400);//re
Beep(659, 400);//mi
Sleep(1200);//等上面的doremi
if (disScore / 1000 <= 9) { data.hisGrade = disScore / 1000; }//下次玩的初始速度
else { data.hisGrade = 0; }//分数过万且创记录,相当于通过所有挑战而爆机,等级恢复最低
system("cls");
color(7);
CursorJump(2 * (COL / 3), ROW / 2 - 3);
if (disScore > data.max)
{
printf("恭喜你创造出新的世界记录%d,留给后人慢慢挑战吧!", disScore);
data.max = disScore;
}
else if (disScore == data.max)
{
printf("你已追平世界记录,离超越只差一点点");
}
else
{
printf("你与最高记录相差%d,请继续加油", data.max - disScore);
}
WriteGrade(data);//不管玩不玩,还是保存一下吧
CursorJump(2 * (COL / 3), ROW / 2);
printf("GAME OVER");
CursorJump(2 * (COL / 3), ROW / 2 + 3);
printf("再来一局?(y/n):");
while (1)//要有明确选择才肯放过你
{
ch = getch();
if (ch == 'y' || ch == 'Y')
{
gameover = 0;
system("cls");
InitInterface();
Restart();
break;//跳出本死循环,进入主死循环,就开始了新的一局
}
else if (ch == 'n' || ch == 'N')
{
return 1;//直接跳出死循环返回主程序
}
else//错误选择鸣笛
{
Beep(500, 500);
}
}
}
//准备下一个方块
shape = nextShape, form = nextForm;
DrawSpace(nextShape, nextForm, COL + 3, 3); //将右上角的方块信息用空格覆盖
}
}
主程序,仅仅为了测试一下
#include "Tetris.h"
int main()
{
Tetris t = Tetris();
t.StartGame(); //开始游戏
return 0;
}
错误之处敬请批评指正