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

你可能感兴趣的文章
Position属性四个值:static、fixed、absolute和relative的区别和用法
查看>>
POSIX thread编程中关于临界区内条件变量的分析
查看>>
POSIX与程序可移植性
查看>>
posix多线程有感--自旋锁
查看>>
SpringBoot中集成海康威视SDK实现布防报警数据上传/交通违章图片上传并在linux上部署(附示例代码资源)
查看>>
POSIX标准和XSI扩展
查看>>
post install error,please remove node_moules before retry
查看>>
postcss-pxtorem 参数之selectorBlackList、exclude的用法
查看>>
Postek博思得标签打印机更换电脑,打印出来标签空白
查看>>
postfix+ dovecot搭建邮件服务器
查看>>
postfix在邮件服务器中的使用
查看>>
PostGIS 3.1.2软件安装详细教程(地图工具篇.8)
查看>>
PostGIS中获取所有EPSG的编码以及对应Proj4字符串
查看>>
SpringBoot中集成海康威视SDK实现布防报警数据上传/交通违章图片上传并在linux上部署(附示例代码资源)
查看>>
PostGIS在Windows上的下载与安装
查看>>
Qt开发——网络编程之UDP客户端
查看>>
postgis数据库优化_postgresql 性能优化
查看>>
postgis求面积、交集等相关函数
查看>>
postgis相关函数
查看>>
Postgres Docker版本安装mysql_fdw 插件
查看>>