《逐梦旅程 WINDOWS游戏编程之从零开始》笔记2——透明贴图,动画技术

第5章 透明贴图

像这样直接贴图会产生这种情况,所以我们需要透明贴图。

透明遮罩法:主要利用BitBlt函数中Raser(光栅)值的运算,需要准备素材图和遮罩图:

这个方法的原理解释见书131页。

示例程序:

  1 #include <windows.h>
  2 
  3 #define WINDOW_WIDTH    800                            //为窗口宽度定义的宏,以方便在此处修改窗口宽度
  4 #define WINDOW_HEIGHT    600                            //为窗口高度定义的宏,以方便在此处修改窗口高度
  5 #define WINDOW_TITLE    L"【致我们永不熄灭的游戏开发梦想】程序核心框架"        //为窗口标题定义的宏
  6 
  7 HDC    g_hdc=NULL,g_mdc=NULL;       //全局设备环境句柄
  8 HBITMAP g_hBackGround,g_hCharacter1,g_hCharacter2;  //定义3个位图句柄,用于3张图片的存放
  9 
 10 LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );  //窗口过程函数
 11 BOOL Game_Init(HWND hwnd); //在此函数中进行资源的初始化
 12 VOID Game_Paint(HWND hwnd); //进行绘图代码的书写
 13 BOOL Game_CleanUp(HWND hwnd); //资源的清理
 14 
 15 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
 16 {
 17     //【1】窗口创建四步曲之一:开始设计一个完整的窗口类
 18     WNDCLASSEX wndClass = { 0 };                            //用WINDCLASSEX定义了一个窗口类,{0}用来初始化结构体
 19     wndClass.cbSize = sizeof( WNDCLASSEX ) ;            //设置结构体的字节数大小
 20     wndClass.style = CS_HREDRAW | CS_VREDRAW;    //设置窗口的样式
 21     wndClass.lpfnWndProc = WndProc;                    //设置指向窗口过程函数的指针
 22     wndClass.cbClsExtra  = 0;                                //窗口类的附加内存,取0就可以了
 23     wndClass.cbWndExtra  = 0;                            //窗口的附加内存,依然取0就行了
 24     wndClass.hInstance = hInstance;                        //指定包含窗口过程的程序的实例句柄。
 25     wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);  //本地加载自定义ico图标
 26     wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。
 27     wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);  //为hbrBackground成员指定一个灰色画刷句柄    
 28     wndClass.lpszMenuName = NULL;                        //用一个以空终止的字符串,指定菜单资源的名字。
 29     wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";        //用一个以空终止的字符串,指定窗口类的名字。
 30 
 31     //【2】窗口创建四步曲之二:注册窗口类
 32     if( !RegisterClassEx( &wndClass ) )                //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
 33         return -1;        
 34 
 35     //【3】窗口创建四步曲之三:正式创建窗口
 36     HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE,        //喜闻乐见的创建窗口函数CreateWindow
 37         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
 38         WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
 39 
 40     //【4】窗口创建四步曲之四:窗口的移动、显示与更新
 41     MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true);        //调整窗口显示时的位置,使窗口左上角位于(250,80)处
 42     ShowWindow( hwnd, nShowCmd );    //调用ShowWindow函数来显示窗口,第二个参数用于指定窗口的显示状态
 43     UpdateWindow(hwnd);                        //对窗口进行更新,就像我们买了新房子要装修一样
 44 
 45     //游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE
 46     if(!(Game_Init(hwnd)))
 47     {
 48         MessageBox(hwnd,L"资源初始化失败",L"消息窗口",0);
 49         return FALSE;
 50     }
 51 
 52     //【5】消息循环过程
 53     MSG msg = { 0 };        //定义并初始化msg
 54     while( msg.message != WM_QUIT )            //使用while循环,如果消息不是WM_QUIT消息,就继续循环
 55     {
 56         if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。
 57         {
 58             TranslateMessage( &msg );        //将虚拟键消息转换为字符消息
 59             DispatchMessage( &msg );            //分发一个消息给窗口程序。
 60         }
 61     }
 62 
 63     //【6】窗口类的注销
 64     UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);  //程序准备结束,注销窗口类
 65     return 0;  
 66 }
 67 
 68 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )      
 69 {
 70     PAINTSTRUCT paintStruct;    //定义一个PAINTSTRUCT结构体来记录一些绘制信息
 71 
 72     switch( message )                        //switch语句开始
 73     {
 74     case WM_PAINT:                        // 若是客户区重绘消息
 75         g_hdc=BeginPaint(hwnd,&paintStruct);    //指定窗口进行绘图工作的准备,并将和绘图有关的信息填充到paintstruct结构体中
 76         Game_Paint(hwnd);
 77         EndPaint(hwnd,&paintStruct);    //EndPaint函数标记指定窗口的绘画过程结束
 78         ValidateRect(hwnd, NULL);        // 更新客户区的显示
 79         break;                                    //跳出该switch语句
 80 
 81     case WM_KEYDOWN:                // 若是键盘按下消息
 82         if (wParam == VK_ESCAPE)    // 如果被按下的键是ESC
 83             DestroyWindow(hwnd);        // 销毁窗口, 并发送一条WM_DESTROY消息
 84         break;                                    //跳出该switch语句
 85 
 86     case WM_DESTROY:                //若是窗口销毁消息
 87         PostQuitMessage( 0 );        //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
 88         break;                                //跳出该switch语句
 89 
 90     default:                                    //若上述case条件都不符合,则执行该default语句
 91         return DefWindowProc( hwnd, message, wParam, lParam );        //调用缺省的窗口过程
 92     }
 93     return 0;            //正常退出
 94 }
 95 
 96 //初始化函数,进行一些简单的初始化
 97 BOOL Game_Init(HWND hwnd)
 98 {
 99     g_hdc = GetDC(hwnd);  //获取设备环境句柄
100 
101     //-----【位图绘制四步曲之一:加载位图】-----
102     //从文件加载3张位图
103     g_hBackGround = (HBITMAP)LoadImage(NULL,L"bg.bmp",IMAGE_BITMAP,WINDOW_WIDTH,WINDOW_HEIGHT,LR_LOADFROMFILE);   
104     g_hCharacter1 = (HBITMAP)LoadImage(NULL,L"character1.bmp",IMAGE_BITMAP,640,579,LR_LOADFROMFILE);  
105     g_hCharacter2 =  (HBITMAP)LoadImage(NULL,L"character2.bmp",IMAGE_BITMAP,800,584,LR_LOADFROMFILE);
106 
107     //-----【位图绘制四步曲之二:建立兼容DC】-----
108     g_mdc = CreateCompatibleDC(g_hdc);    //建立兼容设备环境的内存DC
109 
110     Game_Paint(hwnd);
111     ReleaseDC(hwnd,g_hdc);  //释放设备环境
112     return TRUE;
113 }
114 
115 //绘制函数
116 VOID Game_Paint(HWND hwnd)
117 {
118     //先贴上背景图
119     SelectObject(g_mdc,g_hBackGround);
120     BitBlt(g_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_mdc,0,0,SRCCOPY);    //采用BitBlt函数在g_hdc中先贴上背景图
121 
122     //用透明遮罩法绘制出第一个人物
123     SelectObject(g_mdc,g_hCharacter1);
124     BitBlt(g_hdc,50,WINDOW_HEIGHT-579,320,640,g_mdc,320,0,SRCAND);//透明遮罩法第一步,即将屏蔽图与背景图做"AND"运算 
125     BitBlt(g_hdc,50,WINDOW_HEIGHT-579,320,640,g_mdc,0,0,SRCPAINT);//透明遮罩法第二步,即将前景图与背景图做"OR"运算
126 
127     //用透明遮罩法绘制出第二个人物
128     SelectObject(g_mdc,g_hCharacter2);
129     BitBlt(g_hdc,450,WINDOW_HEIGHT-584,400,584,g_mdc,400,0,SRCAND);//透明遮罩法第一步,即将屏蔽图与背景图做"AND"运算
130     BitBlt(g_hdc,450,WINDOW_HEIGHT-584,400,584,g_mdc,0,0,SRCPAINT);//透明遮罩法第二步,即将前景图与背景图做"OR"运算
131 }
132 
133 //清理资源
134 BOOL Game_CleanUp(HWND hwnd)
135 {
136     //释放资源对象
137     DeleteObject(g_hBackGround);
138     DeleteObject(g_hCharacter2);
139     DeleteObject(g_hCharacter1);
140     DeleteDC(g_mdc);
141     return TRUE;
142 }
View Code

透明彩色法

利用在贴图时可以设置某种颜色为透明色的函数,比如TransparentBlt函数,AlphaBlend函数

示例程序(貌似有问题,暂且不贴出了)


第6章 动画技术

首先是使用定时器,通过调用SetTimer来创建一个定时器,每隔指定的时间间隔给我们的程序发送WM_TIMER消息。

示例程序:

  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"【致我们永不熄灭的游戏开发梦想】程序核心框架"        //为窗口标题定义的宏
  9 
 10 HDC    g_hdc=NULL,g_mdc=NULL;       //全局设备环境句柄
 11 HBITMAP    g_hSprite[12];     //声明位图数组用来储存各张人物位图
 12 int    g_iNum=0;         //"g_iNum"变量用来记录目前显示的图号
 13 
 14 LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );  //窗口过程函数
 15 BOOL Game_Init(HWND hwnd); //在此函数中进行资源的初始化
 16 VOID Game_Paint(HWND hwnd); //进行绘图代码的书写
 17 BOOL Game_CleanUp(HWND hwnd); //资源的清理
 18 
 19 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
 20 {
 21     //【1】窗口创建四步曲之一:开始设计一个完整的窗口类
 22     WNDCLASSEX wndClass = { 0 };                            //用WINDCLASSEX定义了一个窗口类,{0}用来初始化结构体
 23     wndClass.cbSize = sizeof( WNDCLASSEX ) ;            //设置结构体的字节数大小
 24     wndClass.style = CS_HREDRAW | CS_VREDRAW;    //设置窗口的样式
 25     wndClass.lpfnWndProc = WndProc;                    //设置指向窗口过程函数的指针
 26     wndClass.cbClsExtra  = 0;                                //窗口类的附加内存,取0就可以了
 27     wndClass.cbWndExtra  = 0;                            //窗口的附加内存,依然取0就行了
 28     wndClass.hInstance = hInstance;                        //指定包含窗口过程的程序的实例句柄。
 29     wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);  //本地加载自定义ico图标
 30     wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。
 31     wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);  //为hbrBackground成员指定一个灰色画刷句柄    
 32     wndClass.lpszMenuName = NULL;                        //用一个以空终止的字符串,指定菜单资源的名字。
 33     wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";        //用一个以空终止的字符串,指定窗口类的名字。
 34 
 35     //【2】窗口创建四步曲之二:注册窗口类
 36     if( !RegisterClassEx( &wndClass ) )                //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
 37         return -1;        
 38 
 39     //【3】窗口创建四步曲之三:正式创建窗口
 40     HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE,        //喜闻乐见的创建窗口函数CreateWindow
 41         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
 42         WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
 43 
 44     //【4】窗口创建四步曲之四:窗口的移动、显示与更新
 45     MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true);        //调整窗口显示时的位置,使窗口左上角位于(250,80)处
 46     ShowWindow( hwnd, nShowCmd );    //调用ShowWindow函数来显示窗口,第二个参数用于指定窗口的显示状态
 47     UpdateWindow(hwnd);                        //对窗口进行更新,就像我们买了新房子要装修一样
 48 
 49     //游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE
 50     if(!(Game_Init(hwnd)))
 51     {
 52         MessageBox(hwnd,L"资源初始化失败",L"消息窗口",0);
 53         return FALSE;
 54     }
 55 
 56     //【5】消息循环过程
 57     MSG msg = { 0 };        //定义并初始化msg
 58     while( msg.message != WM_QUIT )            //使用while循环,如果消息不是WM_QUIT消息,就继续循环
 59     {
 60         if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。
 61         {
 62             TranslateMessage( &msg );        //将虚拟键消息转换为字符消息
 63             DispatchMessage( &msg );            //分发一个消息给窗口程序。
 64         }
 65     }
 66 
 67     //【6】窗口类的注销
 68     UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);  //程序准备结束,注销窗口类
 69     return 0;  
 70 }
 71 
 72 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )      
 73 {
 74     PAINTSTRUCT paintStruct;    //定义一个PAINTSTRUCT结构体来记录一些绘制信息
 75 
 76     switch( message )                        //switch语句开始
 77     {
 78     case WM_TIMER:                        //定时器消息
 79         Game_Paint(hwnd);                //调用Game_Paint()函数进行窗口绘图
 80         break;                                    //跳出该switch语句;                                    //跳出该switch语句
 81 
 82     case WM_KEYDOWN:                // 若是键盘按下消息
 83         if (wParam == VK_ESCAPE)    // 如果被按下的键是ESC
 84             DestroyWindow(hwnd);        // 销毁窗口, 并发送一条WM_DESTROY消息
 85         break;                                    //跳出该switch语句
 86 
 87     case WM_DESTROY:                //若是窗口销毁消息
 88         Game_CleanUp(hwnd);            //调用自定义的资源清理函数Game_CleanUp()进行退出前的资源清理
 89         PostQuitMessage( 0 );        //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
 90         break;                                //跳出该switch语句
 91 
 92     default:                                    //若上述case条件都不符合,则执行该default语句
 93         return DefWindowProc( hwnd, message, wParam, lParam );        //调用缺省的窗口过程
 94     }
 95     return 0;            //正常退出
 96 }
 97 
 98 //初始化函数,进行一些简单的初始化
 99 BOOL Game_Init(HWND hwnd)
100 {
101     g_hdc = GetDC(hwnd);  //获取设备环境句柄
102 
103     wchar_t   filename[20];
104     //载入各个萝莉位图
105     for(int i=0;i<12;i++)      
106     {
107         memset(filename, 0, sizeof(filename));  //filename的初始化
108         swprintf_s(filename,L"%d.bmp",i);  //调用swprintf_s函数,“组装”出对应的图片文件名称
109         g_hSprite[i] = (HBITMAP)LoadImage(NULL,filename,IMAGE_BITMAP,WINDOW_WIDTH,WINDOW_HEIGHT,LR_LOADFROMFILE);
110     }
111     //-----【位图绘制四步曲之二:建立兼容DC】-----
112     g_mdc = CreateCompatibleDC(g_hdc);    //建立兼容设备环境的内存DC
113 
114     g_iNum = 0;                     //设置初始的显示图号为"0"
115     SetTimer(hwnd,1,90,NULL);   //建立定时器,间隔0.09秒发出消息,第四个参数取NULL表示我们会在消息处理函数中对WM_TIMER消息添加相应的代码
116 
117     Game_Paint(hwnd);
118     return TRUE;
119 }
120 
121 //绘制函数
122 VOID Game_Paint(HWND hwnd)
123 {
124     //处理图号
125     if(g_iNum == 11)               //判断是否超过最大图号,若超过最大图号“12”,则将显示图号重设为"0"。
126         g_iNum = 0;               
127 
128     //依据图号来贴图
129     SelectObject(g_mdc,g_hSprite[g_iNum]);//根据图号选入对应的位图
130     BitBlt(g_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_mdc,0,0,SRCCOPY);         //以目前图号进行窗口贴图
131 
132     //图号自增
133     g_iNum++;                    //将“g_iNum”值加1,为下一次要显示的图号
134 }
135 
136 //清理资源
137 BOOL Game_CleanUp(HWND hwnd)
138 {
139     KillTimer(hwnd,1);   //删除所建立的定时器  
140     //释放资源对象
141     for(int i=0;i<12;i++)
142         DeleteObject(g_hSprite[i]);
143     DeleteDC(g_mdc);
144     ReleaseDC(hwnd,g_hdc);  //释放设备环境
145     return TRUE;
146 }
View Code

这样就能连续切换一组图片,形成了动画效果。

游戏循环技术是目前WIndows游戏中普遍采用的动画显示技术。

透明动画

就是透明贴图在加上游戏循环来显示动画的技巧

排序贴图

示例程序:

  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 //    描述:定义一些辅助宏
  9 //------------------------------------------------------------------------------------------------
 10 #define WINDOW_WIDTH    800                            //为窗口宽度定义的宏,以方便在此处修改窗口宽度
 11 #define WINDOW_HEIGHT    600                            //为窗口高度定义的宏,以方便在此处修改窗口高度
 12 #define WINDOW_TITLE        L"【致我们永不熄灭的游戏开发梦想】游戏动画显示技术之 排序贴图"    //为窗口标题定义的宏
 13 #define SPRITE_NUMBER 30  //定义宏SPRITE_NUMBER,表示画面上要出现的人物数目,在此设定为30个
 14 
 15 //-----------------------------------【全局结构体定义部分】-------------------------------------
 16 //    描述:全局结构体定义
 17 //------------------------------------------------------------------------------------------------
 18 struct Sprites        //定义sprite结构,代表画面上的人物,其结构成员x和y为贴图坐标,direction为目前人物的移动方向
 19 {
 20     int x,y;            //x和y为贴图坐标
 21     int direction; // direction为目前人物的移动方向
 22 }; 
 23 
 24 //-----------------------------------【全局变量声明部分】-------------------------------------
 25 //    描述:全局变量的声明
 26 //------------------------------------------------------------------------------------------------
 27 HDC            g_hdc=NULL,g_mdc=NULL,g_bufdc=NULL;      //全局设备环境句柄,两个全局内存DC句柄
 28 HBITMAP        g_hSprite[4],g_hBackGround;                                //声明位图数组用来储存各张人物位图
 29 DWORD        g_tPre=0,g_tNow=0;                    //声明l两个函数来记录时间,g_tPre记录上一次绘图的时间,g_tNow记录此次准备绘图的时间
 30 int            g_iPicNum=0,g_iX=0,g_iY=0;                //g_iPicNum用来记录图号,g_iX,g_iY分别记录贴图的横纵坐标            
 31 Sprites        Sprite[SPRITE_NUMBER];   //按照SPRITE_NUMBER的值建立数组Sprite[]
 32 
 33 //-----------------------------------【全局函数声明部分】-------------------------------------
 34 //    描述:全局函数声明,防止“未声明的标识”系列错误
 35 //------------------------------------------------------------------------------------------------
 36 LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );//窗口过程函数
 37 BOOL                        Game_Init(HWND hwnd);            //在此函数中进行资源的初始化
 38 VOID                            Game_Paint( HWND hwnd);        //在此函数中进行绘图代码的书写
 39 BOOL                        Game_CleanUp(HWND hwnd );    //在此函数中进行资源的清理
 40 
 41 //-----------------------------------【WinMain( )函数】--------------------------------------
 42 //    描述:Windows应用程序的入口函数,我们的程序从这里开始
 43 //------------------------------------------------------------------------------------------------
 44 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
 45 {
 46     //【1】窗口创建四步曲之一:开始设计一个完整的窗口类
 47     WNDCLASSEX wndClass = { 0 };                            //用WINDCLASSEX定义了一个窗口类
 48     wndClass.cbSize = sizeof( WNDCLASSEX ) ;            //设置结构体的字节数大小
 49     wndClass.style = CS_HREDRAW | CS_VREDRAW;    //设置窗口的样式
 50     wndClass.lpfnWndProc = WndProc;                    //设置指向窗口过程函数的指针
 51     wndClass.cbClsExtra        = 0;                                //窗口类的附加内存,取0就可以了
 52     wndClass.cbWndExtra        = 0;                            //窗口的附加内存,依然取0就行了
 53     wndClass.hInstance = hInstance;                        //指定包含窗口过程的程序的实例句柄。
 54     wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);  //本地加载自定义ico图标
 55     wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。
 56     wndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  //为hbrBackground成员指定一个白色画刷句柄    
 57     wndClass.lpszMenuName = NULL;                        //用一个以空终止的字符串,指定菜单资源的名字。
 58     wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";        //用一个以空终止的字符串,指定窗口类的名字。
 59 
 60     //【2】窗口创建四步曲之二:注册窗口类
 61     if( !RegisterClassEx( &wndClass ) )                //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
 62         return -1;        
 63 
 64     //【3】窗口创建四步曲之三:正式创建窗口
 65     HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE,                //喜闻乐见的创建窗口函数CreateWindow
 66         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
 67         WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
 68 
 69     //【4】窗口创建四步曲之四:窗口的移动、显示与更新
 70     MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true);        //调整窗口显示时的位置,使窗口左上角位于(250,80)处
 71     ShowWindow( hwnd, nShowCmd );    //调用ShowWindow函数来显示窗口
 72     UpdateWindow(hwnd);                        //对窗口进行更新,就像我们买了新房子要装修一样
 73 
 74     //游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE
 75     if (!Game_Init (hwnd)) 
 76     {
 77         MessageBox(hwnd, L"资源初始化失败", L"消息窗口", 0); //使用MessageBox函数,创建一个消息窗口
 78         return FALSE;
 79     }
 80     PlaySound(L"OrcTheme.wav", NULL, SND_FILENAME | SND_ASYNC|SND_LOOP); //循环播放背景音乐 
 81 
 82     //【5】消息循环过程
 83     MSG msg = { 0 };                //定义并初始化msg
 84     while( msg.message != WM_QUIT )        //使用while循环,如果消息不是WM_QUIT消息,就继续循环
 85     {
 86         if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。
 87         {
 88             TranslateMessage( &msg );        //将虚拟键消息转换为字符消息
 89             DispatchMessage( &msg );            //分发一个消息给窗口程序。
 90         }
 91         else
 92         {
 93             g_tNow = GetTickCount();   //获取当前系统时间
 94             if(g_tNow-g_tPre >= 150)        //当此次循环运行与上次绘图时间相差0.1秒时再进行重绘操作
 95                 Game_Paint(hwnd);
 96         }
 97 
 98     }
 99 
100     //【6】窗口类的注销
101     UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);  //程序准备结束,注销窗口类
102     return 0;  
103 }
104 
105 //-----------------------------------【WndProc( )函数】--------------------------------------
106 //    描述:窗口过程函数WndProc,对窗口消息进行处理
107 //------------------------------------------------------------------------------------------------
108 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )      
109 {
110 
111     switch( message )                        //switch语句开始
112     {
113 
114     case WM_KEYDOWN:                    // 若是键盘按下消息
115         if (wParam == VK_ESCAPE)    // 如果被按下的键是ESC
116             DestroyWindow(hwnd);        // 销毁窗口, 并发送一条WM_DESTROY消息
117         break;                                    //跳出该switch语句
118 
119     case WM_DESTROY:                    //若是窗口销毁消息
120         Game_CleanUp(hwnd);            //调用自定义的资源清理函数Game_CleanUp()进行退出前的资源清理
121         PostQuitMessage( 0 );            //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
122         break;                                    //跳出该switch语句
123 
124     default:                                        //若上述case条件都不符合,则执行该default语句
125         return DefWindowProc( hwnd, message, wParam, lParam );        //调用缺省的窗口过程
126     }
127 
128     return 0;                                    //正常退出
129 }
130 
131 
132 //-----------------------------------【BubSort( )函数】--------------------------------------
133 //    描述:进行气泡法排序
134 //------------------------------------------------------------------------------------------------
135 VOID        BubSort(int n)
136 {
137     int i,j;
138     bool f;
139     Sprites tmp;
140 
141     for(i=0;i<n-1;i++)
142     {
143         f = false;
144         for(j=0;j<n-i-1;j++)
145         {
146             if(Sprite[j+1].y < Sprite[j].y)
147             {
148                 tmp = Sprite[j+1];
149                 Sprite[j+1] = Sprite[j];
150                 Sprite[j] = tmp;
151                 f = true;
152             }
153         }
154         if(!f)
155             break;
156     }
157 }
158 
159 
160 
161 //-----------------------------------【Game_Init( )函数】--------------------------------------
162 //    描述:初始化函数,进行一些简单的初始化
163 //------------------------------------------------------------------------------------------------
164 BOOL Game_Init( HWND hwnd )
165 {
166     srand((unsigned)time(NULL));      //用系统时间初始化随机种子
167     HBITMAP bmp;
168 
169     g_hdc = GetDC(hwnd);  
170     g_mdc = CreateCompatibleDC(g_hdc);  //创建一个和hdc兼容的内存DC
171     g_bufdc = CreateCompatibleDC(g_hdc);//再创建一个和hdc兼容的缓冲DC
172     bmp = CreateCompatibleBitmap(g_hdc,WINDOW_WIDTH,WINDOW_HEIGHT); //建一个和窗口兼容的空的位图对象
173 
174     SelectObject(g_mdc,bmp);//将空位图对象放到mdc中
175 
176     //加载各张跑动图及背景图
177     g_hBackGround= (HBITMAP)LoadImage(NULL,L"bg.bmp",IMAGE_BITMAP,WINDOW_WIDTH,WINDOW_HEIGHT,LR_LOADFROMFILE);
178     g_hSprite[0] = (HBITMAP)LoadImage(NULL,L"11.bmp",IMAGE_BITMAP,384,96,LR_LOADFROMFILE);
179     g_hSprite[1] = (HBITMAP)LoadImage(NULL,L"22.bmp",IMAGE_BITMAP,384,96,LR_LOADFROMFILE);
180     g_hSprite[2] = (HBITMAP)LoadImage(NULL,L"33.bmp",IMAGE_BITMAP,384,96,LR_LOADFROMFILE);
181     g_hSprite[3] = (HBITMAP)LoadImage(NULL,L"44.bmp",IMAGE_BITMAP,384,96,LR_LOADFROMFILE);
182 
183 
184     //设定初始的贴图坐标都为窗口内的任意坐标,初始的移动方向都为向左。
185     for(int i=0;i<SPRITE_NUMBER;i++)
186     {
187         Sprite[i].direction = 3;    //起始方向
188         Sprite[i].x = rand()%WINDOW_WIDTH;       //贴图的起始X坐标
189         Sprite[i].y = rand()%WINDOW_HEIGHT;    //贴图的起始Y坐标
190     }
191 
192     Game_Paint(hwnd);
193     return TRUE;
194 }
195 
196 //-----------------------------------【Game_Paint( )函数】--------------------------------------
197 //    描述:绘制函数,在此函数中进行绘制操作
198 //--------------------------------------------------------------------------------------------------
199 VOID Game_Paint( HWND hwnd )
200 {
201     if(g_iPicNum == 4)        //判断是否超过最大图号,若超过最大图号“3”,则将显示图号重设为"0"。
202         g_iPicNum = 0;
203 
204     //在mdc中贴上背景图
205     SelectObject(g_bufdc,g_hBackGround);
206     BitBlt(g_mdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_bufdc,0,0,SRCCOPY);
207 
208     BubSort(SPRITE_NUMBER);    //贴上人物图之前调用BubSort()函数进行排序
209 
210     for(int i=0;i<SPRITE_NUMBER;i++)
211     {
212         SelectObject(g_bufdc,g_hSprite[Sprite[i].direction]);
213         TransparentBlt(g_mdc,Sprite[i].x,Sprite[i].y,96,96,g_bufdc,g_iPicNum*96,0,96,96,RGB(0,0,0));//采用TransparentBlt透明色彩法
214     }
215 
216     //将最后的画面显示在窗口中
217     BitBlt(g_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_mdc,0,0,SRCCOPY);
218 
219 
220     g_tPre = GetTickCount();     //记录此次绘图时间
221     g_iPicNum++;
222 
223 
224     //下面这个for循环,决定每一只精灵下一次的移动方向及贴图坐标
225     for(int i=0;i<SPRITE_NUMBER;i++)
226     {
227         switch(rand()%4)          //随机数除以4的余数来决定下次移动方向,余数0,1,2,3分别代表上,下,左,右
228         {
229         case 0:                         //
230             Sprite[i].y -= 20;
231             //在计算出新的贴图坐标之后,还需判断此新的坐标会不会使得人物贴图超出窗口边界,若超出,则将该方向上的坐标设定为刚好等于临界值
232             if(Sprite[i].y < 0)
233                 Sprite[i].y = 0;
234             Sprite[i].direction = 0;
235             break;
236             //其他方向按照和上面相同的方法计算
237         case 1:        //
238             Sprite[i].y += 20;
239             if(Sprite[i].y > WINDOW_HEIGHT-100)
240                 Sprite[i].y = WINDOW_HEIGHT-100;
241             Sprite[i].direction = 1;
242             break;
243         case 2:                        //
244             Sprite[i].x-= 20;
245             if(Sprite[i].x < 0)
246                 Sprite[i].x = 0;
247             Sprite[i].direction = 2;
248             break;
249         case 3:                        //
250             Sprite[i].x+= 20;
251 
252             if(Sprite[i].x >WINDOW_WIDTH-100)
253                 Sprite[i].x = WINDOW_WIDTH-100;
254             Sprite[i].direction = 3;
255             break;
256         }
257     }
258 }
259 
260 //-----------------------------------【Game_CleanUp( )函数】--------------------------------
261 //    描述:资源清理函数,在此函数中进行程序退出前资源的清理工作
262 //---------------------------------------------------------------------------------------------------
263 BOOL Game_CleanUp( HWND hwnd )
264 {
265     //释放资源对象
266     DeleteObject(g_hBackGround);
267     for (int i=0;i<4;i++)
268     {
269         DeleteObject(g_hSprite[i]);
270     }
271     DeleteDC(g_bufdc);
272     DeleteDC(g_mdc);
273     ReleaseDC(hwnd,g_hdc);
274     return TRUE;
275 }
View Code

参考博客:

【Visual C++】游戏开发笔记之六——游戏画面绘图(三)透明特效的制作方法

【Visual C++】游戏开发笔记之十 基础动画显示(三) 透明动画的实现

【Visual C++】游戏开发笔记之十一 基础动画显示(四) 排序贴图

【Visual C++】游戏开发笔记之七——基础动画显示(一)定时器的使用

【Visual C++】游戏开发笔记之八——基础动画显示(二)游戏循环的使用

原文地址:https://www.cnblogs.com/f91og/p/7146569.html