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编程要学的东西还比较多,下面给出一个简单的知识点:



  • 相关阅读:
    [Spring] ClassPathXmlApplicationContext类
    [mybatis-spring] Transaction 事务/事务处理/事务管理器
    [mybatis-spring]sqlSessionFactoryBean
    [ /* 和 / 的区别 ] Difference between / and /* in servlet mapping url pattern
    [Database]各数据库连接配置:Oracle:thin 数据库连接/MySQL 连接配置
    MySQL utf8 和 utf8mb4 的区别
    MySQL 8.0 安装时 Authentication Method
    bootstrap
    Batch
    mysqlsh : mysql shell tutorial
  • 原文地址:https://www.cnblogs.com/wuyida/p/6301281.html
Copyright © 2011-2022 走看看