• 【MFC】打砖块小游戏(下)(7)


    任务点:

    1、键盘左右键消息处理;

    2、碰撞检测(与砖块、挡板、上、左、右);

    3、控制转向;

    程序shix

    解决思路:

    1、左右键消息处理:

    响应 WM_KEYDOWN 消息,移动挡板(如果能够的话,重新绘制窗口即可)

    1. case WM_KEYDOWN:
    2. {
    3. switch(wParam)
    4. {
    5. case VK_LEFT:
    6. if(bafflex>=5)
    7. {
    8. bafflex-=5;
    9. hdc=::GetDC(hWnd);
    10. Draw(hdc);
    11. ::ReleaseDC(hWnd,hdc);
    12. }
    13. break;
    14. case VK_RIGHT:
    15. if(bafflex+110<=445)
    16. {
    17. bafflex+=5;
    18. hdc=::GetDC(hWnd);
    19. Draw(hdc);
    20. ::ReleaseDC(hWnd,hdc);
    21. }
    22. break;
    23. default: //其他消息发给windows默认窗口处理函数
    24. return (DefWindowProc(hWnd,message,wParam,lParam));
    25. }
    26. }

    2、检测球与砖块的碰撞,通过砖块数组下标,计算砖块位置 :

    左上(j * 45,i * 20)   右下 ((j +1)* 45,( i + 1)* 20)

    判断条件:

    bally+5 >= i * 20 && bally+5 <= (i+1) *20 && ballx +5>= j * 45 && ballx+5 <= (j+1) *45;

    另外,也可以反过来用 bally / 20    ballx / 45 计算出当前的(i ,j  )要注意数组越界

    砖块碰撞后的处理:

    消除砖块( 数组中 (i,j) 元素置 0 )  球速水平、垂直 逆转

        brick_array[i][j] = 0;                    cy = -cy;                    cx=-cx;

    3、检测球与窗口边缘(左、右、上)以及挡板(是否接住)的碰撞

    1. bool isBlock()
    2. {
    3. if(bally <= 0) cy=-cy;
    4. if(ballx <= 0 || ballx >= 435) cx=-cx;
    5. if(bally + 5 >= baffley)
    6. {
    7. if(ballx < bafflex || ballx > bafflex +111)
    8. {
    9. return false;
    10. }
    11. cy =-cy;
    12. }
    13. return true;
    14. }

    上、左、 右 分别逆转球的运动方向,最后判断是否接住: 先看球已经掉落到挡板位置,然后判断是否在挡板范围内

    4、球没接住的后续处理:

    ::KillTimer(hWnd,1);    // 停了时钟,然后后续的处理

    5、简单粗暴完成版本代码如下:

    主函数:SDK_3.cpp

    1. // SDK_3.cpp : 定义应用程序的入口点。
    2. //
    3. #include "stdafx.h"
    4. #include "SDK_3.h"
    5. #include "SDK3_API.h"
    6. #define MAX_LOADSTRING 100
    7. // 全局变量:
    8. HINSTANCE hInst; // 当前实例
    9. TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
    10. TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
    11. // 此代码模块中包含的函数的前向声明:
    12. ATOM MyRegisterClass(HINSTANCE hInstance);
    13. BOOL InitInstance(HINSTANCE, int);
    14. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    15. INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
    16. extern int bafflex; //外部引用全局变量
    17. int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
    18. _In_opt_ HINSTANCE hPrevInstance,
    19. _In_ LPTSTR lpCmdLine,
    20. _In_ int nCmdShow)
    21. {
    22. UNREFERENCED_PARAMETER(hPrevInstance);
    23. UNREFERENCED_PARAMETER(lpCmdLine);
    24. // TODO: 在此放置代码。
    25. MSG msg;
    26. HACCEL hAccelTable;
    27. // 初始化全局字符串
    28. LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    29. LoadString(hInstance, IDC_SDK_3, szWindowClass, MAX_LOADSTRING);
    30. MyRegisterClass(hInstance);
    31. // 执行应用程序初始化:
    32. if (!InitInstance (hInstance, nCmdShow))
    33. {
    34. return FALSE;
    35. }
    36. hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SDK_3));
    37. // 主消息循环:
    38. while (GetMessage(&msg, NULL, 0, 0))
    39. {
    40. if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    41. {
    42. TranslateMessage(&msg);
    43. DispatchMessage(&msg);
    44. }
    45. }
    46. return (int) msg.wParam;
    47. }
    48. //
    49. // 函数: MyRegisterClass()
    50. //
    51. // 目的: 注册窗口类。
    52. //
    53. ATOM MyRegisterClass(HINSTANCE hInstance)
    54. {
    55. WNDCLASSEX wcex;
    56. wcex.cbSize = sizeof(WNDCLASSEX);
    57. wcex.style = CS_HREDRAW | CS_VREDRAW;
    58. wcex.lpfnWndProc = WndProc;
    59. wcex.cbClsExtra = 0;
    60. wcex.cbWndExtra = 0;
    61. wcex.hInstance = hInstance;
    62. wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SDK_3));
    63. wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    64. wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    65. wcex.lpszMenuName = MAKEINTRESOURCE(IDC_SDK_3);
    66. wcex.lpszClassName = szWindowClass;
    67. wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    68. return RegisterClassEx(&wcex);
    69. }
    70. //
    71. // 函数: InitInstance(HINSTANCE, int)
    72. //
    73. // 目的: 保存实例句柄并创建主窗口
    74. //
    75. // 注释:
    76. //
    77. // 在此函数中,我们在全局变量中保存实例句柄并
    78. // 创建和显示主程序窗口。
    79. //
    80. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    81. {
    82. HWND hWnd;
    83. hInst = hInstance; // 将实例句柄存储在全局变量中
    84. hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
    85. CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    86. if (!hWnd)
    87. {
    88. return FALSE;
    89. }
    90. ShowWindow(hWnd, nCmdShow);
    91. UpdateWindow(hWnd);
    92. return TRUE;
    93. }
    94. //
    95. // 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
    96. //
    97. // 目的: 处理主窗口的消息。
    98. //
    99. // WM_COMMAND - 处理应用程序菜单
    100. // WM_PAINT - 绘制主窗口
    101. // WM_DESTROY - 发送退出消息并返回
    102. //
    103. //
    104. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    105. {
    106. int wmId, wmEvent;
    107. PAINTSTRUCT ps;
    108. HDC hdc;
    109. switch (message)
    110. {
    111. case WM_CREATE:
    112. Init(hWnd);
    113. break;
    114. case WM_KEYDOWN:
    115. {
    116. switch(wParam)
    117. {
    118. case VK_LEFT:
    119. if(bafflex>=5)
    120. {
    121. bafflex-=5;
    122. hdc=::GetDC(hWnd);
    123. Draw(hdc);
    124. ::ReleaseDC(hWnd,hdc);
    125. }
    126. break;
    127. case VK_RIGHT:
    128. if(bafflex+110<=445)
    129. {
    130. bafflex+=5;
    131. hdc=::GetDC(hWnd);
    132. Draw(hdc);
    133. ::ReleaseDC(hWnd,hdc);
    134. }
    135. break;
    136. default: //其他消息发给windows默认窗口处理函数
    137. return (DefWindowProc(hWnd,message,wParam,lParam));
    138. }
    139. }
    140. case WM_COMMAND:
    141. wmId = LOWORD(wParam);
    142. wmEvent = HIWORD(wParam);
    143. // 分析菜单选择:
    144. switch (wmId)
    145. {
    146. case IDC_START:
    147. ::SetTimer(hWnd,1,10,TimerProc);
    148. break;
    149. case IDM_ABOUT:
    150. DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
    151. break;
    152. case IDM_EXIT:
    153. DestroyWindow(hWnd);
    154. break;
    155. default:
    156. return DefWindowProc(hWnd, message, wParam, lParam);
    157. }
    158. break;
    159. case WM_PAINT:
    160. hdc = BeginPaint(hWnd, &ps);
    161. // TODO: 在此添加任意绘图代码...
    162. Draw(hdc);
    163. EndPaint(hWnd, &ps);
    164. break;
    165. case WM_DESTROY:
    166. PostQuitMessage(0);
    167. break;
    168. default:
    169. return DefWindowProc(hWnd, message, wParam, lParam);
    170. }
    171. return 0;
    172. }
    173. // “关于”框的消息处理程序。
    174. INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    175. {
    176. UNREFERENCED_PARAMETER(lParam);
    177. switch (message)
    178. {
    179. case WM_INITDIALOG:
    180. return (INT_PTR)TRUE;
    181. case WM_COMMAND:
    182. if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
    183. {
    184. EndDialog(hDlg, LOWORD(wParam));
    185. return (INT_PTR)TRUE;
    186. }
    187. break;
    188. }
    189. return (INT_PTR)FALSE;
    190. }

    自定义的API头文件:SDK3_API.h

    1. #pragma once
    2. void Init(HWND hWnd);
    3. void Draw(HDC hdc );
    4. void WINAPI TimerProc(HWND, UINT, UINT, DWORD);
    5. bool isHit(int i,int j);
    6. bool isBlock();
    7. void Check();
    8. void MoveBaffle(int x);

    SDK3_API.cpp

    1. #include "stdafx.h"
    2. #include "SDK_3.h"
    3. #include "resource.h"
    4. #include "SDK3_API.h"
    5. extern HINSTANCE hInst;
    6. BYTE brick_array[4][10]={
    7. {0,1,1,1,1,1,1,1,1,0},
    8. {0,0,1,1,1,1,1,1,0,0},
    9. {0,0,0,1,1,1,1,0,0,0},
    10. {0,0,0,0,1,1,0,0,0,0}
    11. };
    12. HBITMAP ball,brick,baffle,tempBitmap;
    13. HDC hMemDC,hTempDC;
    14. int ballx=225,bally=420,bafflex=170,baffley=430,cx=2,cy=-2;
    15. void Init(HWND hWnd)
    16. {
    17. baffle = ::LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BITMAP1));
    18. ball = ::LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BITMAP2));
    19. brick = ::LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BITMAP3));
    20. ::SetWindowPos(hWnd,
    21. NULL,
    22. (::GetSystemMetrics(SM_CXFULLSCREEN) - 450)/2,
    23. (::GetSystemMetrics(SM_CYFULLSCREEN) - 450)/2,
    24. 450,
    25. 500,
    26. SWP_NOZORDER);
    27. HDC hdc = ::GetDC(hWnd);
    28. hMemDC = ::CreateCompatibleDC(hdc);
    29. hTempDC=::CreateCompatibleDC(hdc);
    30. tempBitmap = ::CreateCompatibleBitmap(hdc,450,500);
    31. ::SelectObject(hTempDC,tempBitmap);
    32. ::ReleaseDC(hWnd,hdc);
    33. }
    34. void Draw(HDC hdc )
    35. {
    36. int i,j;
    37. ::PatBlt(hTempDC,0,0,450,500,WHITENESS);
    38. ::SelectObject(hMemDC,brick);
    39. for(i = 0; i < 4; i++)
    40. {
    41. for(j = 0; j < 10; j++)
    42. {
    43. if(brick_array[i][j] == 1)
    44. {
    45. ::BitBlt(hTempDC,j * 45,i * 20,45,20,hMemDC,0,0,SRCCOPY);
    46. }
    47. }
    48. }
    49. ::SelectObject(hMemDC,ball);
    50. ::BitBlt(hTempDC,ballx,bally,10,10,hMemDC,0,0,SRCCOPY);
    51. ::SelectObject(hMemDC,baffle);
    52. ::BitBlt(hTempDC,bafflex,baffley,111,10,hMemDC,0,0,SRCCOPY);
    53. ::BitBlt(hdc,0,0,450,500,hTempDC,0,0,SRCCOPY);
    54. }
    55. void WINAPI TimerProc(HWND hWnd, UINT nMsg,UINT nTimerid,DWORD dwTime)
    56. {
    57. if(nTimerid ==1)
    58. {
    59. ballx+=cx;
    60. bally+=cy;
    61. Check();
    62. if(!isBlock())
    63. {
    64. ::KillTimer(hWnd,1);
    65. return;
    66. }
    67. HDC hdc=::GetDC(hWnd);
    68. Draw(hdc);
    69. ::ReleaseDC(hWnd,hdc);
    70. }
    71. }
    72. bool isHit(int i,int j)
    73. {
    74. return bally+5 >= i * 20 && bally+5 <= (i+1) *20 && ballx +5>= j * 45 && ballx+5 <= (j+1) *45;
    75. }
    76. bool isBlock()
    77. {
    78. if(bally <= 0) cy=-cy;
    79. if(ballx <= 0 || ballx >= 435) cx=-cx;
    80. if(bally + 5 >= baffley)
    81. {
    82. if(ballx < bafflex || ballx > bafflex +111)
    83. {
    84. return false;
    85. }
    86. cy =-cy;
    87. }
    88. return true;
    89. }
    90. void Check()
    91. {
    92. int i,j;
    93. for(i = 0; i < 4; i++)
    94. {
    95. for(j = 0; j < 10; j++)
    96. {
    97. if(brick_array[i][j] == 1)
    98. {
    99. if(isHit(i,j))
    100. {
    101. brick_array[i][j] = 0;
    102. cy = -cy;
    103. cx=-cx;
    104. }
    105. }
    106. }
    107. }
    108. }

    挡板图片 (111 * 10 )

     球图片(10*10)

    砖块图片(45 *20)

     

     以上素材可以利用画图工具来完成,如修改尺寸,则需要调整代码中很多数值(窗口大小等等)。

    后记:

    1、本程序为初学Windows C SDK 窗口程序设计 做的一个比较 “粗糙” 的 小游戏,初衷是快速了解窗口程序设计的流程,以及消除对窗口编程的 “恐惧”,所以,很多 细节 都以舍弃,也写了很多很不规划的代码;

    2、从以上的例子可以看出,窗口编程急需解决的问题是 “在哪里写代码” (通过消息处理来确定),“怎么写代码”(熟悉窗口元素的处理,利用以前学的编程逻辑,解决实际问题);

    3、打砖块游戏的后续处理:如何重新开始游戏、如何设置游戏难度、如何更精细化处理游戏碰撞等等,都可以深入去处理一下。

     

  • 相关阅读:
    【Spring-5.3】AbstractAutowireCapableBeanFactory#initializeBean实现Bean的初始化
    2246: 【区赛】【宁波32届小学生】最佳交换
    自动化工具-文件清单对比
    二维二硫化钼纳米片负载纳米银复合材料|二硫化钼碳纳米管复合材料|纳米金/磁性纳米颗粒修饰二硫化钼纳米片
    【Vue 开发实战】基础篇 # 14:template和JSX的对比以及它们的本质
    Spring注解开发和XML开发
    【JavaScript】JS引擎中执行上下文如何顺序执行代码
    JVM源码剖析之线程的创建过程
    Mybatis动态xml中sql语句拼接参数#和$使用
    js直接操作数据库会怎么样
  • 原文地址:https://blog.csdn.net/yixiaobo2001/article/details/127885928