我罗斯方块最终篇(正文部分)

2.运行图片/视频

3.代码要点收获与心得
<1>Windows应用程序入口点

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR IpCmdLine, int ncmdShow) {
	//1.设计窗口类
	TCHAR  setAppClassName[] = TEXT("Tetris From GC");
	WNDCLASS winclass;
	winclass.cbClsExtra = 0;																										//窗口类额外空间大小
	winclass.cbWndExtra = 0;																										//窗口拓展空间大小
	winclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);						//加载白色背景画刷
	winclass.hCursor = LoadCursor(NULL, IDC_ARROW);														//加载光标
	winclass.hIcon= LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON1));					//设置图标
	winclass.hInstance = hInstance;																							//当前应用程序实例句柄
	winclass.lpfnWndProc = WindowProc;																				//窗口处理函数
	winclass.lpszClassName = setAppClassName;																	//窗口类型名
	winclass.lpszMenuName = NULL;																						//窗口菜单名
	winclass.style = CS_HREDRAW | CS_VREDRAW;																//窗口类的风格
	//2.注册窗口类
	RegisterClass(&winclass);
	//3.创建窗口
	HWND hwnd=CreateWindow(
		setAppClassName,																						//窗口类型名
		TEXT("NEW--Tetris"),																						//窗口标题
		WS_OVERLAPPEDWINDOW,																		//窗口风格
		400,100,																											//窗口左上角坐标
		800,700,																										//窗口宽和高
		NULL,																											//父窗口句柄
		NULL,																											//菜单句柄
		hInstance,																									//应用程序实例句柄
		NULL																											//参数
		);
	//4.显示窗口
	ShowWindow(hwnd,SW_SHOW);
	//5.更新窗口
	UpdateWindow(hwnd);
	//6.消息循环(Windows消息机制)
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);																	//将虚拟键消息转化为字符消息
		DispatchMessage(&msg);																	//将消息分发给窗口处理函数

	}
	//Sleep(10000);休眠占用
		return 0;//退出程序
}

<2>窗口处理函数

	PAINTSTRUCT ps;
	HDC hdc;
	switch (uMsg) {
	case WM_CREATE://窗口创建消息
			oncreate1();
			oncreate2();
		break;
	case WM_TIMER:
		ontimer1(hwnd);
		ontimer2(hwnd);
		break;
	case WM_PAINT://窗口绘图消息
		hdc=BeginPaint(hwnd, &ps);
		onpaint(hdc);
		EndPaint(hwnd, &ps);
		break;
	case WM_KEYDOWN://键盘按下
		switch (wParam) {
		case VK_RETURN://回车键
			onReturn(hwnd);//开始
			break;
		case VK_LEFT://左键
			onLeft2(hwnd);//左移
			break;
		case VK_RIGHT://右键
			onRight2(hwnd);//右移
			break;
		case VK_UP://上键
			onUp2(hwnd);//变形
			break;
		case VK_DOWN://下键
			onDown2(hwnd);//加速
			break;
		case 'A'://左键
		case 'a':
			onLeft(hwnd);//左移
			break;
		case 'D'://右键
		case 'd':
			onRight(hwnd);//右移
			break;
		case 'W'://上键
		case 'w':
			onUp(hwnd);//变形
			break;
		case 'S'://下键
		case 's':
			onDown(hwnd);//加速
			break;
		}
		break;
	case WM_CLOSE://窗口关闭消息
		DestroyWindow(hwnd);
		break;
	case WM_DESTROY://窗口销毁消息
		PostQuitMessage(0);
		break;
	}
	return DefWindowProc(hwnd,uMsg,wParam,lParam);
}

<3>绘图和方块的绘制

//绘图
void onpaint(HDC hdc) {
	//双缓冲
	HDC hmemdc=CreateCompatibleDC(hdc);															//创建一个兼容的内存DC
	HBITMAP hbackbmp=CreateCompatibleBitmap(hdc, 800, 600);					    //创建一个兼容位图  目前全黑
	SelectObject(hmemdc, hbackbmp);
	SelectObject(hmemdc, CreateFont(0, 0, 0, 0, 200, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, FIXED_PITCH, NULL));//字体设置
	drawblock(hmemdc);
	ShowScore(hmemdc);
	BitBlt(hdc, 0, 0, 800, 600,hmemdc,0,0,SRCCOPY);													//复制位图
}
//绘制方块
void drawblock(HDC hdc) {
	//绘制大矩形
	Rectangle(hdc, 0, 0, 300, 600);
	Rectangle(hdc, 600, 0, 800, 600);
	Rectangle(hdc, 610, 40, 780,80);
	Rectangle(hdc, 610, 90, 780, 130);
	Rectangle(hdc, 610, 140, 780, 280);
	//绘制出每一个小方块
	if (flag_begin == 1) {
		for (int i = 0; i < 20; i++) {
			for (int j = 0; j < 10; j++) {
				if (map1_arrground[i][j] == 1 || map1_arrground[i][j] == 2) {
					//绘制矩形
					Rectangle(hdc, j * 30 + 1, i * 30 + 1, j * 30 + 30 - 1, i * 30 + 30 - 1);
				}
				if (map2_arrground[i][j] == 1 || map2_arrground[i][j] == 2) {
					//绘制矩形
					Rectangle(hdc, (j + 10) * 30 + 1, i * 30 + 1, (j + 10) * 30 + 30 - 1, i * 30 + 30 - 1);
				}
			}
		}
	}
}

<4>七种方块二维数组的定义:0代表无方块 1表示方块正在下落 2表示方块已经下落到底

void oncreate1() {
	
		srand((UINT)time(NULL));
		int flag1 = rand() % 7;
		memset(map1_Tetris_block, 0, sizeof(map1_Tetris_block));//清空方块数组
		switch (flag1) {
		case 0:
			type_block1 = 0;//仰躺7
			at_line1 = 0;
			at_list1 = 0;
			map1_Tetris_block[0][0] = 1; map1_Tetris_block[0][1] = 0; map1_Tetris_block[0][2] = 0; map1_Tetris_block[0][3] = 0;
			map1_Tetris_block[1][0] = 1; map1_Tetris_block[1][1] = 1; map1_Tetris_block[1][2] = 1; map1_Tetris_block[1][3] = 0;
			break;
		case 1:
			type_block1 = 1;//趴躺7
			at_line1 = 0;
			at_list1 = 0;
			map1_Tetris_block[0][0] = 1; map1_Tetris_block[0][1] = 1; map1_Tetris_block[0][2] = 1; map1_Tetris_block[0][3] = 0;
			map1_Tetris_block[1][0] = 1; map1_Tetris_block[1][1] = 0; map1_Tetris_block[1][2] = 0; map1_Tetris_block[1][3] = 0;
			break;
		case 2:
			type_block1 = 2;//正Z
			at_line1 = 0;
			at_list1 = 0;
			map1_Tetris_block[0][0] = 1; map1_Tetris_block[0][1] = 1; map1_Tetris_block[0][2] = 0; map1_Tetris_block[0][3] = 0;
			map1_Tetris_block[1][0] = 0; map1_Tetris_block[1][1] = 1; map1_Tetris_block[1][2] = 1; map1_Tetris_block[1][3] = 0;
			break;
		case 3:
			type_block1 = 3;//反Z
			at_line1 = 0;
			at_list1 = 0;
			map1_Tetris_block[0][0] = 0; map1_Tetris_block[0][1] = 1; map1_Tetris_block[0][2] = 1; map1_Tetris_block[0][3] = 0;
			map1_Tetris_block[1][0] = 1; map1_Tetris_block[1][1] = 1; map1_Tetris_block[1][2] = 0; map1_Tetris_block[1][3] = 0;
			break;
		case 4:
			type_block1 = 4;//正方形
			map1_Tetris_block[0][0] = 0; map1_Tetris_block[0][1] = 1; map1_Tetris_block[0][2] = 1; map1_Tetris_block[0][3] = 0;
			map1_Tetris_block[1][0] = 0; map1_Tetris_block[1][1] = 1; map1_Tetris_block[1][2] = 1; map1_Tetris_block[1][3] = 0;
			break;
		case 5:
			type_block1 = 5;//凸形
			at_line1 = 0;
			at_list1 = 0;
			map1_Tetris_block[0][0] = 0; map1_Tetris_block[0][1] = 1; map1_Tetris_block[0][2] = 0; map1_Tetris_block[0][3] = 0;
			map1_Tetris_block[1][0] = 1; map1_Tetris_block[1][1] = 1; map1_Tetris_block[1][2] = 1; map1_Tetris_block[1][3] = 0;
			break;
		case 6:
			type_block1 = 6;//长条
			at_line1 = 0;
			at_list1 = 0;
			map1_Tetris_block[0][0] = 1; map1_Tetris_block[0][1] = 1; map1_Tetris_block[0][2] = 1; map1_Tetris_block[0][3] = 1;
			map1_Tetris_block[1][0] = 0; map1_Tetris_block[1][1] = 0; map1_Tetris_block[1][2] = 0; map1_Tetris_block[1][3] = 0;
			break;

		}
		for (int i = 0; i < 2; i++) {
			for (int j = 0; j < 4; j++) {
				map1_arrground[i][j] = map1_Tetris_block[i][j];
			}
		}
	
}

<5>方块的变形:除长条和石头形方块,其余方块均采用3*3矩阵转置再水平镜像操作实现每一次变形均是顺时针旋转90°;石头形不变形,长条形仅采用转置实现两种形态的变换

//四种类型方块变形
void Changed1() {
	int a[3][3] = { 0 };
	//小方块复制出来
	for (int i = 0; i < 3; i++){
		for (int j = 0; j < 3; j++){
			a[i][j] =map1_arrground[at_line1 + i][at_list1+ j];
		}
	}
	int t;
	for (int i = 0; i < 3; ++i){
		for (int j = 0; j < i; ++j){
			t = a[j][i];
			a[j][i] = a[i][j];
			a[i][j] = t;
		}
	}//先转置

	for (int i = 0; i < 3; ++i){
		for (int j = 0; j < 3 / 2; ++j){
			t = a[i][j];
			a[i][j] = a[i][3 - 1 - j];
			a[i][3 - 1 - j] = t;
		}
	}//水平镜像
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 3; j++) {
			map1_arrground[at_line1 + i][at_list1 + j] = a[i][j];
		}
	}
}
//长条变形
void ChangedLine1() {
	int a[4][4] = { 0 };
	//小方块复制出来
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			a[i][j] = map1_arrground[at_line1 + i][at_list1 + j];
		}
	}
	int t;
	for (int i = 0; i < 4; ++i) {
		for (int j = 0; j < i; ++j) {
			t = a[j][i];
			a[j][i] = a[i][j];
			a[i][j] = t;
		}
	}//先转置

	/*for (int i = 0; i < 4; ++i) {
		for (int j = 0; j < 4 / 2; ++j) {
			t = a[i][j];
			a[i][j] = a[i][4- 1 - j];
			a[i][4 - 1 - j] = t;
		}
	}//水平镜像*/
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			map1_arrground[at_line1 + i][at_list1 + j] = a[i][j];
		}
	}
}

<6>左移右移操作:在背景20*10二维数组上更新左移右移后方块位置

//左移
void blockLeft() {
	for (int i = 0; i < 20; i++) {
		for (int j = 0; j < 10; j++) {
			if (1 == map1_arrground[i][j]) {
				if (j - 1 >= 0) {
					map1_arrground[i][j - 1] = map1_arrground[i][j];
					map1_arrground[i][j] = 0;
				}
			}
		}
	}
}
//右移
void blockRight() {
	for (int i = 0; i < 20; i++) {
		for (int j = 9; j >= 0; j--) {
			if (1 == map1_arrground[i][j]) {
				if (j + 1 <= 9) {
					map1_arrground[i][j + 1] = map1_arrground[i][j];
					map1_arrground[i][j] = 0;
				}
			}
		}
	}
}

<7>下落操作

//调用所含定时器的函自定义数ontimer,实现不按时自动下落,按时加速下落
void onDown(HWND hwnd) {
	ontimer1(hwnd);
}
//ontimer函数:内含能否下落的判断以及下落函数,若无法下落,则调用flagchanged函数改变画布上的方块标记1->2,满足消行时进行消行
void ontimer1(HWND hwnd) {
	HDC hdc = GetDC(hwnd);
	if (CanDownF1() == 1 && CanDownS1() == 1) {
		BlockDown1();
		at_line1++;
	}
	else {
		FlagChanged1();
		DestroyBlock1();
		if (Gameover1(hwnd) == 0) {
			KillTimer(hwnd, 1234);
			return;
		}
		oncreate1();
	}
	onpaint(hdc);
	ReleaseDC(hwnd, hdc);
}
//下落函数
void BlockDown1() {
	int i = 0;
	int j = 0;
	for (i = 19; i >= 0; i--)
	{
		for (j = 0; j < 10; j++)
		{
			if (1 == map1_arrground[i][j])
			{
				map1_arrground[i + 1][j] = map1_arrground[i][j];
				map1_arrground[i][j] = 0;
			}
		}
	}
}
//消行函数
void DestroyBlock1() {
	int i = 0, j = 0;
	int sum = 0;
	int n= 0;
	int num = 0;
	for (i = 19; i >= 0; i--){
		for (j = 0; j < 10; j++){
			sum += map1_arrground[i][j];
		}
		if (20 == sum){
			//消除一行
			num++;
			for (n = i - 1; n>= 0; n--){
				for (j = 0; j < 10; j++){
					map1_arrground[n + 1][j] = map1_arrground[n][j];
					map1_arrground[n][j] = 0;
				}
			}
			//记分用
			player1++;
			//为了能消除多行
			i = 20;
			sum = 0;
			//给另一个玩家增加一行


		}
		//清零
		sum = 0;
	}
	if(num!=0)AddLineForOther(2,num);
}

<8>此消彼长机制(笔者放在了消行函数之中,考虑到依次可能消除多行的可能,故用num记录消除函数,用来作为给PK玩家增加的函数

void AddLineForOther(int flag, int num) {
	if (flag == 1) {
		int k=0;
		int flag1 = 0;
		for (int i = 0; i < 20; i++) {
			for (int j = 0; j < 10; j++) {
				if (map1_arrground[i][j] == 2) {
					flag1 = 1;
					break;
				}
				if (flag1 == 1) {
					k = i;
					break;
				}
			}
		}
		for (int i = k - num; i < 20; i++) {
			for (int j = 0; j < 10; j++) {
				if (2 == map1_arrground[i][j]) {
					int temp= map1_arrground[i][j];
					map1_arrground[i - 1][j] = map1_arrground[i][j];
					map1_arrground[i][j] = 0;
				}
			}
		}
		for (int i = 19; i > 19 - num; i--) {
			for (int j = 0; j < 3; j++) {
				//srand((UINT)time(NULL));
				int luck = rand() % 10;
				map1_arrground[i][luck] = 2;
				//map1_arrground[i][luck+1] = 2;
			}
		}
	}
	if (flag == 2) {
		int k=0;
		int flag1 = 0;
		for (int i = 0; i < 20; i++) {
			for (int j = 0; j < 10; j++) {
				if (map2_arrground[i][j] == 2) {
					flag1 = 1;
					break;
				}
				if (flag1== 1) {
					k = i;
					break;
				}
			}
		}
		for (int i = k -num; i < 20; i++) {
			for (int j = 0; j < 10; j++) {
				if (2 == map2_arrground[i][j]) {
					int temp = map2_arrground[i][j];;
					map2_arrground[i - 1][j] = map2_arrground[i][j];
					map2_arrground[i][j] = 0;
				}
			}
		}
		for (int i = 19; i > 19 - num; i--) {
			for (int j = 0; j < 3; j++) {
				//srand((UINT)time(NULL));
				int luck = rand() % 10;
				map2_arrground[i][luck] = 2;
				//map2_arrground[i][luck+1] = 2;

			}
		}
	}
}

<9>概要与省略说明的不分函数以及参数

void ShowScore(HDC hmemdc)//显示分数以及操作帮助函数
int map1_arrground[20][10] = {0};//玩家一背景
int map2_arrground[20][10] = {0};//玩家二背景
int  map1_Tetris_block[2][4] = { 0 };//玩家一随机方块
int  map2_Tetris_block[2][4] = { 0 };//玩家二随机方块
int type_block1= -1;//随机玩家一方块形状
int type_block2 = -1;//随机玩家二方块形状
int at_line1 = -1;//位置_行--玩家一
int at_list1= -1;//位置_列--玩家一
int at_line2 = -1;//位置_行--玩家二
int at_list2 = -1;//位置_列--玩家二
int player1 = 0;//玩家一分数
int player2 = 0;//玩家二分数
void oncreate1();//窗口创建时生成方块
int CanLeft1();//左移判定1
int CanLeft2();//左移判定2
int CanRight1();//右移判定1
int CanRight2();//右移判定2
int CanChanged1();//能否普通除石头长条能否变形判断
int CanChangedLine1();//长条能否变形判断
int CanDownF1();//能否下落判定1
int CanDownS1();//能否下落判定2

4.心得以及不足
<1>制作过程
因为一开始考虑做成exe ,有需要页面设计,渲染啥的,方块如何才能够从二进制代码编程有颜色的矩形块,无从头绪,就查找资料电脑端游如何制作,才会采用windowapi来制作这个我罗斯游戏界面(因此自我学习windows编程语言的时间比较长,也不太娴熟)。再然后开始方块如何表达,借鉴了网络上其他博主的方法采用二维数组进行描述(Ps:这里有而插曲,一开始我们并未考虑到直接33转置镜像来完成变形,而是采用了19个44二维数组统统表示了方块的各种形状。但在程序加入代码进行衔接的时候发现,对于每一次操作,实现每一个方块的各自的变形就有点复杂了,刚好有同学的准备线代补考问矩阵转置的问题,刚好得以利用哈哈哈哈),再来就是下落的问题,完成了按单词下落键实现单次下落后,存在了,方块不会像正版游戏一样自动下落哈哈哈,后来百度搜索接触到了settime,killtime定时器(Ps:后续游戏更新可以通过这个设置不同下落速度的关卡)。最后来说此消彼长吧:这个机制类似下落,将整个地图二维数组向上移动相应行数,而下方空出来的空白0方块,则运用rand()%7随机数来设置每一列随机方块0->2,在画布上出现随机增加方块。
<2>存在不足
首先.此程序居然会在少个别电脑上出现右侧得分栏文字乱码情况,估计是CreateFont函数里的参数在个别电脑上出BUG了吧
其次.游戏还缺少了预告下一次方块的功能,这点笔者在游戏界面构图时留出了空间供以更新该功能(ps:嘻嘻)+缺少了结束游戏退出Or重新开始的功能(Ps:笔者这一段messagebox的功能还在继续学习后续更新游戏尽力加上哈哈)
最后.对于此消彼长的机制,我觉得还有很多地方可以优化,比如在游戏展示的时候同学提到的如果在下落方块触底的一瞬暴增情况,以及暴增之后导致原来的随即方块被顶出游戏二维数组到了-1行或者以上呢。
总之:改进的地方存在许多,做完游戏之后第一时间给好友玩,接受不少意见,也会一点一点学习改进,第一次做游戏的感觉真的不错。{巨大不足就是,原先代码以及写的特别长,忽略了类的时候,继承以及多态。后面总的来看,很多地方代码其实时重复的了以及函数实现部分,但考虑到程序实现时限以及重构所带来的BUG问题,就先只能这么提交
接下来首先先把代码重构类,继承多态,然后改进上述的不足,逐步优化~~

posted @ 2020-06-16 19:59  Mr-CaoMei  阅读(125)  评论(0编辑  收藏  举报