《MFC游戏开发》笔记四 键盘响应和鼠标响应:让人物动起来

时间:2021-09-09 07:46:22

本系列文章由七十一雾央编写,转载请注明出处。

http://blog.csdn.net/u011371356/article/details/9327377

作者:七十一雾央 新浪微博:http://weibo.com/1689160943/profile?rightmod=1&wvr=5&mod=personinfo

这篇文章里有一个问题,可能会使一些同学感到困惑:在大家进行键盘操作后,改变了人物的位置,但是并没有刷新画面,按道理来说应该是看到不到人物的位置改变的(大家看到的还是之前的画面),但是由于雾央在绘图OnPaint中漏掉了一句代码ValidateRect(&m_client);缺少它会导致系统一直产生WM_PAINT消息,也就是会一直调用OnPaint函数,所以大家操作后立马就看到了人物的移动,详细的解释我写在了笔记五 定时器和简单动画。 雾央感到很抱歉,但是这里并不影响大家学习键盘响应和鼠标响应,这部分的知识是没有问题的。

by雾央 2013.7.15

在上一节笔记里,我们讲解了贴图的透明效果的实现,使得画面变得美观起来,但是大家也知道,游戏还有一个非常重要的部分:交互性。大家在玩游戏的时候,按下键盘,点击鼠标,游戏中的人物都会根据自己的操作而做出不同的行为,或者走动,或者攻击,或者释放技能,这些都是通过键盘响应和鼠标响应来实现的,通过学习今天的内容,大家就可以让自己的程序中人物接收指令,动起来了。

好了,就让我们开始今天的学习吧。

一、键盘响应

1.键盘消息

当我们在键盘上进行了操作后,那么系统便会接收到对应的键盘消息,下面我们列出最常用的2种键盘消息:

WM_KEYDOWN 按下按键的消息

WM_KEYUP 松开按键消息

当某一按键被按下时,伴随着这个操作所产生的是WM_KEYDOWN,当松开时产生WM_KEYUP消息。当程序接收到这些消息时。便可由虚拟键码的信息来得知是哪个按键被按下。

2.添加键盘响应函数

MFC不仅仅只是一个封装好WIN32 API的框架,它还提供了很多工具,来实现可视化的开发,减少程序员的工作量,比如添加键盘响应程序我们就可以通过“类向导“来进行。

大家点击VS2010菜单栏上的 项目->类向导 即可打开类向导。

《MFC游戏开发》笔记四 键盘响应和鼠标响应:让人物动起来

大家需要注意的是“类型“这一栏里要选择CChildView,其他的默认就好。我们需要添加键盘处理消息,所以要选择”消息“,雾央已经用红色框出来了。在消息中大家搜索到WM_KEYDOWN后,双击它就可以添加进消息了,然后点确就可以了。

完成上述步骤后,大家会发现在CChildView.cpp中已经多出了一段函数名为

void  CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)的代码

大家把函数里面的内容可以都删掉,我们会自己写处理程序。

我先解释一下这两个函数的参数意义:


UINT nChar
指定特定键的虚键控代码。大家是不是很疑惑,我们收到按键消息后,怎么知道是哪个键被按下了呢?答案就是nChar这个参数了。大家如果按下了某个字母键,那么nChar存储的就是这个字母键的ASCII码,如果是上下左右键呢,那么就分别是VK_LEFT,VK_RIGHT,VK_UP,VK_DOWN这几个宏了。其他的键游戏中使用不多,我就不多说了,若有需要,大家百度“按键的虚拟值“即可查到。

UINT nRepCnt,保存了键被重击的次数。

UINT nFlags ,定扫描代码,键转换代码,前一个关键状态。

后两个大家可以先不用关心,很少用到。

我们现在想要实现的是按下WSAD键,人物就会分别往上下左右移动,这个要怎么实现呢?大家思考一下就会清楚,我们只要在要移动到的位置重新把人物贴出来,他不就移动过去了吗?

要达到这样的效果,我们就不能再把贴图函数的坐标写成数值形式了,我们可以用变量保存人物的位置,在按下WSAD键后,改变人物的位置就可以了,具体代码请见最后,雾央对每一句都加上了注释,这里先给大家看一下效果图,呵呵。

《MFC游戏开发》笔记四 键盘响应和鼠标响应:让人物动起来

向右上移动n步后

《MFC游戏开发》笔记四 键盘响应和鼠标响应:让人物动起来

二、鼠标响应

在游戏中,鼠标的作用可以使控制人物的行走,与NPC对话,购买东西等,鼠标增加了游戏操作的很多灵活性,是很多游戏都不可缺少的部分,接下来我们就讲解一下鼠标响应。

其实大家如果弄懂了键盘响应后,鼠标响应非常简单。鼠标的消息有下面这些种

WM_LBUTTONDBLCLK 双击鼠标左键消息

WM_LBUTTONDOWN 单击鼠标左键消息

WM_LBUTTONUP 松开鼠标左键消息

WM_MBUTTONDBLCLK 双击鼠标中键(滚轮)消息

WM_MBUTTONDOWN 单击鼠标中键(滚轮)消息

WM_MBUTTONUP 松开鼠标中键(滚轮)消息

WM_RBUTTONDBLCLK 双击鼠标右键消息

WM_RBUTTONDOWN 单击鼠标右键消息

WM_RBUTTONUP 松开鼠标右键消息

WM_MOUSEMOVE 鼠标移动消息

WM_MOUSEWHEEL 鼠标滚轮转动消息

大家需要处理什么样的消息,在类向导中添加它就可以了。

我们以鼠标左键单击消息处理为例,它添加的函数有两个参数

参数一:nFlags

指示是否各种虚拟键是否被按下。这个参数可以是任何以下值的组合:

MK_CONTROL 如果Ctrl键按下时设置这个标志。

MK_LBUTTON 如果鼠标左键按下时设置这个标志。

MK_MBUTTON 如果鼠标中键按下时设置这个标志。

MK_RBUTTON 如果鼠标右键按下时设置这个标志。

MK_SHIFT 如果Shift键按下时设置这个标志。

这个大家也可以不用管,在我们的教程中几乎用不到。

参数二:CPoint point

大家查看CPoint类型就可以发现,它包含两个成员变量x和y,就分别是鼠标的位置了,注意是相对于窗口左上角的,我想大家都知道程序中窗口的左上角是(0,0)点,向右为x正方向,向下为y正方向吧。

我们的程序就设定为点击鼠标左键后,将人物的左上角移动到鼠标的位置。

三、源代码

大家如果有什么不清楚的地方可以看源代码,我尽量详细的添加了注释,大家如果有什么疑问可以留言。大家如果需要运行的直接复制进大家建的工程中就可以运行了,注意在工程下要放入图片,要不然会出错的哦。我使用的是相对路径,所以大家应该把图片放在工程文件夹下。

首先是头文件

// ChildView.h : CChildView 类的接口
// #pragma once // CChildView 窗口 class CChildView : public CWnd
{
// 构造
public:
CChildView(); // 特性
public:
CRect m_client; //保存客户区大小
CRect m_heroPos; //保存英雄的位置
CImage m_hero; //英雄
CImage m_bg; //背景图片
// 操作
public: // 重写
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs); // 实现
public:
virtual ~CChildView(); // 生成的消息映射函数
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};

然后是CPP文件

// ChildView.cpp : CChildView 类的实现
// #include "stdafx.h"
#include "GameMFC.h"
#include "ChildView.h" #ifdef _DEBUG
#define new DEBUG_NEW
#endif // CChildView CChildView::CChildView()
{
} CChildView::~CChildView()
{
} BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_KEYDOWN()
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP() //将png贴图透明
void TransparentPNG(CImage *png)
{
for(int i = 0; i <png->GetWidth(); i++)
{
for(int j = 0; j <png->GetHeight(); j++)
{
unsigned char* pucColor = reinterpret_cast<unsigned char *>(png->GetPixelAddress(i , j));
pucColor[0] = pucColor[0] * pucColor[3] / 255;
pucColor[1] = pucColor[1] * pucColor[3] / 255;
pucColor[2] = pucColor[2] * pucColor[3] / 255;
}
}
} // CChildView 消息处理程序 BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE; cs.dwExStyle |= WS_EX_CLIENTEDGE;
cs.style &= ~WS_BORDER;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL); //加载背景
m_bg.Load("bg.png"); //加载英雄图片
m_hero.Load("hero.png");
TransparentPNG(&m_hero); //设置英雄初始位置
m_heroPos.left=100; //人物左边贴在100的位置
m_heroPos.right=100+60; //人物的右边等于左边加上人物的宽度
m_heroPos.top=400;
m_heroPos.bottom=400+60; return TRUE;
} void CChildView::OnPaint()
{
//获取窗口DC指针
CDC *cDC=this->GetDC();
//获取窗口大小
GetClientRect(&m_client);
//贴背景
m_bg.Draw(*cDC,m_client);
//贴英雄
m_hero.Draw(*cDC,m_heroPos); //释放DC
ReleaseDC(cDC);
} //按键响应函数
void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
//nChar表示按下的键值
switch(nChar)
{
case 'd': //游戏中按下的键当然应该不区分大小写了
case 'D':
m_heroPos.left+=10; //向右移动10个像素的单位
m_heroPos.right+=10; //左边和右边都要移动哦
break;
case 'a':
case 'A':
m_heroPos.left-=10;
m_heroPos.right-=10;
break;
case 'w':
case 'W':
m_heroPos.top-=10;
m_heroPos.bottom-=10;
break;
case 's':
case 'S':
m_heroPos.top+=10;
m_heroPos.bottom+=10;
break;
}
} //鼠标左键单击响应函数
void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
m_heroPos.left=point.x;
m_heroPos.right=m_heroPos.left+60;
m_heroPos.top=point.y;
m_heroPos.bottom=m_heroPos.top+60;
}

《MFC游戏开发》笔记四到这里就结束了,更多精彩请关注下一篇。如果您觉得文章对您有帮助的话,请留下您的评论,点个赞,能看到你们的留言是我最高兴的事情,因为这让我知道我正在帮助曾和我一样迷茫的少年,你们的支持就是我继续写下去的动力,愿我们一起学习,共同努力,复兴国产游戏。

对于文章的疏漏或错误,欢迎大家的指出。