本系列文章由@二货梦想家张程 所写,转载请注明出处。

作者: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++】游戏编程学习笔记之七:键盘输入消息的更多相关文章

  1. 【Visual C++】游戏编程学习笔记之八:鼠标输入消息(小demo)

     本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder  微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.c ...

  2. 【Visual C++】游戏编程学习笔记之四:透明动画实现

    本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44224963 作者:ZeeCod ...

  3. 【Visual C++】游戏编程学习笔记之六:多背景循环动画

    本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44264153 作者:ZeeCod ...

  4. DirectX 11游戏编程学习笔记之8: 第6章Drawing in Direct3D(在Direct3D中绘制)(习题解答)

            本文由哈利_蜘蛛侠原创,转载请注明出处.有问题欢迎联系2024958085@qq.com         注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候 ...

  5. DirectX 11游戏编程学习笔记之6: 第5章The Rendering Pipeline(渲染管线)

            本文由哈利_蜘蛛侠原创,转载请注明出处.有问题欢迎联系2024958085@qq.com         注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候 ...

  6. 【Visual C++】游戏编程学习笔记之五:单一背景滚动

    本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44224963 作者:ZeeCod ...

  7. 【Visual C++】游戏编程学习笔记之三:游戏循环的使用

     本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44208419 作者:Zee ...

  8. 【Visual C++】游戏编程学习笔记之九:回合制游戏demo(剑侠客VS巡游天神)

    本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder  微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.com ...

  9. 【Visual C++】游戏编程学习笔记之二:定时器的使用

    本系列文章由@二货梦想家张程所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44195831 作者:ZeeCode ...

随机推荐

  1. DOS界面下的翻译软件制作

    准备 素材 依赖 接口 地址 参数 返回值解析 编码及测试 功能代码 运行脚本 环境变量 结果展示 英语转汉语 汉语转英语 总结 昨天看到一篇关于Linux下的桌面词典的文章,于是就想实现一个Wind ...

  2. 安卓开发:简单的登陆跳转_APK实现直接跳转到本CSDN博客

    最近在开始接触Android APP开发,有了一点java基础之后,安卓代码确实看起来就没有那么难了,可以跟着书上把例程敲一遍,然后熟能生巧可以应用起来,现在写了一个简单的APP,实现的是Edit编辑 ...

  3. iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge

    上一篇文章介绍了UIWebView 如何通过WebViewJavascriptBridge 来实现JS 与OC 的互相调用,这一篇来介绍一下WKWebView 又是如何通过WebViewJavascr ...

  4. Linux--FTP和MAIL服务器

     1) FTP协议 FTP(FileTransfer Protocol,文件传输协议)用于管理计算机之间的文件传送.FTP 是Internet 上使用非常广泛的一种通讯协议,它是由支持Intern ...

  5. [ExtJS5学习笔记]第七节 Extjs5的组件components及其模板事件方法学习

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/38487519 本文作者:sushengmiyan ------------------ ...

  6. ExtJS学习(二)Ext组件模型

    Ext中所有的组件都继承自Ext.component,这种单根继承的模型保证所有组件都拥有相同的通用方法与生命周期,这样在后续对这些组件进行维护管理时将更加便捷,同时也保证了在进行布局时的便利. 组件 ...

  7. iOS开发关于适配iOS10以及Xcode8的相关新知识

    一.证书管理 用Xcode8打开工程后,比较明显的就是下图了,这个是苹果的新特性,可以帮助我们自动管理证书.建议大家勾选这个Automatically manage signing(Ps.但是在bea ...

  8. Java基础---Java---IO流-----File 类、递归、删除一个带内容的目录、列出指定目录下文件夹、FilenameFilte

    File 类 用来将文件或者文件夹封装成对象 方便对文件与文件夹进行操作. File对象可以作为参数传递给流的构造函数 流只用操作数据,而封装数据的文件只能用File类 File类常见方法: 1.创建 ...

  9. 音乐API

    博主在前几篇博客中介绍了小Q聊天机器人的源码及其包含的一些功能,并在应用市场上上线了一个版本,其中有一个功能是歌曲搜索,即输入格式为"歌曲#歌曲名#歌手"即可搜索出相应的歌曲并进行 ...

  10. 【安卓中的缓存策略系列】安卓缓存策略之磁盘缓存DiskLruCache

    安卓中的缓存包括两种情况即内存缓存与磁盘缓存,其中内存缓存主要是使用LruCache这个类,其中内存缓存我在[安卓中的缓存策略系列]安卓缓存策略之内存缓存LruCache中已经进行过详细讲解,如看官还 ...