《逐梦旅程 WINDOWS游戏编程之从零开始》笔记3——输入消息处理,物理建模与粒子系统初步...

第7章 Windows游戏输入消息处理

1. 键盘消息处理

之前提到的窗口过程函数有两参数与消息输出有关——wParam和llParam

LRESULT CALLBACK WindowProc(
  _In_ HWND   hwnd,
  _In_ UINT   uMsg,
  _In_ WPARAM wParam,
  _In_ LPARAM lParam
);

当键盘消息触发时,wParam的值为按下按键的虚拟键码,lParam则存储按键的相关状态的信息,因此,如果要对键盘输入的消息进行处理,就可以用一个switch来判断wParam中的内容并进行相关的处理。

下面示例程序让玩家以上下左右方向键来控制人物的移动,使用透明遮罩法进行透明贴图:

  1 #include <windows.h>
  2 #include <tchar.h>//使用swprintf_s函数所需的头文件
  3 
  4 #pragma  comment(lib,"Msimg32.lib")        //添加使用TransparentBlt函数所需的库文件
  5 
  6 #define WINDOW_WIDTH    800                            //为窗口宽度定义的宏,以方便在此处修改窗口宽度
  7 #define WINDOW_HEIGHT    600                            //为窗口高度定义的宏,以方便在此处修改窗口高度
  8 #define WINDOW_TITLE        L"【致我们永不熄灭的游戏开发梦想】Windows消息处理之 键盘消息处理 "    //为窗口标题定义的宏
  9 
 10 HDC                g_hdc=NULL,g_mdc=NULL,g_bufdc=NULL;      //全局设备环境句柄与两个全局内存DC句柄
 11 HBITMAP        g_hSprite[4]={NULL},g_hBackGround=NULL;                                //定义位图句柄数组用于存储四张方向图,以及定义存储背景图的句柄
 12 DWORD        g_tPre=0,g_tNow=0;                    //声明l两个函数来记录时间,g_tPre记录上一次绘图的时间,g_tNow记录此次准备绘图的时间
 13 int                    g_iNum=0,g_iX=0,g_iY=0;                //g_iNum用来记录图号,g_iX,g_iY分别表示贴图的横纵坐标
 14 int                    g_iDirection=0;//g_iDirection为人物移动方向,这里我们中以0,1,2,3代表人物上,下,左,右方向上的移动
 15 
 16 LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );//窗口过程函数
 17 BOOL                        Game_Init(HWND hwnd);            //在此函数中进行资源的初始化
 18 VOID                            Game_Paint( HWND hwnd);        //在此函数中进行绘图代码的书写
 19 BOOL                        Game_CleanUp(HWND hwnd );    //在此函数中进行资源的清理
 20 
 21 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
 22 {
 23     //【1】窗口创建四步曲之一:开始设计一个完整的窗口类
 24     WNDCLASSEX wndClass = { 0 };                            //用WINDCLASSEX定义了一个窗口类
 25     wndClass.cbSize = sizeof( WNDCLASSEX ) ;            //设置结构体的字节数大小
 26     wndClass.style = CS_HREDRAW | CS_VREDRAW;    //设置窗口的样式
 27     wndClass.lpfnWndProc = WndProc;                    //设置指向窗口过程函数的指针
 28     wndClass.cbClsExtra        = 0;                                //窗口类的附加内存,取0就可以了
 29     wndClass.cbWndExtra        = 0;                            //窗口的附加内存,依然取0就行了
 30     wndClass.hInstance = hInstance;                        //指定包含窗口过程的程序的实例句柄。
 31     wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);  //本地加载自定义ico图标
 32     wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。
 33     wndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  //为hbrBackground成员指定一个白色画刷句柄    
 34     wndClass.lpszMenuName = NULL;                        //用一个以空终止的字符串,指定菜单资源的名字。
 35     wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";        //用一个以空终止的字符串,指定窗口类的名字。
 36 
 37     //【2】窗口创建四步曲之二:注册窗口类
 38     if( !RegisterClassEx( &wndClass ) )                //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
 39         return -1;        
 40 
 41     //【3】窗口创建四步曲之三:正式创建窗口
 42     HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE,                //喜闻乐见的创建窗口函数CreateWindow
 43         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
 44         WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
 45 
 46     //【4】窗口创建四步曲之四:窗口的移动、显示与更新
 47     MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true);        //调整窗口显示时的位置,使窗口左上角位于(250,80)处
 48     ShowWindow( hwnd, nShowCmd );    //调用ShowWindow函数来显示窗口
 49     UpdateWindow(hwnd);                        //对窗口进行更新,就像我们买了新房子要装修一样
 50 
 51     //游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE
 52     if (!Game_Init (hwnd)) 
 53     {
 54         MessageBox(hwnd, L"资源初始化失败", L"消息窗口", 0); //使用MessageBox函数,创建一个消息窗口
 55         return FALSE;
 56     } 
 57 
 58     //【5】消息循环过程
 59     MSG msg = { 0 };                //定义并初始化msg
 60     while( msg.message != WM_QUIT )        //使用while循环,如果消息不是WM_QUIT消息,就继续循环
 61     {
 62         if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。
 63         {
 64             TranslateMessage( &msg );        //将虚拟键消息转换为字符消息
 65             DispatchMessage( &msg );            //分发一个消息给窗口程序。
 66         }
 67         else
 68         {
 69             g_tNow = GetTickCount();   //获取当前系统时间
 70             if(g_tNow-g_tPre >= 50)        //当此次循环运行与上次绘图时间相差0.05秒时再进行重绘操作
 71                 Game_Paint(hwnd);
 72         }
 73 
 74     }
 75 
 76     //【6】窗口类的注销
 77     UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);  //程序准备结束,注销窗口类
 78     return 0;  
 79 }
 80 
 81 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )      
 82 {
 83 
 84     switch( message )                        //switch语句开始
 85     {
 86 
 87     case WM_KEYDOWN:         //按下键盘消息
 88         //判断按键的虚拟键码
 89         switch (wParam) 
 90         {
 91         case VK_ESCAPE:           //按下【Esc】键
 92             DestroyWindow(hwnd);    // 销毁窗口, 并发送一条WM_DESTROY消息
 93             PostQuitMessage( 0 );  //结束程序
 94             break;
 95         case VK_UP:                  //按下【↑】键
 96             //根据按键加入人物移动的量(每次按下一次按键移动10个单位),来决定人物贴图坐标的X与Y值,接着判断坐标是否超出窗口区域,若有则进行修正
 97             g_iY -= 10;
 98             g_iDirection = 0;
 99             if(g_iY < 0)
100                 g_iY = 0;
101             break;
102         case VK_DOWN:              //按下【↓】键
103             g_iY += 10;
104             g_iDirection = 1;
105             if(g_iY > WINDOW_HEIGHT-135)
106                 g_iY = WINDOW_HEIGHT-135;    
107             break;
108         case VK_LEFT:              //按下【←】键            
109             g_iX -= 10;
110             g_iDirection = 2;
111             if(g_iX < 0)
112                 g_iX = 0;        
113             break;
114         case VK_RIGHT:               //按下【→】键
115             g_iX += 10;
116             g_iDirection = 3;
117             if(g_iX > WINDOW_WIDTH-75)
118                 g_iX = WINDOW_WIDTH-75;
119             break;
120         }            
121         break;                                //跳出该switch语句
122 
123     case WM_DESTROY:                    //若是窗口销毁消息
124         Game_CleanUp(hwnd);            //调用自定义的资源清理函数Game_CleanUp()进行退出前的资源清理
125         PostQuitMessage( 0 );            //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
126         break;                                    //跳出该switch语句
127 
128     default:                                        //若上述case条件都不符合,则执行该default语句
129         return DefWindowProc( hwnd, message, wParam, lParam );        //调用缺省的窗口过程
130     }
131 
132     return 0;                                    //正常退出
133 }
134 
135 //-----------------------------------【Game_Init( )函数】--------------------------------------
136 //    描述:初始化函数,进行一些简单的初始化
137 //------------------------------------------------------------------------------------------------
138 BOOL Game_Init( HWND hwnd )
139 {
140     HBITMAP bmp;
141 
142     g_hdc = GetDC(hwnd);  
143     g_mdc = CreateCompatibleDC(g_hdc);  //创建一个和hdc兼容的内存DC
144     g_bufdc = CreateCompatibleDC(g_hdc);//再创建一个和hdc兼容的缓冲DC
145     bmp = CreateCompatibleBitmap(g_hdc,WINDOW_WIDTH,WINDOW_HEIGHT);
146 
147     //设定人物贴图初始位置和移动方向
148     g_iX = 150;
149     g_iY = 350;
150     g_iDirection = 3;
151     g_iNum = 0;
152 
153     SelectObject(g_mdc,bmp);
154     //加载各张跑动图及背景图,这里以0,1,2,3来代表人物的上,下,左,右移动
155     g_hSprite[0] = (HBITMAP)LoadImage(NULL,L"go1.bmp",IMAGE_BITMAP,480,216,LR_LOADFROMFILE);
156     g_hSprite[1] = (HBITMAP)LoadImage(NULL,L"go2.bmp",IMAGE_BITMAP,480,216,LR_LOADFROMFILE);
157     g_hSprite[2] = (HBITMAP)LoadImage(NULL,L"go3.bmp",IMAGE_BITMAP,480,216,LR_LOADFROMFILE);
158     g_hSprite[3] = (HBITMAP)LoadImage(NULL,L"go4.bmp",IMAGE_BITMAP,480,216,LR_LOADFROMFILE);
159     g_hBackGround = (HBITMAP)LoadImage(NULL,L"bg.bmp",IMAGE_BITMAP,WINDOW_WIDTH,WINDOW_HEIGHT,LR_LOADFROMFILE);
160 
161     Game_Paint(hwnd);
162     return TRUE;
163 }
164 
165 //-----------------------------------【Game_Paint( )函数】--------------------------------------
166 //    描述:绘制函数,在此函数中进行绘制操作
167 //--------------------------------------------------------------------------------------------------
168 VOID Game_Paint( HWND hwnd )
169 {
170     //先在mdc中贴上背景图
171     SelectObject(g_bufdc,g_hBackGround);
172     BitBlt(g_mdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_bufdc,0,0,SRCCOPY);
173 
174     //按照目前的移动方向取出对应人物的连续走动图,并确定截取人物图的宽度与高度
175     SelectObject(g_bufdc,g_hSprite[g_iDirection]);
176     BitBlt(g_mdc,g_iX,g_iY,60,108,g_bufdc,g_iNum*60,108,SRCAND);
177     BitBlt(g_mdc,g_iX,g_iY,60,108,g_bufdc,g_iNum*60,0,SRCPAINT);
178     //将最后的画面显示在窗口中
179     BitBlt(g_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_mdc,0,0,SRCCOPY);
180 
181     g_tPre = GetTickCount();     //记录此次绘图时间
182     g_iNum++;
183     if(g_iNum == 8)
184         g_iNum = 0;
185 
186 }
187 
188 //-----------------------------------【Game_CleanUp( )函数】--------------------------------
189 //    描述:资源清理函数,在此函数中进行程序退出前资源的清理工作
190 //---------------------------------------------------------------------------------------------------
191 BOOL Game_CleanUp( HWND hwnd )
192 {
193     //释放资源对象
194     DeleteObject(g_hBackGround);
195     for (int i=0;i<4;i++)
196     {
197         DeleteObject(g_hSprite[i]);
198     }
199     DeleteDC(g_bufdc);
200     DeleteDC(g_mdc);
201     ReleaseDC(hwnd,g_hdc);
202     return TRUE;
203 }
View Code

要点:

根据键盘输入的消息来改变贴图坐标,从而在下次绘制时改变贴图的位置,产生一种移动的效果。

 

2.  鼠标消息处理

对于鼠标消息,lParam参数的值可分为高字节和低字节两个部分,其中高字节存储的是光标所在的X坐标值,低字节存储Y坐标值。可以通过下面两个宏来取得鼠标的坐标值:

Function:Retrieves the low-order word from the specified value

WORD LOWORD(
   DWORD dwValue
);

 

Function:Retrieves the high-order word from the specified 32-bit value.

WORD HIWORD(
   DWORD dwValue
);

WORD:A 16-bit unsigned integer. The range is 0 through 65535 decimal. This type is declared in WinDef.h as follows: typedef unsigned short WORD;

DWORD:A 32-bit unsigned integer. The range is 0 through 4294967295 decimal. This type is declared in IntSafe.h as follows: typedef unsigned long DWORD;

 

wParam参数的值记录着鼠标按键及键盘Ctrl键与Shift键的状态信息,一般通过定义在 "WINUSER.H" 中的测试标志与wParam参数来检查上述按键的按下状态。比如某个鼠标消息发生时,要测试鼠标左键是否也被按下,就把wParam拿着和某种消息&(逻辑与)一下:

if(wParam & MK_LBUTTON)  //单击了鼠标左键
{
        //鼠标左键被按下的消息处理代码
}

比如要测试鼠标左键与Shift键的按下状态,那么程序可以这么写:

if(wParam & MK_LBUTTON)  //单击了鼠标左键
{
        if( wParam & MK_CONTROL )  //单击了鼠标左键,也按下了Ctrl键
        {
        }
        else
        {
        }   
}    
else
{
}

 

3. 滚轮消息

滚轮转动消息WM_MOUSEWHEEL。当滚轮消息发生时,lParam参数中的值同样记录光标所在的位置的,而wParam参数则分为高字节和低字节两部分,低字节部分存储鼠标键与Shift、Ctrl键的状态信息,高字节部分的值会是“120”或“-120”,120表示向前滚动,-120则表示向后转动。

 

4. 鼠标相关常用函数(详细查mdsn文档)

setCursorPos函数来设定光标位置,将窗口坐标转换到屏幕坐标函数ClientToScreen,将屏幕坐标转换为窗口坐标的ScreenToClient函数,显示与隐藏鼠标光标函数ShowCursor,获取窗口外鼠标消息的函数SetCapture,释放窗口取得窗外鼠标消息函数ReleaseCapture,限制鼠标光标移动区域函数ClipCursor,取得窗口外部区域函数GetWindowRect以及取得内部区域函数GetClientRect

下面是一个综合的程序:

RECT rect;
POINT lt,rb;

GetClientRect(hWnd,&rect);  //取得窗口内部矩形

//将矩形左上角坐标存入lt中
lt.x=rect.left;
lt.y=rect.top;
//将矩形右下角坐标存入rb中
rb.x=rect.right;
rb.y=rect.bottom;
//将lt和rb的窗口坐标转换为屏幕坐标
ClientToScreen(hWnd,&lt);
ClientToScreen(hWnd,&rb);
//以屏幕坐标重新设定矩形区域
rect.left=lt.x;
rect.top=lt.y;
rect.right=rb.x;
rect.bottom=rb.y;
//限制鼠标光标移动区域
ClipCursor(&rect);

因为限制鼠标光标移动区域的ClipCursor函数中输入的矩形区域必须是屏幕坐标,因此如果取得的是窗口内部区域,那么还必须将那个窗口坐标转换为屏幕坐标。

The POINT structure defines the x- and y- coordinates of a point.

typedef struct tagPOINT {
  LONG x;
  LONG y;
} POINT, *PPOINT;

 The RECT structure defines the coordinates of the upper-left and lower-right corners of a rectangle.

typedef struct _RECT {
  LONG left;
  LONG top;
  LONG right;
  LONG bottom;
} RECT, *PRECT;

 

国际惯例,还是来个完整的示例程序,要实现的效果是这样的:

将第一张图贴到背景图上,用鼠标控制其移动,单击鼠标发射弹幕

  1 #include <windows.h>
  2 #include <tchar.h>//使用swprintf_s函数所需的头文件
  3 
  4 #pragma  comment(lib,"Msimg32.lib")        //添加使用TransparentBlt函数所需的库文件
  5 
  6 #define WINDOW_WIDTH    800                            //为窗口宽度定义的宏,以方便在此处修改窗口宽度
  7 #define WINDOW_HEIGHT    600                            //为窗口高度定义的宏,以方便在此处修改窗口高度
  8 #define WINDOW_TITLE        L"【致我们永不熄灭的游戏开发梦想】Windows消息处理之 鼠标消息处理 "    //为窗口标题定义的宏
  9 
 10 struct SwordBullets       //SwordBullets结构体代表剑气(子弹)
 11 {
 12     int x,y;        //剑气(子弹)坐标
 13     bool exist;     //剑气(子弹)是否存在
 14 };
 15 
 16 //-----------------------------------【全局变量声明部分】-------------------------------------
 17 HDC            g_hdc=NULL,g_mdc=NULL,g_bufdc=NULL;      //全局设备环境句柄与全局内存DC句柄
 18 HBITMAP        g_hSwordMan=NULL,g_hSwordBlade=NULL,g_hBackGround=NULL;        //定义位图句柄用于存储位图资源
 19 DWORD        g_tPre=0,g_tNow=0;      //声明l两个函数来记录时间,g_tPre记录上一次绘图的时间,g_tNow记录此次准备绘图的时间
 20 int            g_iX=0,g_iY=0,g_iXnow=0,g_iYnow=0;    //g_iX,g_iY代表鼠标光标所在位置,g_iXnow,g_iYnow代表当前人物坐标,也就是贴图的位置
 21 int            g_iBGOffset=0,g_iBulletNum=0;       //g_iBGOffset为滚动背景所要裁剪的区域宽度,g_iBulletNum记录剑侠现有剑气(子弹)数目
 22 SwordBullets Bullet[30];           //声明一个“SwordBullets”类型的数组,用来存储剑侠发出的剑气(子弹)
 23 
 24 
 25 //-----------------------------------【全局函数声明部分】-------------------------------------
 26 //------------------------------------------------------------------------------------------------
 27 LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );//窗口过程函数
 28 BOOL                Game_Init(HWND hwnd);            //在此函数中进行资源的初始化
 29 VOID                Game_Paint( HWND hwnd);        //在此函数中进行绘图代码的书写
 30 BOOL                Game_CleanUp(HWND hwnd );    //在此函数中进行资源的清理
 31 
 32 //-----------------------------------【WinMain( )函数】--------------------------------------
 33 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
 34 {
 35     //【1】窗口创建四步曲之一:开始设计一个完整的窗口类
 36     WNDCLASSEX wndClass = { 0 };                            //用WINDCLASSEX定义了一个窗口类
 37     wndClass.cbSize = sizeof( WNDCLASSEX ) ;            //设置结构体的字节数大小
 38     wndClass.style = CS_HREDRAW | CS_VREDRAW;    //设置窗口的样式
 39     wndClass.lpfnWndProc = WndProc;                    //设置指向窗口过程函数的指针
 40     wndClass.cbClsExtra        = 0;                                //窗口类的附加内存,取0就可以了
 41     wndClass.cbWndExtra        = 0;                            //窗口的附加内存,依然取0就行了
 42     wndClass.hInstance = hInstance;                        //指定包含窗口过程的程序的实例句柄。
 43     wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);  //本地加载自定义ico图标
 44     wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。
 45     wndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  //为hbrBackground成员指定一个白色画刷句柄    
 46     wndClass.lpszMenuName = NULL;                        //用一个以空终止的字符串,指定菜单资源的名字。
 47     wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";        //用一个以空终止的字符串,指定窗口类的名字。
 48 
 49     //【2】窗口创建四步曲之二:注册窗口类
 50     if( !RegisterClassEx( &wndClass ) )                //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
 51         return -1;        
 52 
 53     //【3】窗口创建四步曲之三:正式创建窗口
 54     HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE,                //喜闻乐见的创建窗口函数CreateWindow
 55         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
 56         WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
 57 
 58     //【4】窗口创建四步曲之四:窗口的移动、显示与更新
 59     MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true);        //调整窗口显示时的位置,使窗口左上角位于(250,80)处
 60     ShowWindow( hwnd, nShowCmd );    //调用ShowWindow函数来显示窗口
 61     UpdateWindow(hwnd);                        //对窗口进行更新,就像我们买了新房子要装修一样
 62 
 63     //游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE
 64     if (!Game_Init (hwnd)) 
 65     {
 66         MessageBox(hwnd, L"资源初始化失败", L"消息窗口", 0); //使用MessageBox函数,创建一个消息窗口
 67         return FALSE;
 68     }
 69 
 70     //【5】消息循环过程
 71     MSG msg = { 0 };                //定义并初始化msg
 72     while( msg.message != WM_QUIT )        //使用while循环,如果消息不是WM_QUIT消息,就继续循环
 73     {
 74         if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。
 75         {
 76             TranslateMessage( &msg );        //将虚拟键消息转换为字符消息
 77             DispatchMessage( &msg );            //分发一个消息给窗口程序。
 78         }
 79         else
 80         {
 81             g_tNow = GetTickCount();   //获取当前系统时间
 82             if(g_tNow-g_tPre >= 5)        //当此次循环运行与上次绘图时间相差0.1秒时再进行重绘操作
 83                 Game_Paint(hwnd);
 84         }
 85 
 86     }
 87 
 88     //【6】窗口类的注销
 89     UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);  //程序准备结束,注销窗口类
 90     return 0;  
 91 }
 92 
 93 //-----------------------------------【WndProc( )函数】--------------------------------------
 94 //    描述:窗口过程函数WndProc,对窗口消息进行处理
 95 //------------------------------------------------------------------------------------------------
 96 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )      
 97 {
 98     switch( message )                        //switch语句开始
 99     {
100 
101     case WM_KEYDOWN:         //按下键盘消息
102         //判断按键的虚拟键码
103         switch (wParam) 
104         {
105         case VK_ESCAPE:           //按下【Esc】键
106             DestroyWindow(hwnd);    // 销毁窗口, 并发送一条WM_DESTROY消息
107             PostQuitMessage( 0 );  //结束程序
108             break;
109         }
110 
111         break;
112 
113     case WM_LBUTTONDOWN:            //单击鼠标左键消息
114         for(int i=0;i<30;i++)
115         {
116             if(!Bullet[i].exist)
117             {
118                 Bullet[i].x = g_iXnow;        //剑气(子弹)x坐标
119                 Bullet[i].y = g_iYnow + 30; //剑气(子弹)y坐标
120                 Bullet[i].exist = true;
121                 g_iBulletNum++;            //累加剑气(子弹)数目
122                 break;
123             }
124         }
125 
126     case WM_MOUSEMOVE:   //鼠标移动消息
127         //对X坐标的处理
128         g_iX = LOWORD(lParam);            //取得鼠标X坐标
129         if(g_iX > WINDOW_WIDTH-317)    //设置临界坐标
130             g_iX = WINDOW_WIDTH-317;
131         else if(g_iX < 0)
132             g_iX = 0;
133         //对Y坐标的处理
134         g_iY = HIWORD(lParam);            //取得鼠标Y坐标
135         if(g_iY > WINDOW_HEIGHT-283)
136             g_iY = WINDOW_HEIGHT-283;
137         else if(g_iY < -200)
138             g_iY = -200;
139         break;
140 
141     case WM_DESTROY:                    //若是窗口销毁消息
142         Game_CleanUp(hwnd);            //调用自定义的资源清理函数Game_CleanUp()进行退出前的资源清理
143         PostQuitMessage( 0 );            //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
144         break;                                    //跳出该switch语句
145 
146     default:                                        //若上述case条件都不符合,则执行该default语句
147         return DefWindowProc( hwnd, message, wParam, lParam );        //调用缺省的窗口过程
148     }
149     return 0;                                    //正常退出
150 }
151 
152 //-----------------------------------【Game_Init( )函数】--------------------------------------
153 //    描述:初始化函数,进行一些简单的初始化
154 //------------------------------------------------------------------------------------------------
155 BOOL Game_Init( HWND hwnd )
156 {
157     HBITMAP bmp;
158 
159     g_hdc = GetDC(hwnd);  
160     g_mdc = CreateCompatibleDC(g_hdc);  //创建一个和hdc兼容的内存DC
161     g_bufdc = CreateCompatibleDC(g_hdc);//再创建一个和hdc兼容的缓冲DC
162     bmp = CreateCompatibleBitmap(g_hdc,WINDOW_WIDTH,WINDOW_HEIGHT);
163 
164     //设定人物贴图初始值,鼠标位置初始值
165     g_iX = 300;
166     g_iY = 100;
167     g_iXnow = 300;
168     g_iYnow = 100;
169 
170     SelectObject(g_mdc,bmp);
171     //加载各张跑动图及背景图
172     g_hSwordMan = (HBITMAP)LoadImage(NULL,L"swordman.bmp",IMAGE_BITMAP,317,283,LR_LOADFROMFILE);
173     g_hSwordBlade = (HBITMAP)LoadImage(NULL,L"swordblade.bmp",IMAGE_BITMAP,100,26,LR_LOADFROMFILE);
174     g_hBackGround = (HBITMAP)LoadImage(NULL,L"bg.bmp",IMAGE_BITMAP,WINDOW_WIDTH,WINDOW_HEIGHT,LR_LOADFROMFILE);
175 
176     POINT pt,lt,rb;
177     RECT rect;
178     //设定光标位置
179     pt.x = 300;
180     pt.y = 100;
181     ClientToScreen(hwnd,&pt);
182     SetCursorPos(pt.x,pt.y);
183 
184     ShowCursor(false);        //隐藏鼠标光标
185 
186     //限制鼠标光标移动区域
187     GetClientRect(hwnd,&rect);  //取得窗口内部矩形
188     //将矩形左上点坐标存入lt中
189     lt.x = rect.left;
190     lt.y = rect.top;
191     //将矩形右下坐标存入rb中
192     rb.x = rect.right;
193     rb.y = rect.bottom;
194     //将lt和rb的窗口坐标转换为屏幕坐标
195     ClientToScreen(hwnd,&lt);
196     ClientToScreen(hwnd,&rb);
197     //以屏幕坐标重新设定矩形区域
198     rect.left = lt.x;
199     rect.top = lt.y;
200     rect.right = rb.x;
201     rect.bottom = rb.y;
202     //限制鼠标光标移动区域
203     ClipCursor(&rect);
204 
205     Game_Paint(hwnd);
206     return TRUE;
207 }
208 
209 //-----------------------------------【Game_Paint( )函数】--------------------------------------
210 //    描述:绘制函数,在此函数中进行绘制操作
211 //--------------------------------------------------------------------------------------------------
212 VOID Game_Paint( HWND hwnd )
213 {
214     //先在mdc中贴上背景图
215     SelectObject(g_bufdc,g_hBackGround);
216     BitBlt(g_mdc,0,0,g_iBGOffset,WINDOW_HEIGHT,g_bufdc,WINDOW_WIDTH-g_iBGOffset,0,SRCCOPY);
217     BitBlt(g_mdc,g_iBGOffset,0,WINDOW_WIDTH-g_iBGOffset,WINDOW_HEIGHT,g_bufdc,0,0,SRCCOPY);
218 
219     wchar_t str[20] = {};
220 
221     //计算剑侠的贴图坐标,设定每次进行剑侠贴图时,其贴图坐标(g_iXnow,g_iYnow)会以10个单位慢慢向鼠标光标所在的目的点(x,y)接近,直到两个坐标相同为止
222     if(g_iXnow < g_iX)//若当前贴图X坐标小于鼠标光标的X坐标
223     {
224         g_iXnow += 10;
225         if(g_iXnow > g_iX)
226             g_iXnow = g_iX;
227     }
228     else   //若当前贴图X坐标大于鼠标光标的X坐标
229     {
230         g_iXnow -=10;
231         if(g_iXnow < g_iX)
232             g_iXnow = g_iX;
233     }
234 
235     if(g_iYnow < g_iY)  //若当前贴图Y坐标小于鼠标光标的Y坐标
236     {
237         g_iYnow += 10;
238         if(g_iYnow > g_iY)
239             g_iYnow = g_iY;
240     }
241     else  //若当前贴图Y坐标大于于鼠标光标的Y坐标
242     {
243         g_iYnow -= 10;  
244         if(g_iYnow < g_iY)
245             g_iYnow = g_iY;
246     }
247 
248     //贴上剑侠图
249     SelectObject(g_bufdc,g_hSwordMan);
250     TransparentBlt(g_mdc,g_iXnow,g_iYnow,317,283,g_bufdc,0,0,317,283,RGB(0,0,0));
251 
252     //剑气(子弹)的贴图,先判断剑气(子弹)数目“g_iBulletNum”的值是否为“0”。若不为0,则对剑气(子弹)数组中各个还存在的剑气(子弹)按照其所在的坐标(b[i].x,b[i].y)循环进行贴图操作
253     SelectObject(g_bufdc,g_hSwordBlade);
254     if(g_iBulletNum!=0)
255         for(int i=0;i<30;i++)
256             if(Bullet[i].exist)
257             {
258                 //贴上剑气(子弹)图
259                 TransparentBlt(g_mdc,Bullet[i].x-70,Bullet[i].y+100,100,33,g_bufdc,0,0,100,26,RGB(0,0,0));
260 
261                 //设置下一个剑气(子弹)的坐标。剑气(子弹)是从右向左发射的,因此,每次其X轴上的坐标值递减10个单位,这样贴图会产生往左移动的效果。而如果剑气(子弹)下次的坐标已超出窗口的可见范围(h[i].x<0),那么剑气(子弹)设为不存在,并将剑气(子弹)总数g_iBulletNum变量值减1.
262                 Bullet[i].x -= 10;
263                 if(Bullet[i].x < 0)
264                 {
265                     g_iBulletNum--;
266                     Bullet[i].exist = false;
267                 }
268             }
269 
270             HFONT hFont;  
271             hFont=CreateFont(20,0,0,0,0,0,0,0,GB2312_CHARSET,0,0,0,0,TEXT("微软雅黑"));  //创建字体
272             SelectObject(g_mdc,hFont);  //选入字体到g_mdc中
273             SetBkMode(g_mdc, TRANSPARENT);    //设置文字背景透明
274             SetTextColor(g_mdc,RGB(255,255,0));  //设置文字颜色
275 
276             //在左上角进行文字输出
277             swprintf_s(str,L"鼠标X坐标为%d    ",g_iX);
278             TextOut(g_mdc,0,0,str,wcslen(str));
279             swprintf_s(str,L"鼠标Y坐标为%d    ",g_iY);
280             TextOut(g_mdc,0,20,str,wcslen(str));
281 
282             //贴上背景图
283             BitBlt(g_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_mdc,0,0,SRCCOPY);
284 
285             g_tPre = GetTickCount();
286 
287             g_iBGOffset += 5; //让背景滚动量+5
288             if(g_iBGOffset==WINDOW_WIDTH)//如果背景滚动量达到了背景宽度值,就置零
289                 g_iBGOffset = 0;  
290 }
291 
292 //-----------------------------------【Game_CleanUp( )函数】--------------------------------
293 BOOL Game_CleanUp( HWND hwnd )
294 {
295     //释放资源对象
296     DeleteObject(g_hBackGround);
297     DeleteDC(g_bufdc);
298     DeleteDC(g_mdc);
299     ReleaseDC(hwnd,g_hdc);
300     return TRUE;
301 }
View Code

 


第8章 物理建模与粒子系统初步

关于物理建模这块,并没有涉及到新的函数的使用,主要是利用物理和数学知识来模拟匀速,加速运动,重力系统和摩擦力系统,这里要好好读读作者的源码才行。

使用结构体来定义粒子,如下面这个结构体snow便是用来定义“雪花”粒子的:

struct snow
{
    int x; //雪花的x坐标
    int y; //雪花的Y坐标
    BOOL exist; //雪花是否存在  
}

定义完粒子的结构体后,便可以实例化一个粒子数组,可以用如下两种方法来进行:

在结构体声明时的尾部加上我们需要实例化的对象:

struct snow
{
    int x; //雪花的x坐标
    int y; //雪花的Y坐标
    BOOL exist; //雪花是否存在  
} SnowFlowers [100];

 或者在完成结构体定义后,再单独进行定义:

snow SnowFlowers [100]

 雪花飞舞示例程序:

  1 #include <windows.h>
  2 #include <tchar.h>//使用swprintf_s函数所需的头文件
  3 #include  <time.h> //使用获取系统时间time()函数需要包含的头文件
  4 
  5 #pragma  comment(lib,"Msimg32.lib")        //添加使用TransparentBlt函数所需的库文件
  6 
  7 //-----------------------------------【宏定义部分】--------------------------------------------
  8 #define WINDOW_WIDTH    800                            //为窗口宽度定义的宏,以方便在此处修改窗口宽度
  9 #define WINDOW_HEIGHT    600                            //为窗口高度定义的宏,以方便在此处修改窗口高度
 10 #define WINDOW_TITLE        L"【致我们永不熄灭的游戏开发梦想】粒子系统初步之 雪花飞舞demo"    //为窗口标题定义的宏
 11 #define PARTICLE_NUMBER    80                            //表示粒子数量的宏,以方便修改粒子数量
 12 
 13 //-----------------------------------【全局结构体定义部分】-------------------------------------
 14 struct SNOW
 15 {
 16     int x; //雪花的 X坐标 
 17     int y; //雪花的Y坐标
 18     BOOL exist;  //雪花是否存在
 19 };
 20 
 21 
 22 //-----------------------------------【全局变量声明部分】-------------------------------------
 23 HDC            g_hdc=NULL,g_mdc=NULL,g_bufdc=NULL;      //全局设备环境句柄与全局内存DC句柄
 24 HBITMAP        g_hSnow=NULL,g_hBackGround=NULL;           //定义位图句柄用于存储位图资源
 25 DWORD        g_tPre=0,g_tNow=0;                    //声明l两个函数来记录时间,g_tPre记录上一次绘图的时间,g_tNow记录此次准备绘图的时间
 26 RECT        g_rect;                //定义一个RECT结构体,用于储存内部窗口区域的坐标
 27 SNOW        SnowFlowers[PARTICLE_NUMBER];   //雪花粒子数组
 28 int            g_SnowNum=0; //定义g_SnowNum用于计数
 29 
 30 //-----------------------------------【全局函数声明部分】-------------------------------------
 31 LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );//窗口过程函数
 32 BOOL                Game_Init(HWND hwnd);            //在此函数中进行资源的初始化
 33 VOID                Game_Paint( HWND hwnd);        //在此函数中进行绘图代码的书写
 34 BOOL                Game_CleanUp(HWND hwnd );    //在此函数中进行资源的清理
 35 
 36 //-----------------------------------【WinMain( )函数】--------------------------------------
 37 //    描述:Windows应用程序的入口函数,我们的程序从这里开始
 38 //------------------------------------------------------------------------------------------------
 39 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
 40 {
 41     //【1】窗口创建四步曲之一:开始设计一个完整的窗口类
 42     WNDCLASSEX wndClass = { 0 };                            //用WINDCLASSEX定义了一个窗口类
 43     wndClass.cbSize = sizeof( WNDCLASSEX ) ;            //设置结构体的字节数大小
 44     wndClass.style = CS_HREDRAW | CS_VREDRAW;    //设置窗口的样式
 45     wndClass.lpfnWndProc = WndProc;                    //设置指向窗口过程函数的指针
 46     wndClass.cbClsExtra        = 0;                                //窗口类的附加内存,取0就可以了
 47     wndClass.cbWndExtra        = 0;                            //窗口的附加内存,依然取0就行了
 48     wndClass.hInstance = hInstance;                        //指定包含窗口过程的程序的实例句柄。
 49     wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);  //本地加载自定义ico图标
 50     wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。
 51     wndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  //为hbrBackground成员指定一个白色画刷句柄    
 52     wndClass.lpszMenuName = NULL;                        //用一个以空终止的字符串,指定菜单资源的名字。
 53     wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";        //用一个以空终止的字符串,指定窗口类的名字。
 54 
 55     //【2】窗口创建四步曲之二:注册窗口类
 56     if( !RegisterClassEx( &wndClass ) )                //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
 57         return -1;        
 58 
 59     //【3】窗口创建四步曲之三:正式创建窗口
 60     HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE,                //喜闻乐见的创建窗口函数CreateWindow
 61         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
 62         WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
 63 
 64     //【4】窗口创建四步曲之四:窗口的移动、显示与更新
 65     MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true);        //调整窗口显示时的位置,使窗口左上角位于(250,80)处
 66     ShowWindow( hwnd, nShowCmd );    //调用ShowWindow函数来显示窗口
 67     UpdateWindow(hwnd);                        //对窗口进行更新,就像我们买了新房子要装修一样
 68 
 69     //游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE
 70     if (!Game_Init (hwnd)) 
 71     {
 72         MessageBox(hwnd, L"资源初始化失败", L"消息窗口", 0); //使用MessageBox函数,创建一个消息窗口
 73         return FALSE;
 74     }
 75 
 76     //【5】消息循环过程
 77     MSG msg = { 0 };                //定义并初始化msg
 78     while( msg.message != WM_QUIT )        //使用while循环,如果消息不是WM_QUIT消息,就继续循环
 79     {
 80         if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。
 81         {
 82             TranslateMessage( &msg );        //将虚拟键消息转换为字符消息
 83             DispatchMessage( &msg );            //分发一个消息给窗口程序。
 84         }
 85         else
 86         {
 87             g_tNow = GetTickCount();   //获取当前系统时间
 88             if(g_tNow-g_tPre >= 60)        //当此次循环运行与上次绘图时间相差0.06秒时再进行重绘操作
 89                 Game_Paint(hwnd);
 90         }
 91 
 92     }
 93 
 94     //【6】窗口类的注销
 95     UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);  //程序准备结束,注销窗口类
 96     return 0;  
 97 }
 98 
 99 //-----------------------------------【WndProc( )函数】--------------------------------------
100 //    描述:窗口过程函数WndProc,对窗口消息进行处理
101 //------------------------------------------------------------------------------------------------
102 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )      
103 {
104 
105     switch( message )                        //switch语句开始
106     {
107     case WM_KEYDOWN:                    //按键消息
108         if(wParam==VK_ESCAPE)        //按下【Esc】键
109             PostQuitMessage(0);
110         break;
111 
112     case WM_DESTROY:                    //若是窗口销毁消息
113         Game_CleanUp(hwnd);            //调用自定义的资源清理函数Game_CleanUp()进行退出前的资源清理
114         PostQuitMessage( 0 );            //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
115         break;                                    //跳出该switch语句
116 
117     default:                                        //若上述case条件都不符合,则执行该default语句
118         return DefWindowProc( hwnd, message, wParam, lParam );        //调用缺省的窗口过程
119     }
120 
121     return 0;                                    //正常退出
122 }
123 
124 //-----------------------------------【Game_Init( )函数】--------------------------------------
125 //    描述:初始化函数,进行一些简单的初始化
126 //------------------------------------------------------------------------------------------------
127 BOOL Game_Init( HWND hwnd )
128 {
129     srand((unsigned)time(NULL));      //用系统时间初始化随机种子 
130 
131     HBITMAP bmp;
132 
133     g_hdc = GetDC(hwnd);  
134     g_mdc = CreateCompatibleDC(g_hdc);  //创建一个和hdc兼容的内存DC
135     g_bufdc = CreateCompatibleDC(g_hdc);//再创建一个和hdc兼容的缓冲DC
136     bmp = CreateCompatibleBitmap(g_hdc,WINDOW_WIDTH,WINDOW_HEIGHT); //建一个和窗口兼容的空的位图对象
137 
138     SelectObject(g_mdc,bmp);//将空位图对象放到g_mdc中
139 
140     //载入位图资源
141     g_hBackGround = (HBITMAP)LoadImage(NULL,L"bg.bmp",IMAGE_BITMAP,WINDOW_WIDTH,WINDOW_HEIGHT,LR_LOADFROMFILE);
142     g_hSnow = (HBITMAP)LoadImage(NULL,L"snow.bmp",IMAGE_BITMAP,30,30,LR_LOADFROMFILE);
143 
144     GetClientRect(hwnd,&g_rect);        //取得内部窗口区域的大小
145 
146     Game_Paint(hwnd);
147     return TRUE;
148 }
149 
150 //-----------------------------------【Game_Paint( )函数】--------------------------------------
151 //    描述:绘制函数,在此函数中进行绘制操作
152 //--------------------------------------------------------------------------------------------------
153 VOID Game_Paint( HWND hwnd )
154 {
155 
156     //先在mdc中贴上背景图
157     SelectObject(g_bufdc,g_hBackGround);
158     BitBlt(g_mdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_bufdc,0,0,SRCCOPY);
159 
160     //创建粒子
161     if(g_SnowNum< PARTICLE_NUMBER)  //当粒子数小于规定的粒子数时,便产生新的粒子,设定每个粒子的属性值
162     {
163         SnowFlowers[g_SnowNum].x = rand()%g_rect.right; //将粒子的X坐标设为窗口中水平方向上的任意位置
164         SnowFlowers[g_SnowNum].y = 0;    //将每个粒子的Y坐标都设为"0",即从窗口上沿往下落
165         SnowFlowers[g_SnowNum].exist = true; //设定粒子存在
166         g_SnowNum++;   //每产生一个粒子后进行累加计数
167     }
168 
169 
170     //首先判断粒子是否存在,若存在,进行透明贴图操作
171     for(int i=0;i<PARTICLE_NUMBER;i++)
172     {
173         if(SnowFlowers[i].exist)  //粒子还存在
174         {
175             //贴上粒子图
176             SelectObject(g_bufdc,g_hSnow);
177             TransparentBlt(g_mdc,SnowFlowers[i].x,SnowFlowers[i].y,30,30,g_bufdc,0,0,30,30,RGB(0,0,0));
178 
179             //随机决定横向的移动方向和偏移量
180             if(rand()%2==0)
181                 SnowFlowers[i].x+=rand()%6;  //x坐标加上0~5之间的一个随机值
182             else 
183                 SnowFlowers[i].x-=rand()%6;     //y坐标加上0~5之间的一个随机值
184             //纵方向上做匀速运动
185             SnowFlowers[i].y+=10;  //纵坐标加上10
186             //如果粒子坐标超出了窗口长度,就让它以随机的x坐标出现在窗口顶部
187             if(SnowFlowers[i].y > g_rect.bottom)
188             {
189                 SnowFlowers[i].x = rand()%g_rect.right;
190                 SnowFlowers[i].y = 0;
191             }
192         }
193 
194     }
195     //将mdc中的全部内容贴到hdc中
196     BitBlt(g_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_mdc,0,0,SRCCOPY);
197 
198     g_tPre = GetTickCount();     //记录此次绘图时间
199 }
200 
201 //-----------------------------------【Game_CleanUp( )函数】--------------------------------
202 //    描述:资源清理函数,在此函数中进行程序退出前资源的清理工作
203 //---------------------------------------------------------------------------------------------------
204 BOOL Game_CleanUp( HWND hwnd )
205 {
206     //释放资源对象
207     DeleteObject(g_hBackGround);
208     DeleteObject(g_hSnow);
209     DeleteDC(g_bufdc);
210     DeleteDC(g_mdc);
211     ReleaseDC(hwnd,g_hdc);
212     return TRUE;
213 }
View Code

星光绽放示例程序:

模拟一个爆炸特效,爆炸点由随机数产生一个位置,爆炸后会出现很多星光以不同的速度向四方飞散,当粒子飞出窗口或者超出时间后便会消失。首先用结构体来构造出星光粒子:

struct FLYSTAR
{
    int x;  //星光所在的x坐标
    int y;  //y坐标
    int vx;   //星光x方向的速度
    int vy;   //y方向速度
    int lasted;  //星光存在的时间
    BOOL exist;   //星光是否存在
};

完整程序:

  1 #include <windows.h>
  2 #include <tchar.h>//使用swprintf_s函数所需的头文件
  3 #include  <time.h> //使用获取系统时间time函数需要包含的头文件
  4 
  5 #pragma  comment(lib,"Msimg32.lib")        //添加使用TransparentBlt函数所需的库文件
  6 
  7 #define WINDOW_WIDTH    932                            //为窗口宽度定义的宏,以方便在此处修改窗口宽度
  8 #define WINDOW_HEIGHT    700                            //为窗口高度定义的宏,以方便在此处修改窗口高度
  9 #define WINDOW_TITLE        L"【致我们永不熄灭的游戏开发梦想】粒子系统初步之 星光绽放demo"    //为窗口标题定义的宏
 10 #define FLYSTAR_NUMBER    100                            //表示粒子数量的宏,以方便修改粒子数量
 11 #define FLYSTAR_LASTED_FRAME 60                    //表示粒子持续帧数的宏,以方便修改每次星光绽放持续的时间           
 12 
 13 struct FLYSTAR
 14 {
 15     int x;       //星光所在的x坐标
 16     int y;       //星光所在的y坐标
 17     int vx;      //星光x方向的速度
 18     int vy;      //星光y方向的速度
 19     int lasted;  //星光存在的时间
 20     BOOL exist;  //星光是否存在
 21 };
 22 
 23 HDC            g_hdc=NULL,g_mdc=NULL,g_bufdc=NULL;      //全局设备环境句柄与全局内存DC句柄
 24 HBITMAP        g_hStar=NULL,g_hBackGround=NULL;           //定义位图句柄用于存储位图资源
 25 DWORD        g_tPre=0,g_tNow=0;                        //声明l两个函数来记录时间,g_tPre记录上一次绘图的时间,g_tNow记录此次准备绘图的时间
 26 RECT        g_rect;                //定义一个RECT结构体,用于储存内部窗口区域的坐标
 27 FLYSTAR        FlyStars[FLYSTAR_NUMBER];  //粒子数组
 28 int            g_StarNum=0; //定义g_StarNum用于计数
 29 
 30 LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );//窗口过程函数
 31 BOOL                Game_Init(HWND hwnd);            //在此函数中进行资源的初始化
 32 VOID                Game_Paint( HWND hwnd);        //在此函数中进行绘图代码的书写
 33 BOOL                Game_CleanUp(HWND hwnd );    //在此函数中进行资源的清理
 34 
 35 //-----------------------------------【WinMain( )函数】--------------------------------------
 36 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
 37 {
 38     //【1】窗口创建四步曲之一:开始设计一个完整的窗口类
 39     WNDCLASSEX wndClass = { 0 };                            //用WINDCLASSEX定义了一个窗口类
 40     wndClass.cbSize = sizeof( WNDCLASSEX ) ;            //设置结构体的字节数大小
 41     wndClass.style = CS_HREDRAW | CS_VREDRAW;    //设置窗口的样式
 42     wndClass.lpfnWndProc = WndProc;                    //设置指向窗口过程函数的指针
 43     wndClass.cbClsExtra        = 0;                                //窗口类的附加内存,取0就可以了
 44     wndClass.cbWndExtra        = 0;                            //窗口的附加内存,依然取0就行了
 45     wndClass.hInstance = hInstance;                        //指定包含窗口过程的程序的实例句柄。
 46     wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);  //本地加载自定义ico图标
 47     wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。
 48     wndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  //为hbrBackground成员指定一个白色画刷句柄    
 49     wndClass.lpszMenuName = NULL;                        //用一个以空终止的字符串,指定菜单资源的名字。
 50     wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";        //用一个以空终止的字符串,指定窗口类的名字。
 51 
 52     //【2】窗口创建四步曲之二:注册窗口类
 53     if( !RegisterClassEx( &wndClass ) )                //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
 54         return -1;        
 55 
 56     //【3】窗口创建四步曲之三:正式创建窗口
 57     HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE,                //喜闻乐见的创建窗口函数CreateWindow
 58         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
 59         WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
 60 
 61     //【4】窗口创建四步曲之四:窗口的移动、显示与更新
 62     MoveWindow(hwnd,150,20,WINDOW_WIDTH,WINDOW_HEIGHT,true);        //调整窗口显示时的位置,使窗口左上角位于(150,20)处
 63     ShowWindow( hwnd, nShowCmd );    //调用ShowWindow函数来显示窗口
 64     UpdateWindow(hwnd);                        //对窗口进行更新,就像我们买了新房子要装修一样
 65 
 66     //游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE
 67     if (!Game_Init (hwnd)) 
 68     {
 69         MessageBox(hwnd, L"资源初始化失败", L"消息窗口", 0); //使用MessageBox函数,创建一个消息窗口
 70         return FALSE;
 71     } 
 72 
 73     //【5】消息循环过程
 74     MSG msg = { 0 };                //定义并初始化msg
 75     while( msg.message != WM_QUIT )        //使用while循环,如果消息不是WM_QUIT消息,就继续循环
 76     {
 77         if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。
 78         {
 79             TranslateMessage( &msg );        //将虚拟键消息转换为字符消息
 80             DispatchMessage( &msg );            //分发一个消息给窗口程序。
 81         }
 82         else
 83         {
 84             g_tNow = GetTickCount();   //获取当前系统时间
 85             if(g_tNow-g_tPre >= 30)        //当此次循环运行与上次绘图时间相差0.03秒时再进行重绘操作
 86                 Game_Paint(hwnd);
 87         }
 88 
 89     }
 90 
 91     //【6】窗口类的注销
 92     UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);  //程序准备结束,注销窗口类
 93     return 0;  
 94 }
 95 
 96 //-----------------------------------【WndProc( )函数】--------------------------------------
 97 //    描述:窗口过程函数WndProc,对窗口消息进行处理
 98 //------------------------------------------------------------------------------------------------
 99 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )      
100 {
101 
102     switch( message )                        //switch语句开始
103     {
104     case WM_KEYDOWN:                    //按键消息
105         if(wParam==VK_ESCAPE)        //按下【Esc】键
106             PostQuitMessage(0);
107         break;
108 
109     case WM_DESTROY:                    //若是窗口销毁消息
110         Game_CleanUp(hwnd);            //调用自定义的资源清理函数Game_CleanUp()进行退出前的资源清理
111         PostQuitMessage( 0 );            //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
112         break;                                    //跳出该switch语句
113 
114     default:                                        //若上述case条件都不符合,则执行该default语句
115         return DefWindowProc( hwnd, message, wParam, lParam );        //调用缺省的窗口过程
116     }
117 
118     return 0;                                    //正常退出
119 }
120 
121 //-----------------------------------【Game_Init( )函数】--------------------------------------
122 //    描述:初始化函数,进行一些简单的初始化
123 //------------------------------------------------------------------------------------------------
124 BOOL Game_Init( HWND hwnd )
125 {
126 
127     srand((unsigned)time(NULL));      //用系统时间初始化随机种子 
128     HBITMAP bmp;
129 
130     g_hdc = GetDC(hwnd);  
131     g_mdc = CreateCompatibleDC(g_hdc);  //创建一个和hdc兼容的内存DC
132     g_bufdc = CreateCompatibleDC(g_hdc);//再创建一个和hdc兼容的缓冲DC
133     bmp = CreateCompatibleBitmap(g_hdc,WINDOW_WIDTH,WINDOW_HEIGHT); //建一个和窗口兼容的空的位图对象
134 
135     SelectObject(g_mdc,bmp);//将空位图对象放到g_mdc中
136 
137     //载入位图背景
138     g_hBackGround = (HBITMAP)LoadImage(NULL,L"bg.bmp",IMAGE_BITMAP,WINDOW_WIDTH,WINDOW_HEIGHT,LR_LOADFROMFILE);
139     g_hStar = (HBITMAP)LoadImage(NULL,L"star.bmp",IMAGE_BITMAP,30,30,LR_LOADFROMFILE);
140 
141     GetClientRect(hwnd,&g_rect);        //取得内部窗口区域的大小
142 
143     Game_Paint(hwnd);
144     return TRUE;
145 }
146 
147 //-----------------------------------【Game_Paint( )函数】--------------------------------------
148 //    描述:绘制函数,在此函数中进行绘制操作
149 //--------------------------------------------------------------------------------------------------
150 VOID Game_Paint( HWND hwnd )
151 {
152 
153     //先在mdc中贴上背景图
154     SelectObject(g_bufdc,g_hBackGround);
155     BitBlt(g_mdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_bufdc,0,0,SRCCOPY);
156 
157     //创建粒子
158     if(g_StarNum == 0)              //随机设置爆炸点
159     {
160         int    x=rand()%g_rect.right;
161         int    y=rand()%g_rect.bottom;
162 
163         for(int i=0;i<FLYSTAR_NUMBER;i++)       //产生星光粒子
164         {
165             FlyStars[i].x = x;
166             FlyStars[i].y = y;
167             FlyStars[i].lasted = 0;  //设定该粒子存在的时间为零
168              //按粒子编号i来决定粒子在哪个象限运动,且x,y方向的移动速度随机为1—15之间的一个值,由1+rand()%15来完成。
169             if(i%4==0)      
170             {
171                 FlyStars[i].vx =  -(1+rand()%15);
172                 FlyStars[i].vy =  -(1+rand()%15);
173             }
174             if(i%4==1)
175             {
176                 FlyStars[i].vx = 1+rand()%15;
177                 FlyStars[i].vy = 1+rand()%15;
178             }
179             if(i%4==2)
180             {
181                 FlyStars[i].vx = -(1+rand()%15);
182                 FlyStars[i].vy = 1+rand()%15;
183             }
184             if(i%4==3)
185             {
186                 FlyStars[i].vx = 1+rand()%15;
187                 FlyStars[i].vy = -(1+rand()%15);
188             }
189             FlyStars[i].exist = true;  //设定粒子存在
190         }
191         g_StarNum = FLYSTAR_NUMBER;   //全部粒子由for循环设置完成后,我们将粒子数量设为FLYSTAR_NUMBER,代表目前有FLYSTAR_NUMBER颗星光
192     }
193 
194 
195     //显示粒子并更新下一帧的粒子坐标
196     for(int i=0;i<FLYSTAR_NUMBER;i++)
197     {
198         if(FlyStars[i].exist)   //判断粒子是否还存在,若存在,则根据其坐标(FlyStars[i].x,FlyStars[i].y)进行贴图操作
199         {
200             SelectObject(g_bufdc,g_hStar);
201             TransparentBlt(g_mdc,FlyStars[i].x,FlyStars[i].y,30,30,g_bufdc,0,0,30,30,RGB(0,0,0));
202 
203             //计算下一次贴图的坐标
204             FlyStars[i].x+=FlyStars[i].vx;
205             FlyStars[i].y+=FlyStars[i].vy;
206 
207             //在每进行一次贴图后,将粒子的存在时间累加1.
208             FlyStars[i].lasted++;
209             //进行条件判断,若某粒子跑出窗口区域一定的范围,则将该粒子设为不存在,且粒子数随之递减
210             if(FlyStars[i].x<=-10 || FlyStars[i].x>g_rect.right || FlyStars[i].y<=-10 || FlyStars[i].y>g_rect.bottom || FlyStars[i].lasted>FLYSTAR_LASTED_FRAME)
211             {
212                 FlyStars[i].exist = false;  //删除星光粒子 
213                 g_StarNum--;                    //递减星光总数
214             }
215         }
216     }
217 
218     //将mdc中的全部内容贴到hdc中
219     BitBlt(g_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_mdc,0,0,SRCCOPY);
220 
221     g_tPre = GetTickCount();     //记录此次绘图时间
222 }
223 
224 //-----------------------------------【Game_CleanUp( )函数】--------------------------------
225 //    描述:资源清理函数,在此函数中进行程序退出前资源的清理工作
226 //---------------------------------------------------------------------------------------------------
227 BOOL Game_CleanUp( HWND hwnd )
228 {
229     //释放资源对象
230     DeleteObject(g_hBackGround);
231     DeleteObject(g_hStar);
232     DeleteDC(g_bufdc);
233     DeleteDC(g_mdc);
234     ReleaseDC(hwnd,g_hdc);
235     return TRUE;
236 }
View Code 

参考博文:

【Visual C++】游戏开发笔记十二 游戏输入消息处理(一) 键盘消息处理

【Visual C++】游戏开发笔记十三 游戏输入消息处理(二) 鼠标消息处理

【Visual C++】游戏开发笔记十八 游戏基础物理建模(一) 匀速与加速运动

【Visual C++】游戏开发笔记二十 游戏基础物理建模(二) 重力系统的模拟

【Visual C++】游戏开发笔记二十一 游戏基础物理建模(三) 摩擦力系统模拟

【Visual C++】游戏开发笔记二十二 游戏基础物理建模(四) 粒子系统模拟(一)

【Visual C++】游戏开发笔记二十三 游戏基础物理建模(五) 粒子系统模拟(二)

 

转载于:https://www.cnblogs.com/f91og/p/7162074.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
先锋机器人开发库 MobileRobots' Advanced Robot Interface for Applications (ARIA) is a C++ library (software development toolkit or SDK) for all MobileRobots/ActivMedia platforms. ARIA can dynamically control your robot's velocity, heading, relative heading, and other motion parameters either through simple low-level commands or through its high-level Actions infrastructure. ARIA also receives position estimates, sonar readings, and all other current operating data sent by the robot platform. ARIA provides tools to integrate I/O with your own custom hardware—digital, analog, and serial—and includes comprehensive support for all MobileRobots/ActivMedia robot accessories, including the SICK LMS-200, SICK LMS-100, and Hokuyo URG 04-LX laser-range finders, control of the pan-tilt-zoom camera or pan-tilt unit, Pioneer Gripper and Arm, and more. (Some other devices are supported by separate software libraries, either provided by us or the original device manufacturer.) ARIA also includes a library called ArNetworking which implements an extensible infrastructure for easy remote network operations for your robots, user interfaces, and other networked services. Through a server executing on the robot's PC, ArNetworking-enabled clients connect from another computer on the network to get data and issue commands. Download and use our dynamic MobileEyes operator GUI client, to view robot data and issue commands remotely via ArNetworking. A variety of other useful tools for building robot applications are included in ARIA or available as separate libraries, including speech synthesis and recognition; sound effect playback; mathematical functions, cross-platform (Windows/Linux) thread and socket implementations; and more. The ARIA library is written in C++. Access to most of the ARIA API is also available from the Java and Python languages via "wrapper" layers. C++ development with ARIA is supported on GNU/Linux with G++ (3.4 or greater; ARIA is precompiled with the default compiler for the platform but may be recompiled with any other version of G++ greater than 3.4) and on Windows with MS Visual C++ .NET 2003 (7.1), Visual C++ 2008 (9.0) and Visual C++ 2010 (10.0). ARIA comes with full source code under the GNU General Public License. The license allows re-distribution of code as long as all is distributed freely. Proprietary distributions (without releasing your own source code, for example) requires a different, commercial license. (Contact robots@mobilerobots.com for details.) ARIA includes a full API reference manual and example code. Read the distribution README for additional details on getting started with ARIA.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值