因为把目标定在了游戏上,最近在学习win32编程
经过一段时间的学习,开始进行编程实现俄罗斯方块,记录一下其中遇到的一些问题
一开始是看网上的教程学习的,但是做完一部分之后发现 会有闪屏的情况 所以又去网上搜索了一下发现要用到双缓冲。
注意:这段开始正确性有待考证!
我一开始的时候是把
drawTeris();
drawBackGround();
drawBlocked();
都放在了WM_PAINT事件里
而且里面都用了双缓冲来绘制,结果导致后面闪屏更加频繁,后来把所有的绘制动作都交由一个缓冲区来处理,然后删掉其他函数中的双缓冲相关代码并加入drawBackGround中,这样在WM_PAINT事件中只需要调用一次drawBackground就行了 而且也没发现有闪屏了。
另外还发现一个比较奇葩的问题:
一个用了BOOL类型的变量后面把他当作bool类型进行memcpy的时候会出问题
贴上在网上找到的资料:
windows为了兼容问题定义的基础变量。
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef float FLOAT;
typedef FLOAT *PFLOAT;
typedef BOOL near *PBOOL;
typedef BOOL far *LPBOOL;
typedef BYTE near *PBYTE;
typedef BYTE far *LPBYTE;
typedef int near *PINT;
typedef int far *LPINT;
根据上面这段可以发现其实BOOL是个int类型的
附上 代码
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 int CheckValide(int curPosX,int curPosY,BOOL bCurTeris[4][4]); //给定一个矩阵,查看是否合法 int selectDiffculty(HWND hWnd,int dif); int selectLayOut(HWND hWnd,int layout); //常量声明 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;
tetris.cpp:
// Tetris.cpp : 定义应用程序的入口点。 // /* 记录于2016-5-29 星期日 由于闪屏过于显眼,采用双缓冲 */ #include "stdafx.h" #include "Tetris.h" #include "windows.h" #include <mmsystem.h> #pragma comment(lib, "WINMM.LIB") #define MAX_LOADSTRING 100 // 此代码模块中包含的函数的前向声明: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); // 全局变量: HINSTANCE hInst; // 当前实例 TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { init_game(); UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。 MSG msg; HACCEL hAccelTable; // 初始化全局字符串 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_TETRIS, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // 执行应用程序初始化: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TETRIS)); // 主消息循环: 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); if (flag==1) { curPosY++; tPre = tCur; HWND hWnd = GetActiveWindow(); InvalidateRect(hWnd,&rc_left,FALSE); InvalidateRect(hWnd,&rc_right_top,FALSE); } 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) { HWND hWnd = GetActiveWindow(); if (MessageBox(hWnd,L"胜败乃兵家常事,菜鸡请重新来过",L":时光机",MB_YESNO)==IDYES) { init_game(); } else { break; } } } } } } return (int) msg.wParam; } // // 函数: MyRegisterClass() // // 目的: 注册窗口类。 // // 注释: // // 仅当希望 // 此代码与添加到 Windows 95 中的“RegisterClassEx” // 函数之前的 Win32 系统兼容时,才需要此函数及其用法。调用此函数十分重要, // 这样应用程序就可以获得关联的 // “格式正确的”小图标。 // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TETRIS)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCE(IDC_TETRIS); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); } // // 函数: InitInstance(HINSTANCE, int) // // 目的: 保存实例句柄并创建主窗口 // // 注释: // // 在此函数中,我们在全局变量中保存实例句柄并 // 创建和显示主程序窗口。 // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // // 函数: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: 处理主窗口的消息。 // // WM_COMMAND - 处理应用程序菜单 // WM_PAINT - 绘制主窗口 // WM_DESTROY - 发送退出消息并返回 // // 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 = GetSystemMenu(hWnd,false); AppendMenu(hSysmenu,MF_SEPARATOR,0,NULL); AppendMenu(hSysmenu,0,IDM_DIFF,L"难度选择"); break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // 分析菜单选择: 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; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_KEYDOWN: hdc = GetDC(hWnd); InvalidateRect(hWnd,NULL,false); switch(wParam) { case VK_LEFT: curPosX--; if(CheckValide(curPosX,curPosY,bCurTeris)!=1) { curPosX++; } break; case VK_RIGHT: curPosX++; if(CheckValide(curPosX,curPosY,bCurTeris)!=1) { curPosX--; } break; case VK_UP: 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': RotateTeris(bCurTeris); break; case 'A': curPosX--; if(CheckValide(curPosX,curPosY,bCurTeris)!=1) { curPosX++; } break; case 'D': curPosX++; if(CheckValide(curPosX,curPosY,bCurTeris)!=1) { curPosX--; } break; case 'S': if (g_speed==t_speed) g_speed =10 ; else g_speed = t_speed; //outPutBoxInt(g_speed); break; default: break; } case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: 在此添加任意绘图代码... DrawBackGround(hdc); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // “关于”框的消息处理程序。 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); PlaySound(_T("coin.wav"),NULL,SND_FILENAME|SND_ASYNC); } } GAME_SCORE += int(count*1.5); } 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(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; 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 (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(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); 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(0,188,0)); SelectObject(hdc,hBrush); 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)); 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)); 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)); 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); }
于2016-5-29