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

作者:ZeeCoder  微博链接:http://weibo.com/zc463717263

我的邮箱:michealfloyd@126.com   欢迎大家发邮件来和我交流编程心得

you are what you read!与大家共勉!

-------------------------------------------------分割线:ZeeCoder--------------------------------------------

这篇笔记是在【Visual C++】游戏编程学习笔记之七:键盘输入消息的基础上添加了鼠标输入消息,也算完成一个2D游戏的demo(很糙的demo)---剑侠客扔飞镖。

废话就不多说,由于利用到了鼠标输入消息,那么先来讲讲鼠标相关函数的知识。

一、获取鼠标外的消息:

函数原型:

HWND SetCapture( HWND hWnd);  //设定获取窗口外的鼠标消息
BOOL ReleaseCapture( VOID );  //释放获取窗口外的鼠标消息

如果调用了SetCapture()函数,并输入要取得鼠标消息的窗口代号,那么便可以取得鼠标在窗口外的所发出的消息。与之相对应使用的是ReleaseCapture()用于释放窗口外鼠标消息的函数。

二、设定鼠标光标位置

为了防止游戏开始时鼠标出现在不该出现的位置,在程序初始化的时候通常要预先设定好鼠标光标的位置,那么我们就需要用到:

 //设定光标位置
   pt.x = 300;
   pt.y = 300;
   ClientToScreen(hWnd , &pt);//将pt的坐标转换成屏幕坐标
   SetCursorPos(pt.x , pt.y);//设定鼠标光标的初始位置

由于SetCursorPos()需要设定的是相对于屏幕左上角的屏幕坐标,所以需要先利用ClientToScreen()将需要设定的坐标值转换成屏幕坐标。

三、显示或隐藏鼠标光标

int ShowCursor(BOOL true&FALSE); //隐藏或显示鼠标光标

四、限制鼠标光标移动区域

在游戏过程中为了避免鼠标移到窗口外造成游戏bug,,往往需要限制鼠标只能在窗口内移动。

//限制光标移动区域
   GetClientRect(hWnd , (LPRECT)&rect);//获取窗口的四个顶点坐标
   lt.x = rect.left;//矩形窗口右上点x坐标
   lt.y = rect.top;//矩形窗口右上点y坐标
   rb.x = rect.right;//矩形窗口左下点x坐标
   rb.y = rect.bottom;//矩形窗口左下点y坐标
   ClientToScreen(hWnd , &lt);//转换成屏幕坐标
   ClientToScreen(hWnd , &rb);
   rect.left = lt.x;
   rect.top = lt.y;
   rect.right = rb.x;
   rect.bottom = rb.y;
   ClipCursor(&rect);//限制鼠标光标移动位置

五、关于鼠标函数的相关知识就介绍到这了,下面看整个demo是怎么实现的。

游戏说明:【↑】【↓】【←】【→】控制人物 行走

鼠标左键控制角色扔流星镖。

#include "stdafx.h"
#include "MyGame.h"

#define MAX_LOADSTRING 100
#define SPEED 6  //定义跑动速度

//自定义结构体
struct Mydart
{
	int x , y ;
	bool exist;
};

//定义全局变量
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name
HDC hdc , mdc , bufdc;
HWND hWnd;
HBITMAP bg , Walker[8] , dart;
DWORD tNow , tPre ;
int dir , char_x = 0 , char_y = 0;//char_x,char_y为人物贴图坐标
int num , dart_num;//人物跑动图中的编号
int mouse_x , mouse_y;
int dcount = 0 ;
Mydart mydart[20];

// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
//定义自定义函数
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];
   POINT pt , lt , rb ;
   RECT rect;

   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);
   dart = (HBITMAP)LoadImage(NULL , _T("dart.bmp") , IMAGE_BITMAP , 100 , 96 , LR_LOADFROMFILE);

   //初始化各变量
   char_x = 300;
   char_y = 300;

   //设定光标位置
   pt.x = 300;
   pt.y = 300;
   ClientToScreen(hWnd , &pt);
   SetCursorPos(pt.x , pt.y);
   //隐藏鼠标光标
   //ShowCursor(FALSE);

   //限制光标移动区域
   GetClientRect(hWnd , (LPRECT)&rect);//获取窗口的四个顶点坐标
   lt.x = rect.left;//矩形窗口右上点x坐标
   lt.y = rect.top;//矩形窗口右上点y坐标
   rb.x = rect.right;//矩形窗口左下点x坐标
   rb.y = rect.bottom;//矩形窗口左下点y坐标
   ClientToScreen(hWnd , &lt);//转换成屏幕坐标
   ClientToScreen(hWnd , &rb);
   rect.left = lt.x;
   rect.top = lt.y;
   rect.right = rb.x;
   rect.bottom = rb.y;
   ClipCursor(&rect);//限制鼠标光标移动位置

   num = 0 ;
   dart_num = 0 ;

   //调用自定义绘图程序
   MyPaint(hdc);

   return TRUE;
}

//***************************消息响应函数**********************************
//按下【↑】【↓】【←】【→】使人物跑动
//按下鼠标左键使人物跑动到指定位置
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	POINT cursor;

	switch (message)
	{
	case WM_KEYDOWN:

		switch (wParam)
		{
		case VK_ESCAPE:	//按下【ESC】键退出
			PostQuitMessage(0);
			break;
		case VK_UP: //按下【↑】键
			char_y -= SPEED ;
			if (char_y <=0)
			{
				char_y = 0;
			}
			dir = 3;
			break;
		case VK_DOWN://按下【↓】键
			char_y += SPEED;
			if (char_y >= 720)
			{
				char_y = 720;
			}
			dir = 0;
			break;
		case VK_LEFT://按下【←】键
			char_x -= SPEED;
			if (char_x <= 0)
			{
				char_x =0;
			}
			dir = 1;
			break;
		case VK_RIGHT://按下【→】键
			char_x += SPEED;
			if (char_x >= 1100)
			{
				char_x = 1100;
			}
			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_LBUTTONDOWN:
		int i ;
		for (i = 0 ; i < 20 ; i++)
		{
			if (!mydart[i].exist)
			{
				switch (dir)
				{
				case 0:
				case 4://人物面朝下
					mydart[i].x = char_x + 33;
					mydart[i].y = char_y + 91;
					break;
				case 1:
				case 5://人物面朝左
					mydart[i].x = char_x ;
					mydart[i].y = char_y + 20;
					break;
				case 2:
				case 6://人物面朝右
					mydart[i].x = char_x + 30;
					mydart[i].y = char_y + 20;
					break;
				case 7:
				case 3://人物面朝上
					mydart[i].x = char_x + 33;
					mydart[i].y = char_y ;
					break;
				}
				mydart[i].exist = true;
				dcount++;
				break;
			}
		}
	case WM_MOUSEMOVE:
		mouse_x = LOWORD(lParam);
		if (mouse_x > 1100)
		{
			mouse_x = 1100;
		}
		mouse_y = HIWORD(lParam);
		if (mouse_y > 720)
		{
			mouse_y = 720;
		}
		break;
	case WM_DESTROY:
		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)
{
	int i = 0;
	TCHAR str[20] ;
	memset(str , 0 ,sizeof(TCHAR)*20);
	//贴背景图
	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);

	//贴飞镖
	SelectObject(bufdc , dart);
	if (dcount != 0)
	{
		for (i = 0 ; i < 20 ; i++)
		{
			if (mydart[i].exist)
			{
				BitBlt(mdc , mydart[i].x , mydart[i].y , 50 , 48 , bufdc , dart_num*50 , 48 , SRCAND);
				BitBlt(mdc , mydart[i].x , mydart[i].y , 50 , 48 , bufdc , dart_num*50 , 0 , SRCPAINT);
				switch (dir)
				{
				case 0:
				case 4://人物面朝下
					mydart[i].y += 20;
					if (mydart[i].y > 720)
					{
						dcount-- ;
						mydart[i].exist = false;
					}
					break;
				case 1:
				case 5://人物面朝左
					mydart[i].x -= 20;
					if (mydart[i].x < 0)
					{
						dcount-- ;
						mydart[i].exist = false;
					}
					break;
				case 2:
				case 6://人物面朝右
					mydart[i].x += 20;
					if (mydart[i].x > 1100)
					{
						dcount-- ;
						mydart[i].exist = false;
					}
					break;
				case 3:
				case 7://人物面朝上
					mydart[i].y -= 20;
					if (mydart[i].y < 0)
					{
						dcount-- ;
						mydart[i].exist = false;
					}
					break;
				}

			}
		}
	}

	//显示鼠标坐标
	TextOut(mdc , 10 , 10 ,_T("鼠标坐标:"),strlen("鼠标坐标") );
	_stprintf_s(str , TEXT("X坐标 :%d   ") , mouse_x);
	TextOut(mdc , 10 , 30 , str , _tcslen(str));
	_stprintf_s(str , TEXT("Y坐标 :%d   ") ,mouse_y);
	TextOut(mdc , 10 , 50 , str , _tcslen(str));

	//将最后的画面显示在窗口上
	BitBlt(hdc , 0 ,0 , 1100 , 720 ,mdc , 0 , 0 ,SRCCOPY);
	//获取当前时间
	tPre = GetTickCount();
	//动作分解图的图号+1
	num++;
	if (num == 8)
	{
		num = 0;
	}
	dart_num++;
	if (dart_num == 2)
	{
		dart_num = 0;
	}

}

六、游戏效果图



游戏编程学习笔记八就写到这了,游戏的具体效果请自行下载源码后运行。

写完代码看到效果图后,我想我是不是毁了剑侠客~~代码中还存在很多bug,做的不算完美。希望在后续的学习中能够有更好的办法修改和完善。

---end!

笔记八的配套代码已上传,欢迎下载:【visual C++】游戏编程笔记八配套代码

【Visual C++】游戏编程学习笔记之八:鼠标输入消息(小demo)的更多相关文章

  1. 【Visual C++】游戏编程学习笔记之七:键盘输入消息

     本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者: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++】游戏编程学习笔记之九:回合制游戏demo(剑侠客VS巡游天神)

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

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

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

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

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

随机推荐

  1. 【SSH系列】Hibernate映射 -- 一对多关联映射

        映射原理       一对多关联映射和多对一关联映射的映射原理是一样一样的,所以说嘛,知识都是相通的,一通百通,为什么说一对多关联映射和多对一关联映射是一样的呢?因为她们都是在多的一端加入一个 ...

  2. MT8127:改变安卓系统权限问题

    找到对应的文件: system/extras/su/su.c 在main函数中,开头我们可以看下以下代码: uid_t current_uid = getuid(); if (current_uid ...

  3. 【安卓开发】为什么不能往Android的Application对象里存储数据

    在一个App里面总有一些数据需要在多个地方用到.这些数据可能是一个 session token,一次费时计算的结果等.通常为了避免activity之间传递对象的开销 ,这些数据一般都会保存到持久化存储 ...

  4. spark下使用submit提交任务后报jar包已存在错误

    使用spark submit进行任务提交,离线跑数据,提交后的一段时间内可以application可以正常运行.过了一段时间后,就抛出以下错误: org.apache.spark.SparkExcep ...

  5. cocos2dx 3.2之Lua打飞机项目

    1          创建lua打飞机项目 cocos new T32Lua -dE:\Installed\cocos2d-x-3.2\cocos2d-x-3.2\projects -l lua 2 ...

  6. 关于Android PullTorefreshScrollview回到顶部实例

    列表滑动下面显示按钮,点击按钮回到顶部的功能,一般scrollview会有滑动监听的事件,通过setOnScrollChangeListener()滑动监听滑动的距离来判断是否显示按钮就好了,但是Pu ...

  7. 如何将dtb反编译成dts

    点击打开链接 由于device tree会将一个node的信息分布在各个文件里,查看起来很不方便,比如如下例子,ldb在三个文件中都有配置: imx6qdl-sabresd.dtsi: [plain] ...

  8. 【Netty源码分析】Reactor线程模型

    1. 背景 1.1. Java线程模型的演进 1.1.1. 单线程 时间回到十几年前,那时主流的CPU都还是单核(除了商用高性能的小机),CPU的核心频率是机器最重要的指标之一. 在Java领域当时比 ...

  9. 【移动开发】Context类bindService()参数

    bindService()是Context的一个方法,它是抽象的.函数原型的代码如下:(android 2.3.3) /** * Connect to an application service, ...

  10. Ubuntu下安装GTK环境

    要生成C图形界面的程序,得安装GTK环境     安装GTK环境只要安装一个gnome-core-devel就可以了,里面集成了很多其他的包.除此之外还要转一些其他的  东西,如libglib2.0 ...