您的位置:首页 > 编程语言 > C语言/C++

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

2015-03-18 21:12 288 查看

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

作者: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 , <);//转换成屏幕坐标
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 , <);//转换成屏幕坐标 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++】游戏编程笔记八配套代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐