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
代码中的注释部分为个人编辑,存在错误,欢迎大家纠正