本系列文章由@二货梦想家张程 所写,转载请注明出处。
作者:ZeeCoder 微博链接:http://weibo.com/zc463717263
我的邮箱:[email protected] 欢迎大家发邮件来和我交流编程心得
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++】游戏编程笔记八配套代码