zoukankan      html  css  js  c++  java
  • windows下实现win32俄罗斯方块练手,编程的几点心得

    编程珠玑2阅读笔记:

    1.使用c语言性能监视器,完成对代码的调优工作

    2.关联数组:
     拓扑排序算法,可以用于当存在遮挡的时候决定三维场景的绘制顺序。

    3.小型算法中的测试与调试工具
    脚手架程序:《人月神话》一个软件产品中应该有一半的代码都是脚手架。
    类似,小型的代码库

    4.自描述数据
    每个程序员都知道破解神秘数据的挫折与艰辛。

    5.劈开戈尔迪之结
    什么是用户的真正需求:
    一个运筹学者接到任务,设计末座大楼的电梯调度策略,使乘客等待的时间最短,在走访了这座大楼之后,他认识到雇主真正想要解决的问题是,尽量减少乘客的不适( 乘客不喜欢等电梯)。他这样解决问题:在每部电梯附近装上几面镜子。乘客在等电梯时候,可以自我欣赏一下,对电梯速度的抱怨大幅减少了。他发现了用户的真正需求

    7.粗略估算
    程序员3大美德:对数值敏感,实验的欲望,良好的数学功底

    在来说这个俄罗斯方块,其实主要是2个大的部分:

    1.界面绘制(游戏区,信息区,刷新重绘工作)

    游戏区方块的绘制,其实都是数组来记录


    2.游戏逻辑(上下左右,变形)

    其实就是对数组的旋转


    主要代码,才六百行:


    // Russian_cube.cpp : 定义应用程序的入口点。
    //
    //
    //
    //
    #include "stdafx.h"
    #include "Russian_cube.h"
    
    #define MAX_LOADSTRING 100
    //Tetris
    #define BOUND_SIZE 10
    #define TETRIS_SIZE 30
    #define GAME_X 10
    #define GAME_Y 20
    #define INFO_X 6
    #define INFO_Y GAME_Y
    
    //定时器
    #define  MY_TIMEER 1
    #define  DEFAULT_INTERVAL 500 //默认每0.5秒下降一格
    
    //定义俄罗斯方块的形状
    BOOL g_astTetris[][4][4] = 
    {
    	{{1,1,0,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
    	{{1,1,0,0},{0,0,1,1},{0,0,0,0},{0,0,0,0}},
    	{{1,1,0,0},{1,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}},
    	{{0,1,0,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}},
    	{{1,1,1,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}}
    };
    #define  TETRIS_CNT (sizeof(g_astTetris)/sizeof(g_astTetris[0]))
    
    
    //当前方块的形状
    BOOL g_CurTetris[4][4];
    BOOL g_NextTetris[4][4];
    BOOL g_stGame[GAME_X][GAME_Y];//记录已经落下来的方块
    
    //记录方块左上角的坐标
    UINT TetrisX;
    UINT TetrixY;
    UINT g_uiInterval;
    UINT g_uiScore;
    
    UINT g_uiMySeed = 0xffff;
    
    // 全局变量:
    HINSTANCE hInst;								// 当前实例
    TCHAR szTitle[MAX_LOADSTRING];					// 标题栏文本
    TCHAR szWindowClass[MAX_LOADSTRING];			// 主窗口类名
    
    // 此代码模块中包含的函数的前向声明:
    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 _tWinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPTSTR    lpCmdLine,
                         int       nCmdShow)
    {
    	UNREFERENCED_PARAMETER(hPrevInstance);
    	UNREFERENCED_PARAMETER(lpCmdLine);
    
     	// TODO: 在此放置代码。
    	MSG msg;
    	HACCEL hAccelTable;
    
    	// 初始化全局字符串
    	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    	LoadString(hInstance, IDC_RUSSIAN_CUBE, szWindowClass, MAX_LOADSTRING);
    	MyRegisterClass(hInstance);
    
    	// 执行应用程序初始化:
    	if (!InitInstance (hInstance, nCmdShow))
    	{
    		return FALSE;
    	}
    
    	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_RUSSIAN_CUBE));
    
    	// 主消息循环:
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    		{
    			TranslateMessage(&msg);
    			DispatchMessage(&msg);
    		}
    	}
    
    	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_RUSSIAN_CUBE));
    	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
    	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
    	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_RUSSIAN_CUBE);
    	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_MINIMIZEBOX | WS_SYSMENU,
          CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    
       if (!hWnd)
       {
          return FALSE;
       }
    
       ShowWindow(hWnd, nCmdShow);
       UpdateWindow(hWnd);
    
       return TRUE;
    }
    
    int GetRandNum(int iMin,int iMax)
    {
    	//取随机数
    	srand(GetTickCount() + g_uiMySeed-- );
    
    	return iMin + rand()%(iMax -iMin);
    }
    
    VOID DrawBackGround(HDC hdc)
    {
    	int x, y;
    	HPEN hPen = (HPEN)GetStockObject(NULL_PEN);
    	HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
        //(HBRUSH)CreateSolidBrush()
    
    	
    	HBRUSH hBrush_luo = (HBRUSH)GetStockObject(BLACK_BRUSH);
    
    	Rectangle(hdc,BOUND_SIZE,BOUND_SIZE,BOUND_SIZE + GAME_X*TETRIS_SIZE
    		,BOUND_SIZE + GAME_Y * TETRIS_SIZE);
    
    	SelectObject(hdc,hPen);
    	
    	for (x = 0; x < GAME_X; x++)
    	{
    		for (y = 0 ;y < GAME_Y; y++)
    		{
    			if (g_stGame[x][y])
    			{
    				SelectObject(hdc,hBrush_luo);
    			}
    			else
    			{
    				
    				SelectObject(hdc,hBrush);
    			}
    			Rectangle(hdc,BOUND_SIZE + x*TETRIS_SIZE,
    				BOUND_SIZE + y * TETRIS_SIZE,
    				BOUND_SIZE + (x + 1)*TETRIS_SIZE,
    				BOUND_SIZE + (y + 1) * TETRIS_SIZE);
    		}
    	}
    }
    //信息区 的绘制
    VOID DrawInfo(HDC hdc)
    {
    	int x,y;
    	int nStartX,nStartY;
    	RECT rect;
    	TCHAR szBuf[100];//得分的字符串
    
    	HPEN hPen = (HPEN)GetStockObject(BLACK_PEN);
    	HBRUSH hBrush = (HBRUSH)GetStockObject(NULL_BRUSH);
    
    	HBRUSH hBrush_have = (HBRUSH)GetStockObject(GRAY_BRUSH);
    	SelectObject(hdc,hPen);
    	SelectObject(hdc,hBrush);
    
    	Rectangle(hdc,BOUND_SIZE*2 + GAME_X * TETRIS_SIZE,
    		BOUND_SIZE,BOUND_SIZE *2 + (GAME_X + INFO_X)*TETRIS_SIZE,
    		BOUND_SIZE + INFO_Y * TETRIS_SIZE);
    
    	for (x = 0; x < 4; x++)
    	{
    		for (y = 0 ;y < 4 ;y++)
    		{
    			nStartX = BOUND_SIZE *2 + GAME_X*TETRIS_SIZE + (y +1)*TETRIS_SIZE;
    			nStartY = BOUND_SIZE + (x +1)*TETRIS_SIZE;
    			if (g_NextTetris[x][y])
    			{
    				SelectObject(hdc,hBrush);
    			}
    			else
    			{
    				SelectObject(hdc,hBrush_have);
    			}
    			Rectangle(hdc,nStartX,nStartY,nStartX+TETRIS_SIZE,nStartY+TETRIS_SIZE);
    		}
    	}
    
    	nStartX = BOUND_SIZE *2 + GAME_X*TETRIS_SIZE;
    	nStartY = BOUND_SIZE ;
    
    	rect.left = nStartX + TETRIS_SIZE;
    	rect.right = nStartX + TETRIS_SIZE * (INFO_X -1);
    	rect.top = nStartY + TETRIS_SIZE *6;
    	rect.bottom = nStartY + TETRIS_SIZE *7;
    
    	wsprintf(szBuf,L"Score: %d",g_uiScore = 0);
    
    	 DrawText(hdc,szBuf,wcslen(szBuf),&rect,DT_CENTER);
    
    }
    
    //绘制区方块,起始坐标和需要绘制的方块形状
    VOID DrawTetris(HDC hdc, int nStartX,int nStartY,BOOL bTetris[4][4])
    {
    	int i,j;
    	HPEN hPen = (HPEN)GetStockObject(BLACK_PEN);
    	HBRUSH hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
    	SelectObject(hdc,hPen);
    	SelectObject(hdc,hBrush);
    
    	for (i = 0;i < 4; i++)
    	{
    		for (j = 0;j < 4;j++)
    		{
    			//j 是x方向的坐标偏移
    			if (bTetris[i][j])
    			{
    				Rectangle(hdc,BOUND_SIZE +(nStartX + j) * TETRIS_SIZE,
    					BOUND_SIZE + (nStartY + i)* TETRIS_SIZE,
    					BOUND_SIZE +(nStartX + j + 1) * TETRIS_SIZE,
    					BOUND_SIZE + (nStartY + i + 1)* TETRIS_SIZE);
    			}
    		}
    	}
    }
    
    //旋转方块, 并且靠左上角
    VOID RotateTetris(BOOL bTetris[4][4])
    {
    	BOOL bNewTetris[4][4] = {};//初始化置零
    	int x, y;
    	int xPos,yPos;
    	BOOL bFlag;//靠近左上角
    
    	//从上往下,从左往右,顺时针旋转
    	//靠上
    	for (x = 0,xPos = 0 ;x < 4 ; x++)
    	{
    		bFlag = FALSE;
    		for (y = 0 ;y < 4 ;y++)
    		{
    			bNewTetris[xPos][y] = bTetris[3 - y][x];
    			//逆时针旋转
    			//bNewTetris[x][y] = bTetris[y][3 - x];
    			if (bNewTetris[xPos][y])
    			{
    				bFlag = TRUE;//这一行有数据
    			}
    		}
    		if (bFlag)
    		{
    			xPos++;
    		}
    	}
    
    
    	memset(bTetris,0,sizeof(bNewTetris));
    	//靠左
    	for (y = 0, yPos = 0;y < 4 ;y++)
    	{
    		bFlag = FALSE;
    		for (x = 0;x < 4; x++)
    		{
    			bTetris[x][yPos] = bNewTetris[x][y];
    			if (bTetris[x][yPos])
    			{
    				bFlag = TRUE;
    			}
    		}
    		if (bFlag)
    		{
    			yPos++;
    		}
    	}
    	//memcpy(bTetris,bNewTetris,sizeof(bNewTetris));
    	return;
    }
    BOOL CheckTetris(int nStartX,int nStartY,BOOL bTetris[4][4],BOOL bGame[GAME_X][GAME_Y])
    {
    	int x,y;
    	if (nStartX < 0)
    	{//碰到左墙
    		return FALSE;
    	}
    
    	for (x = 0;x < 4;x++)
    	{
    		for (y = 0;y < 4;y++)
    		{
    			if (bTetris[x][y])
    			{
    				//碰右墙
    				if (nStartX +y >=GAME_X)
    				{
    					return FALSE;
    				}
    				//碰下墙
    				if (nStartY + x >=GAME_Y)
    				{
    					return FALSE;
    				}
    				//碰到已有的方块
    				if (bGame[nStartX +y][nStartY + x])
    				{
    					return FALSE;
    				}
    			}
    		}
    	}
    	return TRUE;
    }
    
    //落地的方块合并,并且满足消除一行
    VOID RefreshTetris(int nStartX,int nStartY,BOOL bTetris[4][4], BOOL bGame[GAME_X][GAME_Y])
    {
    	BOOL bFlag = FALSE;
    	int x,y;
    	int newX,newY;//主要用来记录
    	int iFulllie = 0; //校区满行的格子记录行数,用于积分
    
    	for (x = 0; x < 4;x ++)
    	{
    		for (y = 0 ;y < 4; y++)
    		{
    			if (bTetris[x][y])
    			{
    				bGame[nStartX + y][nStartY +x] = TRUE;
    			}
    		}
    	}
    
    	for (y = GAME_Y,newY = GAME_Y; y >= 0; y--)
    	{
    		bFlag= FALSE;
    		for (x = 0;x < GAME_X;x++)
    		{
    			bGame[x][newY] = bGame[x][y];
    			if (!bGame[x][y])//这一行不满格
    			{
    				bFlag = TRUE;
    			}
    		}
    		if (bFlag)
    		{
    			newY--;
    		}
    		else
    		{
    			//满格的话,用上一行替换这一行
    			iFulllie++;
    		}
    	}
    
    	if (iFulllie)
    	{
    		g_uiScore -= iFulllie *1;
    	}
    	//合并以后生成新的方块,并刷新位置
    	memcpy(g_CurTetris,g_NextTetris,sizeof(g_CurTetris));
    	memcpy(g_NextTetris,g_astTetris[ GetRandNum(0,TETRIS_CNT)],sizeof(g_NextTetris));
    
    	TetrisX = (GAME_X - 4)/2;
    	TetrixY = 0;
    }
    
    //初始化游戏,就是方块最先初始化的位置
    VOID InitGame()
    {
    	int iTmp;
    
    	TetrisX = (GAME_X - 4 )/2 ; //居中
    	TetrixY = 0;
    
    	g_uiScore = 0;
    	g_uiInterval = DEFAULT_INTERVAL;
    
    	iTmp = GetRandNum(0,TETRIS_CNT);
    	memcpy(g_CurTetris,g_astTetris[iTmp],sizeof(g_CurTetris));
    
    	iTmp = GetRandNum(0,TETRIS_CNT);
    	memcpy(g_NextTetris,g_astTetris[iTmp],sizeof(g_NextTetris));
    
    	memset(g_stGame,0,sizeof(g_stGame));
    }
    //
    //  函数: 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;
    	RECT rect;
    	BOOL bTmpTetris[4][4] = {};
    
    	switch (message)
    	{
    	case WM_CREATE:
    		//获取窗口大小
    		GetWindowRect(hWnd,&rect);
    		nWinX = rect.right - rect.left;
    		nWinY = rect.bottom - rect.top;
    		//获取客户区大小
    		GetClientRect(hWnd,&rect);
    		nClientX = rect.right - rect.left;
    		nClientY = rect.bottom - rect.top;
    
    		MoveWindow(hWnd,0,0,3 * BOUND_SIZE + (GAME_X + INFO_X)* TETRIS_SIZE +
    			(nWinX  - nClientX),
    			2 * BOUND_SIZE + GAME_Y*TETRIS_SIZE + (nWinY - nClientY),true);
    		InitGame();
    		SetTimer(hWnd,MY_TIMEER,g_uiInterval,NULL);
    		break;
    	case WM_TIMER:
    		//定时器中方块下降
    		if (CheckTetris(TetrisX,TetrixY + 1,g_CurTetris,g_stGame))
    		{
    			TetrixY++;
    		}
    		else
    		{
    			if (TetrixY == 0)
    			{
    				MessageBox(NULL,L"不行了",L"shit!",MB_OK);
    				KillTimer(hWnd,MY_TIMEER);
    			}
    			RefreshTetris(TetrisX,TetrixY,g_CurTetris,g_stGame);
    		}
    		InvalidateRect(hWnd,NULL,TRUE);
    
    		break;
    	case WM_LBUTTONDOWN:
    		RotateTetris(g_CurTetris);
    		InvalidateRect(hWnd,NULL,TRUE);
    		break;
    	case WM_KEYDOWN:
    		switch(wParam)
    		{
    		case VK_LEFT://左方向键
    			if (CheckTetris(TetrisX -1,TetrixY,g_CurTetris,g_stGame))
    			{//判断一下当前方块没有靠墙就
    				TetrisX--;
    				InvalidateRect(hWnd,NULL,TRUE);
    			}
    			else
    			{
    				MessageBeep(0);
    			}
    			break;
    		case  VK_RIGHT:
    			if (CheckTetris(TetrisX +1,TetrixY,g_CurTetris,g_stGame))
    			{//判断一下当前方块没有靠墙就
    				TetrisX++;
    				InvalidateRect(hWnd,NULL,TRUE);
    			}
    			else
    			{
    				MessageBeep(0);
    			}
    			break;
    		case VK_UP://变形,但是要判断变形成功或者失败
    			memcpy(bTmpTetris,g_CurTetris,sizeof(bTmpTetris));
    			RotateTetris((bTmpTetris));
    			if (CheckTetris(TetrisX,TetrixY,bTmpTetris,g_stGame))
    			{
    				//成功后,再把旋转后的copy回来
    				memcpy(g_CurTetris,bTmpTetris,sizeof(bTmpTetris));
    				InvalidateRect(hWnd,NULL,TRUE);
    			}
    			break;
    		case VK_DOWN:
    			while (CheckTetris(TetrisX,TetrixY + 1,g_CurTetris,g_stGame))
    			{
    				TetrixY++;
    				
    			}
    			RefreshTetris(TetrisX,TetrixY,g_CurTetris,g_stGame);
    			InvalidateRect(hWnd,NULL,TRUE);
    			break;
    		default:
    			break;
    		}
    		
    		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;
    		default:
    			return DefWindowProc(hWnd, message, wParam, lParam);
    		}
    		break;
    	case WM_PAINT:
    		hdc = BeginPaint(hWnd, &ps);
    		// TODO: 在此添加任意绘图代码...
    		DrawBackGround(hdc);
    		DrawInfo(hdc);
    		DrawTetris(hdc,TetrisX,TetrixY,g_CurTetris);
    		EndPaint(hWnd, &ps);
    		break;
    	case WM_DESTROY:
    		KillTimer(hWnd,MY_TIMEER);
    		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;
    }
    


    代码参考:

    主要是听了这个课程,这个公开课做点小项目,贪吃蛇,网络啊什么的,都是代码挺好的,作为一个熟悉其他领域的小项目非常适合上手:

    http://study.163.com/course/courseLearn.htm?courseId=1367011#/learn/video?lessonId=1738230&courseId=1367011


    windows下的win32编程要学的东西还比较多,下面给出一个简单的知识点:



  • 相关阅读:
    设计模式系列
    Python3 系列之 可变参数和关键字参数
    设计模式系列
    【HANA系列】SAP HANA ODBC error due to mismatch of version
    【FICO系列】SAP FICO FS00修改科目为未清项目管理
    【FIORI系列】SAP OpenUI5 (SAPUI5) js框架简单介绍
    【HANA系列】SAP HANA SQL获取当前日期加若干天后的日期
    【HANA系列】SAP HANA SQL获取本周的周一
    【HANA系列】SAP HANA SQL获取当前日期
    【HANA系列】SAP HANA SQL获取当前日期最后一天
  • 原文地址:https://www.cnblogs.com/wangyaning/p/7853913.html
Copyright © 2011-2022 走看看