昆仑山:眼中无形心中有穴之穴人合一

夫君子之行,静以修身,俭以养德;非澹泊无以明志,非宁静无以致远。夫学须静也,才须学也;非学无以广才,非志无以成学。怠慢则不能励精,险躁则不能冶性。年与时驰,意与岁去,遂成枯落,多不接世。悲守穷庐,将复何及!

 

游戏人生,玩物丧志系列:俄罗斯方块(注意开发环境VS2019)

tetris.h头文件

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>   //windows系统函数
#include<stdbool.h>   //布尔型头文件,C语言没有布尔类型的基本数据类型
#include<time.h>  //计时处理
#include<conio.h>  //day6  Console Input/Output(控制台输入输出),用户通过按键盘产生的对应操作

//游戏区域位置设置
#define COL_BEGIN 2   //下标都从0开始
#define COL_END 14
#define ROW_BEGIN 4   //游戏池上留出4行,下留出2行,模拟方块缓慢落下
#define ROW_END 26



//利用结构体存储游戏相关数据
typedef struct TetrisManager
{
	unsigned int pool[28];   //游戏池(包括左右边界(半角))
	int x;  //当前方块x坐标,此处坐标为方块左上角坐标
	int y;  //当前方块y坐标
	int type[3]; //当前、下一个、下下一个方块的类型
	int orientation[3]; //当前、下一个、下下一个方块的旋转状态
	unsigned score; //得分
	unsigned erasedcount[4]; //消去行数
	unsigned erasedtotal; //消行总数
	unsigned tetriscount[7]; //各方块数
	unsigned tetristotal; //方块总数
	bool dead;  //挂
}Manager;

//以下结构体存储游戏控制相关数据
typedef struct TetrisControl
{
	bool pause;  //暂停
	bool clockwise; //控制旋转方向是否为顺时针,true为顺时针
	int direction; //移动方向:0向左移动,1向右移动
	//游戏池内每格的颜色
	int color[28][16];
}Control;

HANDLE output;  //控制台输出句柄

//7种方块的4种旋转状态(4位一行)
static const unsigned int TetrisTable[7][4] =
{
	{ 0x2222,0x00f0,0x2222,0x00f0 },//I型
	{ 0x0072,0x04c4,0x0027,0x08c8 },//T型
	{ 0x4460,0x0740,0x0622,0x02e0 },//L型
	{ 0x0226,0x0470,0x0322,0x0071 },//J型
	{ 0x0063,0x0264,0x0063,0x0264 },//Z型
	{ 0x06c0,0x08c4,0x06c0,0x08c4 },//S型
	{ 0x0660,0x0660,0x0660,0x0660 },//O型


};

//初始状态的游戏池
//每个元素表示游戏池的一行,两端(边界)各置2个1,底部2行全置为1,便于进行碰撞检测
//顶部4行用于给方块,但不显示出来
//再除去底部2行,显示出来的游戏池高度为22行
static const unsigned int gs_uInitialTetrisPool[28] =
{
	0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,
	0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,
	0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xc003,0xffff,
	0xffff
};

//函数声明(如果使用全局变量方式实现,就没必要传形参了)

//初始化游戏
void initGame(Manager* manager, Control* control);
//void initGame();

//显示提示信息
void printPrompting();

//显示得分信息
void printScore(const Manager* manager);

//显示游戏池边界
void printPoolBorder();

//以全角定位
void gotoxyWithFullwidth(short x, short y);

//开始游戏
void startGame(Manager* manager, Control* control);

//显示下一个和下下一个方块
void printNextTetris(const Manager* manager);

//day3
//显示游戏池
void printTetrisPool(Manager* manager, Control* control);

//初始化方块(给一个方块)
void initTetris(Manager* manager);

//插入方块
void insertTetris(Manager* manager);

//设置游戏池方格颜色
void SetPoolColor(const Manager* manager, Control* control);

//day4
//显示当前方块
void printCurrentTetris(const Manager* manager, Control* control);

//碰撞检测
bool checkCollision(const Manager* manager);

//消去行检测
bool checkErasing(Manager* manager, Control* control);

//移除方块
void removeTetris(Manager* manager);

//day5
//向下移动方块
void moveDownTetris(Manager* manager, Control* control);

//运行游戏
void runGame(Manager* manager, Control* control);

//水平移动方块
void horzMoveTetris(Manager* manager, Control* control);

//day6
//按键按下
void keydownControl(Manager* manager, Control* control, int key);

//day7
//旋转方块
void rotateTetris(Manager* manager, Control* control);

//方块直接落地
void dropDownTetris(Manager* manager, Control* control);

//再来一次
bool ifplayAgain();



入口文件

#include<cstdio>

#include"tetris.h"





#if 1
int main()
{
	Manager manager;
	Control control;
	initGame(&manager, &control);
	//initGame();
	/*  printPrompting();
	 printPoolBorder();
	 printScore(&manager);
	 printNextTetris(&manager);
	 printTetrisPool(&manager,&control);
	 printCurrentTetris(&manager,&control);  */
	do {
		printPrompting();
		printPoolBorder();
		//  printScore(&manager);  //day5将其并入初始化方块函数中
		//  printNextTetris(&manager);  //day5将其并入初始化方块函数中
		//  printTetrisPool(&manager,&control);  //day5将其放入向下移动方块函数中
		runGame(&manager, &control);  //运行游戏
		//day7
		if (ifplayAgain()) //再来一次
		{
			//定位,再清屏
			SetConsoleTextAttribute(output, 0x7);
			system("cls");
			//重新开始游戏
			startGame(&manager, &control);
		}
		else
		{
			break;
		}

	} while (1);
	gotoxyWithFullwidth(0, 0);
	//关闭句柄
	CloseHandle(output);  //系统资源,用了要还的,也就是说用完后一定要closehandle关闭之,如果不这么做,你系统的句柄资源很快就用光了

	return 0;
}

//初始化游戏
//void initGame()
void initGame(Manager* manager, Control* control)
{
	//获取控制台输出句柄
	output = GetStdHandle(STD_OUTPUT_HANDLE);
	//定义光标隐藏变量并初始化
	CONSOLE_CURSOR_INFO info = { 1,FALSE };
	//设置光标隐藏
	SetConsoleCursorInfo(output, &info);
	//设置控制台标题
	SetConsoleTitle(L"俄罗斯方块");

	//day2增加开始游戏模块
	startGame(manager, control);
}

//以全角定位
void gotoxyWithFullwidth(short x, short y)
{
	static COORD pos;
	pos.X = (2 * x); //左移1,*2使坐标改变为两个像素点距离
	pos.Y = y;
	SetConsoleCursorPosition(output, pos);
}

//显示提示信息
void printPrompting()
{
	//设置文字颜色 1011 高亮+蓝+绿
	SetConsoleTextAttribute(output, 0xB);
	//设置提示信息的位置(26,10),实质上是(26*2=52,10)
	gotoxyWithFullwidth(26, 10);
	//输出文字
	printf("■控制:");  //往下空一行
	gotoxyWithFullwidth(27, 12);
	printf("□向左移动:← A 4");
	gotoxyWithFullwidth(27, 13);
	printf("□向右移动:→ D 6");
	gotoxyWithFullwidth(27, 14);
	printf("□向下移动:↓ S 2");
	gotoxyWithFullwidth(27, 15);
	printf("□顺时旋转:↑ W 8");
	gotoxyWithFullwidth(27, 16);
	printf("□逆时移动:0");
	gotoxyWithFullwidth(27, 17);
	printf("□直接落地:空格");
	gotoxyWithFullwidth(27, 18);
	printf("□暂停游戏:回车");
	gotoxyWithFullwidth(27, 23);
	printf("■By:wanson");
}

//显示游戏池边界
void printPoolBorder()
{
	int y;
	//设置边界颜色(1111 0000 背景色高亮+蓝+绿+红)
	SetConsoleTextAttribute(output, 0xf0);
	for (y = ROW_BEGIN; y < ROW_END; y++)  //不显示顶部4行和底部2行
	{
		//打印两条纵线
		gotoxyWithFullwidth(10, y - 3);  //左边一条纵线
		printf("%2s", "");   //线宽
		gotoxyWithFullwidth(23, y - 3);  //右边一条纵线
		printf("%2s", "");
	}
	//打印一条横线
	gotoxyWithFullwidth(10, y - 3);  //y-3=26-3=23 底部边界
	printf("%28s", "");  //线宽,即长度为28
}

//显示得分信息
void printScore(const Manager* manager)
{
	//内存空间全部置0 增加开始游戏函数(放在初始化函数中调用)后,将其移动到开始游戏的函数中
	//memset(manager,0,sizeof(Manager));
	static const char tetrisName[8] = "ITLJZSO";
	int i;
	//设置文字颜色 高亮+黄 1110
	SetConsoleTextAttribute(output, 0xE);
	//设置得分信息位置(2,2)
	gotoxyWithFullwidth(2, 2);
	printf("★得分:%u", manager->score);
	//设置消行信息位置(1,6)
	gotoxyWithFullwidth(1, 6);
	printf("★消行总数:%u", manager->erasedtotal);
	for (i = 0; i < 4; i++)
	{
		gotoxyWithFullwidth(2, 8 + i);
		printf("☆消%d:%u", i + 1, manager->erasedcount[i]);
	}
	//设置方块总数位置(1,15)
	gotoxyWithFullwidth(1, 15);
	printf("★方块总数:%u", manager->tetristotal);
	for (i = 0; i < 7; i++)
	{
		gotoxyWithFullwidth(2, 17 + i);
		printf("☆%c形:%u", tetrisName[i], manager->tetriscount[i]);
	}
}

//开始游戏
void startGame(Manager* manager, Control* control)
{
	/*主要是初始化游戏相关值*/

	//初始化结构体
	//内存空间全部置0
	memset(manager, 0, sizeof(Manager));
	//day3
	//初始化游戏池
	memcpy(manager->pool, gs_uInitialTetrisPool, sizeof(unsigned int[28]));

	//设置随机种子参数
	srand((unsigned int)time(NULL));
	//初始化下一个方块,随机生成
	manager->type[1] = rand() % 7;  //下一个类型
	manager->orientation[1] = rand() % 4;  //下一个的旋转状态
	//初始化下下一个方块,随机生成
	manager->type[2] = rand() % 7;  //下下一个类型
	manager->orientation[2] = rand() % 4;  //下下一个的旋转状态

	//day3
	//控制内存空间全置为0
	memset(control, 0, sizeof(Control));
	//初始化方块
	initTetris(manager);
	//设置游戏池方格颜色
	SetPoolColor(manager, control);

}

//显示下一个和下下一个方块
void printNextTetris(const Manager* manager)
{
	int i, x;
	unsigned int tetris;  //用于存储下一个,下下一个方块
	//设置边框颜色(前景色高亮+红+绿+蓝)
	SetConsoleTextAttribute(output, 0x0f);
	for (x = 26; x < 37; x++)  //打印◇◇◇◇◇◇◇◇◇◇◇
	{
		gotoxyWithFullwidth(x, 1);
		printf("◇");
	}
	gotoxyWithFullwidth(26, 2);
	printf("◇%8s◇%8s◇", "", "");
	gotoxyWithFullwidth(26, 3);
	printf("◇%8s◇%8s◇", "", "");
	gotoxyWithFullwidth(26, 4);
	printf("◇%8s◇%8s◇", "", "");
	gotoxyWithFullwidth(26, 5);
	printf("◇%8s◇%8s◇", "", "");
	gotoxyWithFullwidth(26, 6);
	printf("◇◇◇◇◇◇◇◇◇◇◇");
	//下一个方块,用相应颜色表示
	tetris = TetrisTable[manager->type[1]][manager->orientation[1]];
	//方块颜色高亮  按位或8(0~6之间的数(颜色)+8(前景色高亮))
	SetConsoleTextAttribute(output, manager->type[1] | 8);
	/*下一个方块显示位置:  (27,2)(28,2)(29,2)(30,2)
							(27,3)(28,3)(29,3)(30,3)
							(27,4)(28,4)(29,4)(30,4)
							(27,5)(28,5)(29,5)(30,5)   */
	for (i = 0; i < 16; i++)
	{
		gotoxyWithFullwidth((i % 4) + 27, (i >> 2) + 2); //i%4也可以写为i&3,右移2位,除以4取整
		((tetris >> i) & 1) ? printf("■") : printf("%2s", ""); //将16位表示的方块(如0000 0000 1111 0000)逐位取出判断是0还是1,是1打印■,是0打印空格
	 //  if((tetris >> i) & 1)
	 //      printf("■");
	 //  else
	 //      printf("%2s","");
	}
	//下下一个方块,不显示颜色
	tetris = TetrisTable[manager->type[2]][manager->orientation[2]];
	SetConsoleTextAttribute(output, 8);
	/*  (32,2)(33,2)(34,2)(35,2)
		(32,3)(33,3)(34,3)(35,3)
		(32,4)(33,4)(34,4)(35,4)
		(32,5)(33,5)(34,5)(35,5)*/
	for (i = 0; i < 16; i++)
	{
		gotoxyWithFullwidth((i & 3) + 32, (i >> 2) + 2);
		((tetris >> i) & 1) ? printf("■") : printf("%2s", "");
	}
}

//显示游戏池
#define gotoxyInPool(x,y) gotoxyWithFullwidth(x+9,y-3)
void printTetrisPool(Manager* manager, Control* control)
{
	int x, y;
	//ROW_BEGIN  4   ROW_END  26
	for (y = ROW_BEGIN; y < ROW_END; y++) //不显示顶部4行,底部2行
	{
		//gotoxyInPool(2,y);
		gotoxyWithFullwidth(2 + 9, y - 3);
		//定点到游戏池中的方格
		//COL_BEGIN 2  COL_END 14
		for (x = COL_BEGIN; x < COL_END; x++)  //不显示左右边界2 3 4 5 (6 7 8 9) 10 11 12 13
		{
			if ((manager->pool[y] >> x) & 1) //游戏池的该位置有方块
			{
				//用相应颜色,显示一个实心方块
			  //   SetConsoleTextAttribute(output,12);
				SetConsoleTextAttribute(output, control->color[y][x]);
				printf("■");
			}
			else
			{
				//没有方块,显示空白
				SetConsoleTextAttribute(output, 0);
				printf("%2s", "");
			}
		}
	}
}

//初始化方块(给一个方块)
void initTetris(Manager* manager)
{
	unsigned int tetris; //用于存放当前方块 16位
	//下一个方块置为当前
	manager->type[0] = manager->type[1];
	manager->orientation[0] = manager->orientation[1];
	//下下方块置为下一个
	manager->type[1] = manager->type[2];
	manager->orientation[1] = manager->orientation[2];
	//随机生成下下一个方块
	manager->type[2] = rand() % 7;
	manager->orientation[2] = rand() % 4;
	//当前方块
	tetris = TetrisTable[manager->type[0]][manager->orientation[0]];
	//设置当前方块y坐标,保证刚给出时只显示方块最下面一行
	//这种实现使得玩家可以以很快的速度将方块落在不显示出来的顶部4行内
	if (tetris & 0xF000) //  俄罗斯方块最先显示最下面一行(16位从后往前依次摆放数组)
	{
		manager->y = 0;
	}
	else {
		manager->y = (tetris & 0xFF00) ? 1 : 2;
	}

	//   manager->y=4;  //能看到方块的行号
	manager->y = 0;   //开始游戏后需要设置为0,缓慢显示方格
	manager->x = 6; //设置当前方块x坐标(游戏池中央4格)

	//插入方块(day4插入前要先检测碰撞)
	if (checkCollision(manager))  //检测到碰撞
	{
		manager->dead = true;  //标记游戏结束
	}
	else
		insertTetris(manager);  //没有碰撞则插入方块
	manager->tetristotal++;   //方块总数
	manager->tetriscount[manager->type[0]]++; //相应方块数

	//day5
	printNextTetris(manager);  //显示下一个方块
	printScore(manager);  //显示得分信息

}

//插入方块
void insertTetris(Manager* manager)
{
	unsigned int tetris; //存放当前方块  16位
	tetris = TetrisTable[manager->type[0]][manager->orientation[0]];
	//当前方块每4位取出,按位或到游戏池相应位置,即完成插入方块,4*4的数组由低位向高位输出
	//manager->y=4,左移manager->x=6(6位),置于游戏池中间(如:0x0232)
	manager->pool[manager->y + 0] |= (((tetris >> 0x0) & 0x000f) << manager->x);//y+0行
	manager->pool[manager->y + 1] |= (((tetris >> 0x4) & 0x000f) << manager->x);//y+1行
	manager->pool[manager->y + 2] |= (((tetris >> 0x8) & 0x000f) << manager->x);//y+2行
	manager->pool[manager->y + 3] |= (((tetris >> 0xc) & 0x000f) << manager->x);//y+3行

}

//移除方块
void removeTetris(Manager* manager)
{
	//当前方块 16位
	unsigned int tetris = TetrisTable[manager->type[0]][manager->orientation[0]];
	//当前方块每4位取出,按位取反后位与到游戏池相应位置,即完成移除方块
	manager->pool[manager->y + 0] &= ~(((tetris >> 0x0) & 0x000f) << manager->x);
	manager->pool[manager->y + 1] &= ~(((tetris >> 0x4) & 0x000f) << manager->x);
	manager->pool[manager->y + 2] &= ~(((tetris >> 0x8) & 0x000f) << manager->x);
	manager->pool[manager->y + 3] &= ~(((tetris >> 0xc) & 0x000f) << manager->x);

}

//设置游戏池方格颜色
void SetPoolColor(const Manager* manager, Control* control)
{
	//显示游戏池时,先要在游戏池里判断某一方格有方块才显示相应方格的颜色
	//这里只作设置颜色,没必要清除
	//当移动方块或给一个方块时调用
	int i, x, y;
	//当前方块
	unsigned int tetris; //16位
	tetris = TetrisTable[manager->type[0]][manager->orientation[0]];
	for (i = 0; i < 16; i++)
	{
		y = manager->y + (i >> 2);   //待设置的行
		if (y > ROW_END)  //超过底部限制
		{
			break;
		}
		x = manager->x + (i & 3);  //i%4 待设置的列
		if ((tetris >> i) & 1)  //检测到小方格属于当前方块区域
		{
			control->color[y][x] = (manager->type[0] | 8); //设置颜色
		}
	}
}

//显示当前方块
void printCurrentTetris(const Manager* manager, Control* control)
{
	//显示当前方块是在方块移动后调用的,为擦去移动前的方块,需要扩展显示区域
	//由于方块不可能向上移动,故不需要向下扩展
	int x, y;
	if (manager->y > ROW_BEGIN)
		y = manager->y - 1;  //向上扩展一行
	else
		y = ROW_BEGIN;
	for (; y < ROW_END && y < manager->y + 4; y++)
	{
		if (manager->x > COL_BEGIN)
			x = manager->x - 1;  //向左扩展一列
		else
			x = COL_BEGIN;
		//可写为:x=(manager->x > COL_BEGIN) ? (manager->x - 1):COL_BEGIN;
		for (; x < COL_END && x < manager->x + 5; x++) //向右扩展一格
		{
			gotoxyInPool(x, y);
			//定点到游戏池中的方格
			if ((manager->pool[y] >> x) & 1) //游戏池中该方格有方块
			{
				SetConsoleTextAttribute(output, control->color[y][x]);
				printf("■");
			}
			else  //没有方块,显示空白
			{
				SetConsoleTextAttribute(output, 0);
				printf("%2s", "");
			}
		}
	}

}

//碰撞检测
bool checkCollision(const Manager* manager)
{
	unsigned int tetris;  //当前方块 16位
	tetris = TetrisTable[manager->type[0]][manager->orientation[0]];
	unsigned int dest = 0;
	//首先获取当前方块在游戏池中的位置4*4
	//游戏池中坐标x,y处小方格信息,按低到高存放在16位二进制的无符号数中
	dest |= ((((manager->pool[manager->y + 0]) >> manager->x) << 0x0) & 0x000f);
	//如果碰撞到之前保存在游戏池中的方块,则为1,否则为0没方块
	dest |= ((((manager->pool[manager->y + 1]) >> manager->x) << 0x4) & 0x00f0);
	dest |= ((((manager->pool[manager->y + 2]) >> manager->x) << 0x8) & 0x0f00);
	dest |= ((((manager->pool[manager->y + 3]) >> manager->x) << 0xc) & 0xf000);
	//每行游戏池的状态为1100 0000 0000 0011,底部1111 1111 1111 1111
	//若游戏池某一行有方块,则状态改变(以横I为例):1100 0011 1100 0011,会产生碰撞
	//检测碰撞实际上就是将游戏池中方块转换成一个16位二进制数,之后与当前方块进行与操作,看是否有重叠
	//若当前方块与目标区域存在重叠,则发生碰撞,则与的结果不为0
	return((dest & tetris) != 0);

}

//消行检测
bool checkErasing(Manager* manager, Control* control)
{
	int count = 0;
	static const unsigned score[5] = { 0,10,30,90,150 }; //消行得分
	int y = manager->y + 3;  //从下往上检测
	/*int k=0;
	for(;y>=manager->y+0 && k<4;y--,k++)
	{
		if(y<ROW_END && manager->pool[y]==0xffff) //有效区域内且一行已填满
		{
			count++;
			//消除一行方块
			memmove(manager->pool+1,manager->pool,sizeof(unsigned int)*y);
			//颜色数组的元素随之移动
			memmove(control->color[1],control->color[0],sizeof(int[16])*y);
		}
	}
	*/
	do {
		if (y < ROW_END && manager->pool[y] == 0xffff) //有效区域内且一行已填满
		{
			count++;
			//消除一行方块
			memmove(manager->pool + 1, manager->pool, sizeof(unsigned int) * y);
			//颜色数组的元素随之移动
			memmove(control->color[1], control->color[0], sizeof(int[16]) * y);
		}
		else {
			--y;
		}
	} while (y >= manager->y);

	manager->erasedtotal += count; //消行总数
	manager->score += score[count]; //得分

	if (count > 0)
		manager->erasedcount[count - 1]++; //消行
	//初始化方块
	initTetris(manager);
	//设置颜色
	SetPoolColor(manager, control);
	return(count > 0);
}

//向下移动方块
void moveDownTetris(Manager* manager, Control* control)
{
	//记录原行的位置
	int y = manager->y;
	//移除当前方块
	removeTetris(manager);
	//向下移动
	manager->y++;
	//检测是否有碰撞,若有碰撞,恢复到下落前的位置插入当前方块,由于位置没变,无需设置颜色
	//检测是否有消行,若有消行,重新显示游戏池  printTetrisPool
	if (checkCollision(manager))  //检测碰撞
	{
		manager->y = y;  //恢复到原行位置
		insertTetris(manager);  //插入当前方块,位置没变不设颜色
		if (checkErasing(manager, control))  //检测到消行
		{
			printTetrisPool(manager, control);  //显示游戏池
		}
	}
	else  //没有碰撞,插入方块,设置颜色,并显示当前方块printCurrentTetris
	{
		insertTetris(manager);  //插入当前方块
		SetPoolColor(manager, control);  //设置颜色
		printCurrentTetris(manager, control);  //显示当前方块
	}
}

//运行游戏
void runGame(Manager* manager, Control* control)
{
	//clock_t代表长整型,clock_t用来保存时间的数据类型
	clock_t clocklast, clocknow;
	clocklast = clock();  //计时
	printTetrisPool(manager, control);  //显示游戏池
	while (!manager->dead)
	{
		/*  clocknow=clock();  //计时
		  if(clocknow-clocklast>=0.1*1000)  //CLOCK_PER_SEC 1000
		  {
			  clocklast=clocknow;
			  moveDownTetris(manager,control);
			//horzMoveTetris(manager,control);
		  }  */

		  //day6
		while (_kbhit()) //有按键按下
		{
			keydownControl(manager, control, _getch());
		}
		if (!control->pause)  //未暂停
		{
			clocknow = clock(); //计时
			if (clocknow - clocklast >= 0.5 * 1000)  //CLOCK_PER_SEC 1000
			{
				clocklast = clocknow;
				moveDownTetris(manager, control);
			}
		}
	}

}

//水平移动方块
void horzMoveTetris(Manager* manager, Control* control)
{
	//记录原列位置(x)
	int x = manager->x;
	removeTetris(manager);  //移除当前方块
	//左移manager->x--,右移manager->x++
	control->direction == 0 ? manager->x-- : manager->x++;
	//manager->x--;
	if (checkCollision(manager))
	{
		manager->x = x;  //恢复原列位置
		insertTetris(manager); //放入当前方块
	}
	else  //无碰撞
	{
		insertTetris(manager);  //插入当前方块
		SetPoolColor(manager, control);  //设置颜色
		printCurrentTetris(manager, control);  //显示当前方块
	}
}

//按键按下
void keydownControl(Manager* manager, Control* control, int key)
{
	if (key == 13)  //暂停/解除暂停
	{
		control->pause = !control->pause;
	}
	if (control->pause)  //暂停状态,不作处理
	{
		return;  //起到终止语句的作用
	}
	switch (key)
	{
	case 75:
	case 'a':
	case 'A':
	case '4':  //左移
		control->direction = 0; //向左移动
		horzMoveTetris(manager, control); //水平移动方块
		break;
	case 77:
	case 'd':
	case 'D':
	case '6':  //右移
		control->direction = 1; //向右移动
		horzMoveTetris(manager, control); //水平移动方块
		break;
		//day7
	case 72:
	case 'w':
	case 'W':
	case '8': //上 旋转
		control->clockwise = true; //顺时针旋转
		rotateTetris(manager, control);  //旋转方块
		break;
	case '0':  //反转
		control->clockwise = false;  //逆时针旋转
		rotateTetris(manager, control); //旋转方块
		break;
	case 32:  //直接落地  空格键
		dropDownTetris(manager, control);
		break;
	default:
		break;
	}
}

//day7
//旋转方块
void rotateTetris(Manager* manager, Control* control)
{
	//记录原旋转状态
	int ori = manager->orientation[0];
	//移除方块
	removeTetris(manager);
	//顺逆时针旋转状态判定,并保存旋转后的状态
	manager->orientation[0] = (control->clockwise ? (ori + 1) % 4 : (ori + 3) % 4);
	//检测到碰撞,若有,方块恢复到旋转前的状态,插入当前方块,若无,在当前位置放入方块并设置颜色,显示
	if (checkCollision(manager))  //检测到碰撞
	{
		manager->orientation[0] = ori;  //恢复到原旋转状态
		insertTetris(manager);  //放入当前方块,不设置颜色
	}
	else  //未检测到碰撞
	{
		insertTetris(manager); //插入方块
		SetPoolColor(manager, control); //设置颜色
		printCurrentTetris(manager, control); //显示当前方块
	}

}

//方块直接落地
void dropDownTetris(Manager* manager, Control* control)
{
	//移走当前方块
	removeTetris(manager);
	//检测是否有碰撞,若有,结束下落,在碰撞上一个位置插入方块,设置颜色,检测消行,显示游戏池
	for (; manager->y < ROW_END; manager->y++) //从下往下有效区域
	{
		if (checkCollision(manager)) //检测到碰撞
			break;
	}
	manager->y--;  //上移一行没有碰撞
	insertTetris(manager); //插入当前方块
	SetPoolColor(manager, control);  //设置颜色
	checkErasing(manager, control);  //检测消行
	printTetrisPool(manager, control); //显示游戏池
}

//再来一次
bool ifplayAgain()
{
	int ch;
	//定位设置结束文字(高亮背景色)
	SetConsoleTextAttribute(output, 0xf0);
	gotoxyWithFullwidth(15, 10);
	printf("Game Over!");
	gotoxyWithFullwidth(13, 11);
	printf("按Y重玩,按N退出");
	//判断是否再来一次,若按'y',返回true,重新开始,若按'n',则结束游戏
	do
	{
		ch = _getch();
		if (ch == 'Y' || ch == 'y')
			return true;
		else if (ch == 'N' || ch == 'n')
			return false;
	} while (1);

}

#endif // 1




posted on 2019-06-14 10:56  Indian_Mysore  阅读(1399)  评论(1编辑  收藏  举报

导航