本文共 2983 字,大约阅读时间需要 9 分钟。
由于最近要找工作,为了熟悉编程环境,怀着学习MFC的热情,编写了这个小程序。本文主要探讨了MFC环境下的绘图和按键控制,过程中遇到了一些技术难点,其中第四节的关键技术攻克尤为重要,希望能得到各位大神的指导。
本应用程序主要包含一个红球和30个蓝球,用户通过按下F2键开始游戏,利用方向键控制红球的移动方向,避免与蓝球碰撞。游戏背景采用经典的魂斗罗音乐,用户需要记录红球的逃逸时间,测试应变能力。程序界面较为简陋,但功能基本完成。
在MFC中,如果将对话框的背景设置为白色,需要手动重绘对话框。为此,可以创建一个白色画刷,并在对话框的CtlColor事件中返回该画刷,实现对话框背景的重绘。
#define WRITE RGB(255, 255, 255)HBRUSH CEscapeBallDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); if (nCtlColor == CTLCOLOR_DLG) return m_Brush; return hbr;} 为了避免按回车键时关闭窗口,我们采用了PreTranslateMessage消息预处理函数,隐藏了默认的按键处理逻辑,实现了按F2键开始游戏的功能。
BOOL CEscapeBallDlg::PreTranslateMessage(MSG* pMsg) { if (pMsg->wParam == VK_RETURN) return TRUE; if (pMsg->wParam == VK_F2 && !m_bEscaping) { BeginGame(); return CDialog::PreTranslateMessage(pMsg); } return CDialog::PreTranslateMessage(pMsg);} 为了提升游戏氛围,我们采用了MFC的PlaySound函数,将WAV格式的音乐文件作为资源嵌入到项目中,实现了背景音乐的循环播放。
PlaySound(MAKEINTRESOURCE(IDR_WAVE_HUNDOULUO), NULL, SND_RESOURCE | SND_ASYNC | SND_LOOP);
程序中采用了简单的碰撞算法来控制球的运动方向。蓝球的运动方向主要有两种情况:一是按随机方向运动,二是根据红球的位置作为运动方向。具体实现如下:
void CEscapeBallDlg::ComputeForward(BallInfo* bBall, BallInfo* rBall, BOOL is_rand) { int x = rBall->pos_x, y = rBall->pos_y; int x1 = bBall->pos_x, y1 = bBall->pos_y; if (is_rand) { x1 = rand() % m_MaxX; y1 = rand() % m_MaxY; } offset_x = fabs((x - x1)) / sqrt(pow((x - x1), 2) + pow((y - y1), 2)) * MOVEOFFSET; offset_y = fabs((y - y1)) / sqrt(pow((x - x1), 2) + pow((y - y1), 2)) * MOVEOFFSET; bBall->offset_x = (x1 - x) < 0 ? -offset_x : offset_x; bBall->offset_y = (y1 - y) < 0 ? -offset_y : offset_y;} 在控制方向键时,我们发现按键按下后会有短暂的延迟,尤其是在长时间按键时会影响用户体验。通过分析,我们发现这是由于操作系统底层对按键状态的判断时间引起的。
经过多次尝试,我们发现线程化处理按键状态可以有效解决这个问题。具体实现如下:
UINT CEscapeBallDlg::RedBallMoveFunc(LPVOID lParam) { CEscapeBallDlg* pDlg = (CEscapeBallDlg*)lParam; while (pDlg->m_bEscaping) { BYTE keyState = 0; keyState |= (GetKeyState(VK_UP) & 0x8000) ? 0x08 : 0; keyState |= (GetKeyState(VK_DOWN) & 0x8000) ? 0x04 : 0; keyState |= (GetKeyState(VK_LEFT) & 0x8000) ? 0x02 : 0; keyState |= (GetKeyState(VK_RIGHT) & 0x8000) ? 0x01 : 0; if ((keyState & 0x01) && (keyState & 0x02)) keyState &= 0x0C; if ((keyState & 0x04) && (keyState & 0x08)) keyState &= 0x03; switch (keyState) { case UP: offset_x = 0, offset_y = -MOVEOFFSET; break; case DOWN: offset_x = 0, offset_y = MOVEOFFSET; break; case LEFT: offset_x = -MOVEOFFSET, offset_y = 0; break; case RIGHT: offset_x = MOVEOFFSET, offset_y = 0; break; // 其他方向类似 } if (offset_x || offset_y) { pDlg->ReDrawRedBall(offset_x, offset_y); if (!pDlg->CheckEscape()) pDlg->EndGame(); } Sleep(30); } return 0;} 通过线程化处理按键状态,有效解决了按键延迟问题,提升了用户操作体验。
转载地址:http://iygoz.baihongyu.com/