基于win32 的俄罗斯方块游戏

Windows软件开发大作业——基于win32 的俄罗斯方块游戏

作业要求:
一、将本代码仓库clone到本地

二、运行MyTetris/Tetris.sln代码,开发环境为visual studio 2017

三、参照example/Tetris.cpp来按照如下步骤来完善Tetris.cpp,对如下每一步编译通过,然后commit一次,commit信息填写对应的步骤

  • 1、在项目中动态添加菜单"开始游戏、结束游戏",在窗口处理函数WM_CREATE 中创建菜单
  • 2、加入对菜单的响应,在窗口处理函数的WM_COMMAND 中处理, 以MessageBox显示点击了哪个菜单
 MessageBox(
        NULL,
        (LPCWSTR)L"Menu File is pressed",
        (LPCWSTR)L"Debug",
        MB_OK
         );
  • 3、加入对键盘消息(4个方向键)的响应,WM_KEYDOWN中处理,以MessageBox显示敲击了哪个键
 MessageBox(
        NULL,
        (LPCWSTR)L"Key A is pressed",
        (LPCWSTR)L"Debug",
        MB_OK
        );
  • 4、在Tetris.cpp中实现头文件中声明的函数

  • 5、加入对WM_PAINT的响应,画背景

  • 6、加入对WM_PAINT的响应,画NEXT

  • 7、加入对WM_PAINT的响应,画SCORE

  • 8、将菜单跟对应的函数对接起来,取消之前的MessageBox

  • 9、将键盘跟对应的函数对接起来,取消之前的MessageBox

  • 10、添加菜单“旋转方向”,参考example/Tetris.cpp中205行~216行

        rotate = CreatePopupMenu();//
        AppendMenu(hSysmenu, MF_POPUP, (UINT_PTR)rotate, L"旋转选择");//
		AppendMenu(rotate, MF_STRING, ID_ROATE1, L"顺时针");//
		AppendMenu(rotate, MF_STRING, ID_ROATE2, L"逆时针");//
		SetMenu(hWnd, rotate);//	
  • 11、添加菜单“顺时针”和“逆时针”的响应
//参考Tetris.cpp中239~244行
        case ID_ROATE1:
			selectRotate(hWnd, 0);//顺时针
			break;
		case ID_ROATE2:
			selectRotate(hWnd, 1);//逆时针
			break;
  • 12、修改Tetris.cpp525行RotateTeris的实现代码,根据GAME_ROTATE的值来确定旋转方向,0:顺时针,1:逆时针
  • 13、填写"MyTetris/大作业报告.doc" 按照该模板撰写大作业报告(需要填写“学号”、“姓名”和报告正文,报告中不需要代码截图,也不需要运行结果截图)

四、将本地代码仓库push到远程仓库

完成的代码:
Tetris.cpp

// Tetris.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "Tetris.h"
#include <windows.h>
#include <mmsystem.h>//包含windows中与多媒体有关的大多数接口
#include<tchar.h>
#pragma comment(lib, "WINMM.LIB")//导入winmm库:WINMM.LIB是Windows多媒体相关应用程序接口

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name
HMENU diff;//菜单句柄,难度选择
HMENU lay;//菜单句柄,布局选择
HMENU bf;
HMENU rotate;

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPWSTR    lpCmdLine,
                     int       nCmdShow)
{
	init_game();//初始化
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    // Initialize global strings
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_TETRIS, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TETRIS));

    MSG msg;

    // Main message loop:
	while (1)
	{
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))//检查消息队列
		{
			TranslateMessage(&msg);//转化字符消息
			DispatchMessage(&msg);//将消息分发给窗口程序
			if (msg.message == WM_QUIT)//
			{
				break;
			}
		}
		else
		{
			if ((GAME_STATE & 2) != 0)
			{
				tCur = GetTickCount();//返回毫秒数
				if (tCur - tPre > g_speed)//判断
				{
					int flag = CheckValide(curPosX, curPosY + 1, bCurTeris);//判断合法性给flag赋值
					if (flag == 1)//
					{
						curPosY++;//
						tPre = tCur;//
						HWND hWnd = GetActiveWindow();
						//这里两次调用实现了双缓冲的作用,改善了视觉效果
						InvalidateRect(hWnd, &rc_left, FALSE);//向指定的窗体更新区域添加一个矩形
						InvalidateRect(hWnd, &rc_right_top, FALSE);//系统不会像窗口发送WM_PAINT消息,来重绘
					}
					else if (flag == -2)//
					{
						g_speed = t_speed;//设置砖块速度
						fillBlock();//填充矩阵
						checkComplite(); //消去一行
						setRandomT();//设置方块形状
						curPosX = (NUM_X - 4) >> 1;//赋值
						curPosY = 0;//赋值
						HWND hWnd = GetActiveWindow();//调用线程的消息队列相关的活动窗口的窗口句柄。
						InvalidateRect(hWnd, &rc_main, FALSE);//向指定的窗体更新区域添加一个矩形
					}
					else if (flag == -3)//curPosY=0
					{
						HWND hWnd = GetActiveWindow();//调用线程的消息队列相关的活动窗口的窗口句柄。
						if (MessageBox(hWnd, L"胜败乃兵家常事,菜鸡请重新来过", L":时光机", MB_YESNO) == IDYES)//出现则重新来过
						{
							init_game();//初始化,重新来
						}
						else
						{
							break;//退出循环
						}
					}
				}
			}
		}
	}


    return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEXW wcex;//窗台类

	wcex.cbSize = sizeof(WNDCLASSEX);//大小
	wcex.style = CS_HREDRAW | CS_VREDRAW;//窗口类的形式
	wcex.lpfnWndProc = WndProc;//处理窗口事件,像单击鼠标会怎样,右击鼠标会怎样,都是由此函数控制的
	wcex.cbClsExtra = 0;//为窗口类的额外信息做记录,初始化为0
	wcex.cbWndExtra = 0;//为窗口类的额外信息做记录,初始化为0
	wcex.hInstance = hInstance;//模块的事例句柄
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TETRIS));//窗口类的图标
	wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);//窗口类的鼠标样式
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//窗口类的背景刷
	wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TETRIS);//指向一个以NULL结尾的字符串,同目录资源的名字一样
	wcex.lpszClassName = szWindowClass;//指向窗口类的指针,LPSTR类型
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));//小图标的句柄,在任务栏显示的图标

	return RegisterClassExW(&wcex);//返回值类型

}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;//包含某应用程序用来绘制他所拥有的窗口客户区所需要的信息
	HDC hdc;//
	int nWinx, nWiny, nClientX, nClientY;
	int posX, posY;
	RECT rect;//存储参数
	HMENU hSysmenu;//菜单条
    switch (message)
    {
	case WM_CREATE://创建窗口
		GetWindowRect(hWnd, &rect);
		//
		nWinx = 530;//设置宽度
		nWiny = 680;//设置长度
		posX = GetSystemMetrics(SM_CXSCREEN);//获取窗口的参数
		posY = GetSystemMetrics(SM_CYSCREEN);//获取窗口的参数
		posX = (posX - nWinx) >> 1;//赋值
		posY = (posY - nWiny) >> 1;//赋值
		GetClientRect(hWnd, &rect);//
		nClientX = rect.right - rect.left;//设置长度
		nClientY = rect.bottom - rect.top;//设置长度

		MoveWindow(hWnd, posX, posY, 530, 680, TRUE);//改变窗口的位置
		hSysmenu = GetMenu(hWnd);//创建窗口
		AppendMenu(hSysmenu, MF_SEPARATOR, 0, NULL);//
													//此处的菜单句柄要全局向前初始化
		diff = CreatePopupMenu();//
		AppendMenu(hSysmenu, MF_POPUP, (UINT_PTR)diff, L"难度选择");//设置菜单栏上的文字
		AppendMenu(diff, MF_STRING, ID_dif1, L"难度1");//下拉的选项
		AppendMenu(diff, MF_STRING, ID_dif2, L"难度2");//下拉的选项
		AppendMenu(diff, MF_STRING, ID_dif3, L"难度3");//下拉的选项
		lay = CreatePopupMenu();//
		AppendMenu(hSysmenu, MF_POPUP, (UINT_PTR)lay, L"布局选择");//设置菜单栏上的文字
		AppendMenu(lay, MF_STRING, ID_LAYOUT1, L"布局1");//下拉的选项
		AppendMenu(lay, MF_STRING, ID_LAYOUT2, L"布局2");//下拉的选项
		bf = CreatePopupMenu();
		AppendMenu(hSysmenu, MF_POPUP, (UINT_PTR)bf, L"开始游戏、结束游戏");
		AppendMenu(bf, MF_STRING, ID_BF, L"开始游戏、结束游戏");//下拉的选项
		rotate = CreatePopupMenu();//
		AppendMenu(hSysmenu, MF_POPUP, (UINT_PTR)rotate, L"旋转选择");//
		AppendMenu(rotate, MF_STRING, ID_ROATE1, L"顺时针");//
		AppendMenu(rotate, MF_STRING, ID_ROATE2, L"逆时针");//
		

		SetMenu(hWnd, hSysmenu);//装配窗口
		SetMenu(hWnd, diff);//装配难度
		SetMenu(hWnd, lay);//装配布局
		SetMenu(hWnd, bf);
		SetMenu(hWnd, rotate);//
		break;
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_ABOUT:
				DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);//创建一个模态对话框
               
                break;
            case IDM_EXIT:
				DestroyWindow(hWnd);//销毁窗口
               
                break;
			case ID_dif1:
				selectDiffculty(hWnd, 1);//选择难度
				
				break;
			case ID_dif2:
				selectDiffculty(hWnd, 2);//选择难度
				
				break;
			case ID_dif3:
				selectDiffculty(hWnd, 3);//选择难度
				
				break;
			case ID_LAYOUT1:
				
				selectLayOut(hWnd, 1);//选择布局
				break;
			case ID_LAYOUT2:
				selectLayOut(hWnd, 2);//选择布局
				break;
			case ID_BF:
				init_game();
				break;
			case ID_ROATE1:
				selectRotate(hWnd, 0);//顺时针
				break;
			case ID_ROATE2:
				selectRotate(hWnd, 1);//逆时针
				break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
	case WM_KEYDOWN://键盘消息
		hdc = GetDC(hWnd);
		InvalidateRect(hWnd, NULL, false);
		switch (wParam)
		{
		case VK_LEFT:
			/*MessageBox(
				NULL,
				(LPCWSTR)L"Key LEFT is pressed",
				(LPCWSTR)L"Debug",
				MB_OK
			);*/
			curPosX--;
			if (CheckValide(curPosX, curPosY, bCurTeris) != 1)
			{
				curPosX++;//右移
			}
			break;
		case VK_RIGHT:
			/*MessageBox(
				NULL,
				(LPCWSTR)L"Key RIGHT is pressed",
				(LPCWSTR)L"Debug",
				MB_OK
			);*/
			curPosX++;
			if (CheckValide(curPosX, curPosY, bCurTeris) != 1)
			{
				curPosX--;//左移
			}
			break;
		case VK_UP:
			/*MessageBox(
				NULL,
				(LPCWSTR)L"Key UP is pressed",
				(LPCWSTR)L"Debug",
				MB_OK
			);*/
			RotateTeris(bCurTeris);//变换图形形状
			break;
		case VK_DOWN://判断按键
			if (g_speed == t_speed)
				g_speed = 10;
			else
				g_speed = t_speed;
			//outPutBoxInt(g_speed);
			break;
		case 'W'://同 VK_UP
			RotateTeris(bCurTeris);
			/*MessageBox(
				NULL,
				(LPCWSTR)L"Key W is pressed",
				(LPCWSTR)L"Debug",
				MB_OK
			);*/
			break;
		case 'A'://同 VK_LEFT
			curPosX--;
			if (CheckValide(curPosX, curPosY, bCurTeris) != 1)
			{
				curPosX++;
			}
			/*MessageBox(
				NULL,
				(LPCWSTR)L"Key A is pressed",
				(LPCWSTR)L"Debug",
				MB_OK
			);*/
			break;
		case 'D'://同 VK_RIGHT
			curPosX++;
			if (CheckValide(curPosX, curPosY, bCurTeris) != 1)
			{
				curPosX--;
			}
			/*MessageBox(
				NULL,
				(LPCWSTR)L"Key D is pressed",
				(LPCWSTR)L"Debug",
				MB_OK
			);*/
			break;

		case 'S'://同 VK_DOWN
			if (g_speed == t_speed)
				g_speed = 10;
			else
				g_speed = t_speed;
			//outPutBoxInt(g_speed);
			/*MessageBox(
				NULL,
				(LPCWSTR)L"Key S is pressed",
				(LPCWSTR)L"Debug",
				MB_OK
			);*/
			break;
		default:
			break;
		}
		break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
			DrawBackGround(hdc);//设置背景
			drawNext(hdc);//设置下一个砖块
			drawScore(hdc);//设置分数
            // TODO: Add any drawing code that uses hdc here...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}
void drawBlocked(HDC mdc)//绘制当前已经存在砖块的区域
{
	int i, j;
	//
	HBRUSH hBrush = (HBRUSH)CreateSolidBrush(RGB(255, 255, 0));//画刷

	SelectObject(mdc, hBrush);//选择一对象

	for (i = 0; i < NUM_Y; i++)
	{
		for (j = 0; j < NUM_X; j++)
		{
			if (g_hasBlocked[i][j])
			{
				Rectangle(mdc, BORDER_X + j * BLOCK_SIZE, BORDER_Y + i * BLOCK_SIZE,
					BORDER_X + (j + 1)*BLOCK_SIZE, BORDER_Y + (i + 1)*BLOCK_SIZE
				);//画出图形
			}
		}
	}
	DeleteObject(hBrush);//删除图形
}

int CheckValide(int startX, int startY, BOOL bTemp[4][4])//给定一个矩阵,查看是否合法
{
	int i, j;
	for (i = 3; i >= 0; i--)
	{
		for (j = 3; j >= 0; j--)
		{
			if (bTemp[i][j])
			{
				if (j + startX < 0 || j + startX >= NUM_X)
				{
					return -1;
				}
				if (i + startY >= NUM_Y)
				{
					return -2;
				}
				if (g_hasBlocked[i + startY][j + startX])
				{
					//outPutBoxInt(j+startY);
					if (curPosY == 0)
					{
						return -3;
					}
					return -2;
				}
			}
		}
	}
	//MessageBox(NULL,L"这里",L"as",MB_OK);
	//outPutBoxInt(curPosY);
	return 1;
}

void checkComplite()//查看一行是否能消去
{
	int i, j, k, count = 0;
	for (i = 0; i < NUM_Y; i++)
	{
		bool flag = true;
		for (j = 0; j < NUM_X; j++)
		{
			if (!g_hasBlocked[i][j])
			{
				flag = false;//有一个为空时,不能消去
			}
		}
		if (flag)
		{
			count++;
			for (j = i; j >= 1; j--)//从上往下把每行下移
			{
				for (k = 0; k < NUM_X; k++)
				{
					g_hasBlocked[j][k] = g_hasBlocked[j - 1][k];
				}

			}
			drawCompleteParticle(i);//画出剩下的图形
			Sleep(300);//暂停300ms

			//PlaySound(_T("coin.wav"), NULL, SND_FILENAME | SND_ASYNC);//消去一行时播放系统自带提示音
		}
	}
	GAME_SCORE += int(count*1.5);//单行+1分,两行+3分,三行+4分,四行6分,以此类推
}

VOID outPutBoxInt(int num)//自定义的弹窗函数  用于调试,已经被注释掉了
{
	TCHAR szBuf[1024];
	LPCTSTR str = TEXT("%d");
	wsprintf(szBuf, str, num);//将一系列的字符和数值输入到缓冲区
	MessageBox(NULL, szBuf, L"aasa", MB_OK);//模态对话框
}

VOID outPutBoxString(TCHAR str[1024])//自定义的弹窗函数  用于调试,已经被注释掉了
{
	TCHAR szBuf[1024];
	LPCTSTR cstr = TEXT("%s");
	wsprintf(szBuf, cstr, str);
	MessageBox(NULL, szBuf, L"aasa", MB_OK);
}



//设置随机方块形状
void setRandomT()
{
	int rd_start = RandomInt(0, sizeof(state_teris) / sizeof(state_teris[0]));
	int rd_next = RandomInt(0, sizeof(state_teris) / sizeof(state_teris[0]));
	//outPutBoxInt(rd_start);
	//outPutBoxInt(rd_next);
	//outPutBoxInt(rd_start);
	if (GAME_STATE == 0)
	{
		GAME_STATE = GAME_STATE | 0x0001;
		//outPutBoxInt(GAME_STATE);
		memcpy(bCurTeris, state_teris[rd_start], sizeof(state_teris[rd_start]));
		memcpy(bNextCurTeris, state_teris[rd_next], sizeof(state_teris[rd_next]));
	}
	else
	{
		memcpy(bCurTeris, bNextCurTeris, sizeof(bNextCurTeris));
		memcpy(bNextCurTeris, state_teris[rd_next], sizeof(state_teris[rd_next]));
	}

}

//游戏初始化
void init_game()
{
	GAME_SCORE = 0;
	setRandomT();//设置随机形状
	curPosX = (NUM_X - 4) >> 1;//设置初始坐标
	curPosY = 0;
	//memset,是计算机语言中的函数。起功能是将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,最后行清零
	memset(g_hasBlocked, 0, sizeof(g_hasBlocked));
	rc_left.left = 0;
	rc_left.right = SCREEN_LEFT_X;
	rc_left.top = 0;
	rc_left.bottom = SCREEN_Y;

	rc_right.left = rc_left.right + BORDER_X;
	rc_right.right = 180 + rc_right.left;
	rc_right.top = 0;
	rc_right.bottom = SCREEN_Y;

	rc_main.left = 0;
	rc_main.right = SCREEN_X;
	rc_main.top = 0;
	rc_main.bottom = SCREEN_Y;

	rc_right_top.left = rc_right.left;
	rc_right_top.top = rc_right.top;
	rc_right_top.right = rc_right.right;
	rc_right_top.bottom = (rc_right.bottom) / 2;

	rc_right_bottom.left = rc_right.left;
	rc_right_bottom.top = rc_right_top.bottom + BORDER_Y;
	rc_right_bottom.right = rc_right.right;
	rc_right_bottom.bottom = rc_right.bottom;

	g_speed = t_speed = 1000 - GAME_DIFF * 280;
}

void fillBlock()//到达底部后填充矩阵
{
	int i, j;
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			if (bCurTeris[i][j])
			{
				g_hasBlocked[curPosY + i][curPosX + j] = 1;
			}
		}
	}
}

void RotateTeris(BOOL bTeris[4][4])
{
	BOOL bNewTeris[4][4];
	int x, y;
	if (GAME_ROTATE == 0)
	{
		for (x = 0; x < 4; x++)
		{
			for (y = 0; y < 4; y++)
			{//旋转角度
				//bNewTeris[x][y] = bTeris[3 - y][x];
				//逆时针:
				bNewTeris[x][y] = bTeris[y][3 - x];
			}
		}
	}
	if (GAME_ROTATE == 1)
	{
		for (x = 0; x < 4; x++)
		{
			for (y = 0; y < 4; y++)
			{//旋转角度
				bNewTeris[x][y] = bTeris[3 - y][x];
				//逆时针:
			}
		}
	}
	if (CheckValide(curPosX, curPosY, bNewTeris) == 1)
	{
		memcpy(bTeris, bNewTeris, sizeof(bNewTeris));//成功则将变换后的形状保存
	}

}

int RandomInt(int _min, int _max)
{
	srand((rd_seed++) % 65532 + GetTickCount());
	return _min + rand() % (_max - _min);
}

VOID DrawTeris(HDC mdc)
{

	int i, j;
	HPEN hPen = (HPEN)GetStockObject(BLACK_PEN);//
	HBRUSH hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
	SelectObject(mdc, hPen);//SelectObject是计算机编程语言函数,该函数选择一对象到指定的设备上下文环境中,新对象替换先前的相同类型的对象
	SelectObject(mdc, hBrush);
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			if (bCurTeris[i][j])
			{
				Rectangle(mdc, (j + curPosX)*BLOCK_SIZE + BORDER_X, (i + curPosY)*BLOCK_SIZE + BORDER_Y, (j + 1 + curPosX)*BLOCK_SIZE + BORDER_X, (i + 1 + curPosY)*BLOCK_SIZE + BORDER_Y);
			}
		}
	}
	drawBlocked(mdc);
	DeleteObject(hPen);
	DeleteObject(hBrush);
}

VOID DrawBackGround(HDC hdc)//绘制背景
{

	HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
	//适用于支持光栅操作的设备,应用程序可以通过调用GetDeviceCaps函数来确定一个设备是否支持这些操作
	HDC mdc = CreateCompatibleDC(hdc);
	HBITMAP hBitmap = CreateCompatibleBitmap(hdc, SCREEN_X, SCREEN_Y);

	SelectObject(mdc, hBrush);
	SelectObject(mdc, hBitmap);

	HBRUSH hBrush2 = (HBRUSH)GetStockObject(WHITE_BRUSH);
	FillRect(mdc, &rc_main, hBrush2);
	Rectangle(mdc, rc_left.left + BORDER_X, rc_left.top + BORDER_Y, rc_left.right, rc_left.bottom);
	Rectangle(mdc, rc_right.left + BORDER_X, rc_right.top + BORDER_Y, rc_right.right, rc_right.bottom);
	DrawTeris(mdc);
	drawNext(mdc);
	drawScore(mdc);
	::BitBlt(hdc, 0, 0, SCREEN_X, SCREEN_Y, mdc, 0, 0, SRCCOPY);
	DeleteObject(hBrush);
	DeleteDC(mdc);
	DeleteObject(hBitmap);
	DeleteObject(hBrush2);


	//  int x,y;
	//  HPEN hPen = (HPEN)GetStockObject(NULL_PEN);
	//  HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
	//  SelectObject(hdc,hPen);
	//  SelectObject(hdc,hBrush);
	//  for (x = 0;x<NUM_X;x++)
	//  {
	//      for(y=0;y<NUM_Y;y++)
	//      {
	//          Rectangle(hdc,BORDER_X+x*BLOCK_SIZE,BORDER_Y+y*BLOCK_SIZE,
	//              BORDER_X+(x+1)*BLOCK_SIZE,
	//              BORDER_Y+(y+1)*BLOCK_SIZE);
	//      }
	//  }
}

void drawNext(HDC hdc)//绘制下一个将要掉落的方块
{
	int i, j;
	HBRUSH hBrush = (HBRUSH)CreateSolidBrush(RGB(188, 0, 0));
	HPEN hPen = (HPEN)CreatePen(PS_SOLID, 2, RGB(0, 0, 233));
	SelectObject(hdc, hBrush);
	SelectObject(hdc, hPen);
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			if (bNextCurTeris[i][j])
			{
				Rectangle(hdc, rc_right_top.left + BLOCK_SIZE * (j + 1), rc_right_top.top + BLOCK_SIZE * (i + 1), rc_right_top.left + BLOCK_SIZE * (j + 2), rc_right_top.top + BLOCK_SIZE * (i + 2));
			}
		}
	}
	HFONT hFont = CreateFont(30, 0, 0, 0, FW_THIN, 0, 0, 0, UNICODE, 0, 0, 0, 0, L"微软雅黑");
	SelectObject(hdc, hFont);
	SetBkMode(hdc, TRANSPARENT);
	SetBkColor(hdc, RGB(255, 255, 0));
	SetTextColor(hdc, RGB(152, 206, 0));
	RECT rect;
	rect.left = rc_right_top.left + 40;
	rect.top = rc_right_top.bottom - 150;
	rect.right = rc_right_top.right;
	rect.bottom = rc_right_top.bottom;
	DrawTextW(hdc, TEXT("下一个"), _tcslen(TEXT("下一个")), &rect, 0);
	DeleteObject(hFont);
	DeleteObject(hBrush);
}

void drawScore(HDC hdc)
{
	HFONT hFont = CreateFont(30, 0, 0, 0, FW_THIN, 0, 0, 0, UNICODE, 0, 0, 0, 0, L"微软雅黑");
	SelectObject(hdc, hFont);
	SetBkMode(hdc, TRANSPARENT);
	SetBkColor(hdc, RGB(255, 255, 0));
	SetTextColor(hdc, RGB(0, 200, 150));
	RECT rect;
	rect.left = rc_right_bottom.left;
	rect.top = rc_right_bottom.top;
	rect.right = rc_right_bottom.right;
	rect.bottom = rc_right_bottom.bottom;
	TCHAR szBuf[30];
	LPCTSTR cstr = TEXT("当前难度:%d");
	wsprintf(szBuf, cstr, GAME_DIFF);
	DrawTextW(hdc, szBuf, _tcslen(szBuf), &rect, DT_CENTER | DT_VCENTER);

	RECT rect2;
	rect2.left = rc_right_bottom.left;
	rect2.top = rc_right_bottom.bottom / 2 + 100;
	rect2.right = rc_right_bottom.right;
	rect2.bottom = rc_right_bottom.bottom;
	TCHAR szBuf2[30];
	LPCTSTR cstr2 = TEXT("得分:%d");
	wsprintf(szBuf2, cstr2, GAME_SCORE);
	//outPutBoxInt(sizeof(szBuf));
	SetTextColor(hdc, RGB(15, 0, 150));
	DrawTextW(hdc, szBuf2, _tcslen(szBuf2), &rect2, DT_CENTER | DT_VCENTER);

	DeleteObject(hFont);
}

int selectDiffculty(HWND hWnd, int diff)
{
	TCHAR szBuf2[30];
	LPCTSTR cstr2 = TEXT("确认选择难度 %d 吗?");
	wsprintf(szBuf2, cstr2, diff);
	if (MessageBox(hWnd, szBuf2, L"难度选择", MB_YESNO) == IDYES)
	{
		GAME_DIFF = diff;
		InvalidateRect(hWnd, &rc_right_bottom, false);
		GAME_STATE |= 2;
		init_game();
		return GAME_DIFF;
	}
	return -1;
}

int selectLayOut(HWND hWnd, int layout)//选择布局
{
	NUM_X = 10 * layout;
	NUM_Y = 20 * layout;
	BLOCK_SIZE = 30 / layout;
	GAME_STATE |= 2;
	InvalidateRect(hWnd, &rc_right_bottom, false);
	init_game();
	return layout;
}

void drawCompleteParticle(int line)
{
	HWND hWnd = GetActiveWindow();
	HDC hdc = GetDC(hWnd);
	HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
	HPEN hPen = (HPEN)CreatePen(PS_DOT, 1, RGB(255, 255, 0));
	SelectObject(hdc, hBrush);
	SelectObject(hdc, hPen);
	Rectangle(hdc, BORDER_X,
		BORDER_Y + line * BLOCK_SIZE,
		BORDER_X + NUM_X * BLOCK_SIZE,
		BORDER_Y + BLOCK_SIZE * (1 + line));
	DeleteObject(hBrush);
	DeleteObject(hPen);
}

int selectRotate(HWND hWnd, int direction)
{
	GAME_ROTATE=direction;
	return GAME_ROTATE;
}

resource.cpp

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// 供 tetris.rc 使用
//

#define IDS_APP_TITLE			103

#define IDR_MAINFRAME			128
#define IDD_TETRIS_DIALOG	102
#define IDD_ABOUTBOX			103
#define IDM_ABOUT				104
#define IDM_EXIT				105
#define IDM_DIFF 106
#define ID_dif1 111
#define ID_dif2 112
#define ID_dif3 113
#define ID_LAYOUT1 114
#define ID_LAYOUT2 115
#define ID_ROATE1 116
#define ID_ROATE2 117
#define ID_BF 118
#define IDI_TETRIS			107
#define IDI_SMALL				108
#define IDC_TETRIS			109
#define IDC_MYICON				2
#ifndef IDC_STATIC
#define IDC_STATIC				-1
#endif
// 新对象的下一组默认值
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NO_MFC					130
#define _APS_NEXT_RESOURCE_VALUE	129
#define _APS_NEXT_COMMAND_VALUE		32771
#define _APS_NEXT_CONTROL_VALUE		1000
#define _APS_NEXT_SYMED_VALUE		110
#endif
#endif

Tetris.h

#pragma once

#include "resource.h"

//函数声明
void checkComplite();  //  
void drawBlocked(HDC hdc);      //
void DrawBackGround(HDC hdc);       //
void outPutBoxInt(int num);     //
void outPutBoxString(TCHAR str[1024]);
void setRandomT();      //
void init_game();       //
void fillBlock();       //
void RotateTeris(BOOL bTeris[4][4]);        //
void DrawTeris(HDC mdc);    //
void drawNext(HDC hdc); //
void drawScore(HDC hdc);    //
void drawCompleteParticle(int line);

int RandomInt(int _min, int _max);       //
int CheckValide(int curPosX, int curPosY, BOOL bCurTeris[4][4]);   //
int selectDiffculty(HWND hWnd, int dif);
int selectLayOut(HWND hWnd, int layout);
int selectRotate(HWND hWnd, int direction);

//常量声明
const int BORDER_X = 10;//
const int BORDER_Y = 10;//
const int SCREEN_LEFT_X = 300 + BORDER_X;//
const int SCREEN_Y = 600 + BORDER_Y;//
const int SCREEN_RIGHT_X = 180 + BORDER_X * 2;//
const int SCREEN_X = SCREEN_LEFT_X + SCREEN_RIGHT_X;//
const BOOL state_teris[][4][4] =
{
	{ { 1,1,1,1 },{ 0,0,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
	{ { 0,1,1,0 },{ 0,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
	{ { 0,1,1,1 },{ 0,0,0,1 },{ 0,0,0,0 },{ 0,0,0,0 } },
	{ { 0,1,1,0 },{ 0,0,1,1 },{ 0,0,0,0 },{ 0,0,0,0 } },
	{ { 0,1,0,0 },{ 1,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
	{ { 0,1,1,1 },{ 0,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },
	{ { 0,1,1,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } }
};//



//全局变量声明
bool g_hasBlocked[50][50];//
RECT rc_left, rc_right, rc_right_top, rc_right_bottom, rc_main;//
int g_speed = 300;//
int t_speed = 300;//
BOOL bCurTeris[4][4];//
BOOL bNextCurTeris[4][4];//
int curPosX, curPosY;//
int rd_seed = 1995421;//
int tPre = 0, tCur;//
int GAME_STATE = 0;//
int GAME_SCORE = 0;//
int GAME_DIFF = 1;//
int NUM_X = 10;//
int NUM_Y = 20;//
int BLOCK_SIZE = 30;//
int GAME_ROTATE = 0;

stdafx.h

// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once

#include "targetver.h"

#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>

// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>



// TODO: reference additional headers your program requires here



代码中的注释部分为个人编辑,存在错误,欢迎大家纠正

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基于STM32的俄罗斯方块游戏设计是使用STM32微控制器来实现俄罗斯方块游戏的功能。这款游戏的全部源码已经根据STM32进行设计,可以在正点原子开发板上运行。该代码采用寄存器风格编写,并且有详细的注释。 在方块的编码形式上,借用了Ucos-II的思想。通过定义一个一维数组,将19种方块利用枚举法进行编码,从而实现方块的随机生成。这样,每次游戏开始时可以生成不同的方块。 在游戏过程中,每当方块下落一次,需要进行满行的检测。当某一行存在满行时,该行会被消除,并且位于其上方的方块会整体下落。这个过程会给玩家加分。通过遍历一维数组,如果存在某个值为0xFFFF,表示该行可以被消除,之后会将该行上方的方块下移。 整个游戏的界面是基于一维数组进行设计,通过不断的方块下落和消除满行的操作,实现了俄罗斯方块游戏的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [基于STM32设计的俄罗斯方块小游戏.zip](https://download.csdn.net/download/xiaolong1126626497/20706125)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [基于STM32F407的俄罗斯方块游戏代码分析](https://blog.csdn.net/capture3333/article/details/125779728)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值