博客
关于我
逃逸的小球(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/

你可能感兴趣的文章
Plotly:如何从单条迹线制作堆积条形图?
查看>>
Plotly:如何以 Root 样式绘制直方图,仅显示直方图的轮廓?
查看>>
Plotly:如何使用 Plotly Express 组合散点图和线图?
查看>>
Plotly:如何使用 plotly.graph_objects 和 plotly.express 定义图形中的颜色?
查看>>
Plotly:如何使用 Python 对绘图对象条形图进行颜色编码?
查看>>
Plotly:如何使用 updatemenus 更新一个特定的跟踪?
查看>>
Plotly:如何使用长格式或宽格式的 pandas 数据框制作线图?
查看>>
Plotly:如何向烛台图添加交易量
查看>>
Plotly:如何在 plotly express 中找到趋势线的系数?
查看>>
Plotly:如何在桑基图中设置节点位置?
查看>>
Plotly:如何处理重叠的颜色条和图例?
查看>>
Plotly:如何手动设置 plotly express 散点图中点的颜色?
查看>>
Plotly:如何结合 make_subplots() 和 ff.create_distplot()?
查看>>
Plotly:如何绘制累积的“步骤“;直方图?
查看>>
Quartz进一步学习与使用
查看>>
Plotly条形图-根据正/负值更改颜色-python
查看>>
PLSQL developer12安装图解
查看>>
PLSQL Developer调试 存储过程和触发器
查看>>
PLSQL window操作
查看>>
plsql 存储过程 测试
查看>>