博客
关于我
逃逸的小球(BALL Escape)
阅读量:644 次
发布时间:2019-03-14

本文共 2983 字,大约阅读时间需要 9 分钟。

由于最近要找工作,为了熟悉编程环境,怀着学习MFC的热情,编写了这个小程序。本文主要探讨了MFC环境下的绘图和按键控制,过程中遇到了一些技术难点,其中第四节的关键技术攻克尤为重要,希望能得到各位大神的指导。


功能介绍

本应用程序主要包含一个红球和30个蓝球,用户通过按下F2键开始游戏,利用方向键控制红球的移动方向,避免与蓝球碰撞。游戏背景采用经典的魂斗罗音乐,用户需要记录红球的逃逸时间,测试应变能力。程序界面较为简陋,但功能基本完成。


采用MFC技术

2.1 控件背景重绘

在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;}

2.2 按键响应

为了避免按回车键时关闭窗口,我们采用了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);}

2.3 播放音乐

为了提升游戏氛围,我们采用了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;}

关键技术攻克

4.1 问题研究

在控制方向键时,我们发现按键按下后会有短暂的延迟,尤其是在长时间按键时会影响用户体验。通过分析,我们发现这是由于操作系统底层对按键状态的判断时间引起的。

4.2 解决方法

经过多次尝试,我们发现线程化处理按键状态可以有效解决这个问题。具体实现如下:

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/

你可能感兴趣的文章
SQL Server 复制 订阅与发布
查看>>
OSPF技术连载20:OSPF 十大LSA类型,太详细了!
查看>>
OSPF技术连载21:OSPF虚链路,现代网络逻辑连接的利器!
查看>>
OSPF技术连载22:OSPF 路径选择 O > O IA > N1 > E1 > N2 > E2
查看>>
OSPF技术连载2:OSPF工作原理、建立邻接关系、路由计算
查看>>
OSPF技术连载5:OSPF 基本配置,含思科、华为、Junifer三厂商配置
查看>>
OSPF技术连载6:OSPF 多区域,近7000字,非常详细!
查看>>
OSPF技术连载7:什么是OSPF带宽?OSPF带宽参考值多少?
查看>>
OSPF技术连载8:OSPF认证:明文认证、MD5认证和SHA-HMAC验证
查看>>
OSPF故障排除技巧
查看>>
spring配置文件中<context:property-placeholder />的使用
查看>>
OSPF有哪些优势?解决了RIP的什么问题?
查看>>
OSPF理论
查看>>
OSPF的七种类型LSA
查看>>
OSPF的安全性考虑:全面解析与最佳实践
查看>>
OSPF知识点大全,网络工程师快速收藏!
查看>>
ospf综合实验2 2012/9/8
查看>>
OSPF规划两大模型:双塔奇兵、犬牙交错
查看>>
OSPF认证
查看>>
OSPF设计原则,命令以H3C为例
查看>>