【Visual C++】游戏编程学习笔记之七:键盘输入消息
本系列文章由@二货梦想家张程 所写,转载请注明出处。
作者:ZeeCoder 微博链接:http://weibo.com/zc463717263
我的邮箱:michealfloyd@126.com 欢迎大家发邮件来和我交流编程心得
you are what you read!与大家共勉!
-------------------------------------------------分割线:ZeeCoder--------------------------------------------
在PC端我们常用到键盘上的快捷键来控制PC机进行相应的操作,游戏中也是如此。这篇笔记主要介绍如何通过键盘来控制人物跑动。
一、键盘输入消息
Windows系统是一个消息驱动的环境,一旦使用者在键盘上进行了输入操作,那么系统便会接收到对应的键盘消息。
整个消息响应的过程是:
使用者按下键盘、弹起键盘→产生虚拟键码→程序通过判断虚拟键码信息得知那个按键被按下→通过响应的函数来处理键盘消息。
常见的键盘消息有:按下键盘消息(WM_KEYDOWN)、弹起键盘消息(WM_KEYUP)和字符消息(WM_CHAR)。
二、键盘消息处理
WINDOWS程序通过WinProc函数来观察消息并响应。其原型:
<strong>LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)</strong>
其中,wParam的值为按下按键的虚拟键码。在程序中即通过判断这个变量来确定哪个按键按下。其具体处理函数示例如下:
<strong>LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_KEYDOWN://按下键盘消息
switch (wParam)//通过判断wParam来确定按下的按键
{
case VK_ESCAPE: //按下【ESC】键
//添加处理程序
PostQuitMessage(0);//退出程序
break;
case VK_UP: //按下【↑】键
//添加处理程序
break;
}
break;
case WM_DESTROY://窗口结束消息
PostQuitMessage(0); //向系统申请终止程序
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}</strong>
三、键盘控制人物移动实现
本笔记要通过【↑】【↓】【←】【→】实现控制人物移动。
首先我们需要四个方向的人物跑动图(下图以左移动示例)。
为了让动画更具备真实性,本程序中还要用到当没有键盘消息时人物站立的分解图。
当然我们还需要一张背景图。
OK,准备工作已经完成,下面看具体的代码实现。
四、代码实现
#include "stdafx.h"
#include "MyGame.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//定义全局变量
HDC hdc , mdc , bufdc;
HWND hWnd;
HBITMAP bg , Walker[8];
DWORD tNow , tPre ;
int dir , char_x = 0 , char_y = 0;//char_x,char_y为人物贴图坐标
int num;//人物跑动图中的编号
//定义自定义函数
void MyPaint(HDC hdc);
//***************************主函数**********************************
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
MSG msg;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_MYGAME, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
// Main message loop:
GetMessage(&msg,NULL,NULL,NULL); //初始化msg
while (msg.message != WM_QUIT)
{
if ( PeekMessage( &msg , NULL ,0 ,0 ,PM_REMOVE))//PM_REMOVE消息从队列里除掉
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
tNow = GetTickCount();//获取当前时间
if (tNow - tPre >= 100)//实现游戏循环
{
MyPaint(hdc);//循环贴图
}
}
}
return (int) msg.wParam;
}
//***************************窗口类函数**********************************
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYGAME));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_MYGAME);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//***************************初始化函数**********************************
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
TCHAR filename[20];
HBITMAP bmp;
memset(filename , 0 ,sizeof(TCHAR)*20);
int i ;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(_T("MyGame"), _T("Game"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
MoveWindow(hWnd , 10 , 10 , 1100 ,700 ,true);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
hdc = GetDC(hWnd);
mdc = CreateCompatibleDC(hdc);
bufdc = CreateCompatibleDC(hdc);
bmp = CreateCompatibleBitmap(hdc , 1100 , 720);
SelectObject(mdc , bmp);
//载入4方向跑动和站立图
for ( i = 0 ; i <= 7 ;i++)
{
_stprintf_s(filename , TEXT("%d.bmp") , i);
Walker[i] = (HBITMAP)LoadImage(NULL , filename , IMAGE_BITMAP , 536 ,182, LR_LOADFROMFILE);
}
//载入背景图
bg = (HBITMAP)LoadImage(NULL , _T("res.bmp") , IMAGE_BITMAP , 1100 ,720 , LR_LOADFROMFILE);
//初始化各变量
char_x = 300;
char_y = 300;
num = 0 ;
//调用自定义绘图程序
MyPaint(hdc);
return TRUE;
}
//***************************消息响应函数**********************************
//按下【↑】【↓】【←】【→】使人物跑动
//按下鼠标左键使人物跑动到指定位置
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_KEYDOWN:
switch (wParam)
{
case VK_ESCAPE: //按下【ESC】键退出
PostQuitMessage(0);
break;
case VK_UP: //按下【↑】键
char_y -= 10 ;
if (char_y <=0)
{
char_y = 0;
}
dir = 3;
break;
case VK_DOWN://按下【↓】键
char_y += 10;
if (char_y >= 1100)
{
char_y = 1100;
}
dir = 0;
break;
case VK_LEFT://按下【←】键
char_x -= 10;
if (char_x <= 0)
{
char_x =0;
}
dir = 1;
break;
case VK_RIGHT://按下【→】键
char_x += 10;
if (char_x >= 720)
{
char_x = 720;
}
dir = 2;
break;
}
break;
case WM_KEYUP:
switch (wParam)
{
case VK_UP: //弹起【↑】键
dir = 7;
break;
case VK_DOWN://弹起【↓】键
dir = 4;
break;
case VK_LEFT://弹起【←】键
dir = 5;
break;
case VK_RIGHT://弹起【→】键
dir = 6;
break;
}
break;
case WM_DESTROY:
int i;
DeleteDC(mdc);
DeleteDC(bufdc);
for ( i = 0 ; i < 8 ; i++)
{
DeleteObject(Walker[i]);
}
DeleteObject(bg);
ReleaseDC(hWnd , hdc);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
void MyPaint(HDC hdc)
{
//贴背景图
SelectObject(bufdc , bg);
BitBlt(mdc , 0 , 0 , 1100 , 720 , bufdc , 0 , 0 ,SRCCOPY);
//贴人物行走或站立
SelectObject(bufdc , Walker[dir]);
BitBlt(mdc , char_x , char_y , 67 , 91 , bufdc , num*67 , 91 , SRCAND);
BitBlt(mdc , char_x , char_y , 67 , 91 , bufdc , num*67 , 0 , SRCPAINT);
//将最后的画面显示在窗口上
BitBlt(hdc , 0 ,0 , 1100 , 720 ,mdc , 0 , 0 ,SRCCOPY);
//获取当前时间
tPre = GetTickCount();
//动作分解图的图号+1
num++;
if (num == 8)
{
num = 0;
}
}
五、实现效果
笔记七就写到这里了。这可以算是一个小游戏,实现起来也不难。
---end
本笔记配套代码已上传,欢迎下载:【Visual C++】游戏编程学习笔记之七--配套代码
You are what you read!与大家共勉~
【Visual C++】游戏编程学习笔记之七:键盘输入消息的更多相关文章
- 【Visual C++】游戏编程学习笔记之八:鼠标输入消息(小demo)
本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder 微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.c ...
- 【Visual C++】游戏编程学习笔记之四:透明动画实现
本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44224963 作者:ZeeCod ...
- 【Visual C++】游戏编程学习笔记之六:多背景循环动画
本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44264153 作者:ZeeCod ...
- DirectX 11游戏编程学习笔记之8: 第6章Drawing in Direct3D(在Direct3D中绘制)(习题解答)
本文由哈利_蜘蛛侠原创,转载请注明出处.有问题欢迎联系2024958085@qq.com 注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候 ...
- DirectX 11游戏编程学习笔记之6: 第5章The Rendering Pipeline(渲染管线)
本文由哈利_蜘蛛侠原创,转载请注明出处.有问题欢迎联系2024958085@qq.com 注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候 ...
- 【Visual C++】游戏编程学习笔记之五:单一背景滚动
本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44224963 作者:ZeeCod ...
- 【Visual C++】游戏编程学习笔记之三:游戏循环的使用
本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44208419 作者:Zee ...
- 【Visual C++】游戏编程学习笔记之九:回合制游戏demo(剑侠客VS巡游天神)
本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder 微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.com ...
- 【Visual C++】游戏编程学习笔记之二:定时器的使用
本系列文章由@二货梦想家张程所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44195831 作者:ZeeCode ...
随机推荐
- Java异常处理-----java异常体系
再三思考后还是决定贴图,csdn的格式,我是真玩不转,对不起了各位,继续将就吧. 错误原因:内存溢出.需要的内存已经超出了java虚拟机管理的内存范围. 错误原因:找不到类文件. 错误(Error): ...
- 安卓6.0新特性在Fragment申请运行时权限
今天在Fragment申请权限时代码如下: public void getContacts(){ int flag = ActivityCompat.checkSelfPermission(getAc ...
- Makefile自动生成
automake/autoconf入门作为Linux下的程序开发人员,大家一定都遇到过Makefile,用make命令来编译自己写的程序确实是很方便.一般情况下,大家都是手工写一个简单Makefile ...
- 5.1.3.jvm java虚拟机系统参数查看
不同的参数配置对系统的执行效果有较大的影响,因此,我们有必要了解系统实际的运行参数. 1.1.1.1. -XX:+PrintVMOptions 参数-XX:+PrintVMOptions可以在程序运行 ...
- Swift延迟加载的一种用途
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 有以下一种情况: 我们试图用Cocoa的语音合成类NSSpee ...
- 为何不能在viewDidLoad方法中显示其他视图
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 你可以使得当视图控制器(view controller)显示自 ...
- Spring Boot微服务架构入门
概述 还记得在10年毕业实习的时候,当时后台三大框架为主流的后台开发框架成软件行业的标杆,当时对于软件的认识也就是照猫画虎,对于为什么会有这么样的写法,以及这种框架的优势或劣势,是不清楚的,Sprin ...
- 关于activitygroup过时,用frament替换操作
现在Fragment的应用真的是越来越广泛了,之前Android在3.0版本加入Fragment的时候,主要是为了解决Android Pad屏幕比较大,空间不能充分利用的问题,但现在即使只是在手机上, ...
- 6、Android Content Provider测试
如果你的应用中使用了Content Provider来与其他应用进行数据交互,你需要对Content Provider进行测试来确保正常工作. 创建Content Provider整合测试 在Andr ...
- antlr v4 使用指南连载2——准备环境
antlr v4 开发环境 从上一篇文章的例子中可以知道,antlr有一套自己的语法来声明目标语言的语法,因此它本身就需要编译或者使用antlr提供的api来读取这些语法规则,并使之可 ...