逐梦旅程:Windows游戏编程之从零开始6.4 排序贴图_逐梦旅程:Windows游戏编程之从零开始6.4 排序贴图试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > 编程 > 逐梦旅程:Windows游戏编程之从零开始 > 6.4 排序贴图

逐梦旅程:Windows游戏编程之从零开始——6.4 排序贴图

6.4 排序贴图 “排序贴图”是源自于物体远近呈现的一种贴图概念。回忆我们之前一直运用的贴图思想,先进行距离比较远的物体的贴图操作,然后再进行近距离物体的贴图操作,一旦定出贴图的顺序之后就无法再改变了。 然而这样的作法在画面上物体会彼此遮掩的情况下就会不适用。也许会出现后面的物体反而遮住了前面的物体的这种不协调的画面。为了避免这种因为贴图顺序固定而产生的错误画面,必须在每一次窗口重新显示时动态地重新决定画面上每一个物体的贴图顺序。 那么,如何动态决定贴图顺序呢?我们可以采用排序的方式。 为了演示排序如何运用在贴图中,我们举一个例子。假设现在有10只要进行贴图的小牛图案,先把它存在一个数组之中,从2D平面的远近角度来看,Y轴坐标比较小的是比较远的物体。如果我们以小牛的Y轴坐标(要排序的值被我们称作键值)来对小牛数组由小到大进行排序,最后会使得Y轴坐标小的小牛排在数组的前面,而进行画面贴图时则由数组由小到大一个个进行处理,这样便可实现“远的物体先贴图“的目的了。 这里我们使用C++教程中不少运用的气泡排序(Bubble Sort)作为我们的排序法。因为此方法有程序代码简单,排序效率中等,属于稳定(stable)排序法的特点。这里的稳定排序法的特性,会使得Y轴坐标相同的物体,不必再去考虑它X坐标上的排序。 下面我们贴出以C/C++写出的气泡排序法的代码,对“pop[ ]”数组的各数据成员的Y值为键值来排序,输出的参数为“n”表示要排序的数组大小。 1. //----------------------【BubSort( )函数】---------------------------------- 2. // 描述:进行气泡法排序 3. //------------------------------------------------------------------------- 4. VOID BubSort(int n) 5. { 6. int i,j; 7. bool f; 8. Sprites tmp; 9. 10. for(i=0;i<n-1;i++) 11. { 12. f = false; 13. for(j=0;j<n-i-1;j++) 14. { 15. if(Sprite[j+1].y < Sprite[j].y) 16. { 17. tmp = Sprite[j+1]; 18. Sprite[j+1] = Sprite[j]; 19. Sprite[j] = tmp; 20. f = true; 21. } 22. } 23. if(!f) 24. break; 25. } 26. } 各种排序法为C/C++中比较核心的知识点,还不太熟悉的朋友,可以参看各种C++,数据结构的教程进行深入学习。在这里我们就不多做介绍了。 接下来,我们来利用示例程序GDIdemo9来演示气泡排序法在画面上贴图的运用,让动画能呈现出接近真实的远近层次效果。 这个示例比较有趣,会产生多只小精灵随机跑动,每次进行画面贴图前先完成排序操作,呈现出比较顺畅真实的动画来。 先看一下人物素材图: 11.bmp 22.bmp 33.bmp 44.bmp 然后我们来看程序代码: 程序代码片段一,宏、全局结构体和全局变量声明: 1. //----------------------【宏定义部分】--------------------------------------- 2. // 描述:定义一些辅助宏 3. //------------------------------------------------------------------------- 4. #define WINDOW_WIDTH 800 //为窗口宽度定义的宏,以方便在此处修改窗口宽度 5. #define WINDOW_HEIGHT 600 //为窗口高度定义的宏,以方便在此处修改窗口高度 6. #define SPRITE_NUMBER 30 //定义宏SPRITE_NUMBER,表示画面上要出现的人物数目,在此设定为30个 7. 8. //--------------------【全局结构体定义部分】------------------------------------ 9. // 描述:全局结构体定义 10. //------------------------------------------------------------------------- 11. struct Sprites //定义sprite结构,代表画面上的人物,其结构成员x和y为贴图坐标,direction为目前人物的移动方向 12. { 13. int x,y; //x和y为贴图坐标 14. int direction; // direction为目前人物的移动方向 15. }; 16. 17. //----------------------【全局变量声明部分】----------------------------------- 18. // 描述:全局变量的声明 19. //------------------------------------------------------------------------- 20. HDC g_hdc=NULL,g_mdc=NULL,g_bufdc=NULL; //全局设备环境句柄,两个全局内存DC句柄 21. HBITMAP g_hSprite[4],g_hBackGround; //声明位图数组用来储存各张人物位图,以及背景图 22. DWORD g_tPre=0,g_tNow=0; //声明l两个函数来记录时间,g_tPre记录上一次绘图的时间,g_tNow记录此次准备绘图的时间 23. int g_iPicNum=0,g_iX=0,g_iY=0; //g_iPicNum用来记录图号,g_iX,g_iY分别记录贴图的横纵坐标 24. Sprites Sprite[SPRITE_NUMBER]; //按照SPRITE_NUMBER的值建立数组Sprite[] 第6行代码精析:定义一个SPRITE_NUMBER宏,这样就方便设置想在画面上出现的人物个数了。 第11~15行代码精析:定义一个人物信息结构体,表示画面上的人物,其结构成员x和y为贴图坐标,direction为目前人物的移动方向。 程序代码片段二,Game_Init()函数: 1. //--------------------【Game_Init( )函数】----------------------------------- 2. // 描述:初始化函数,进行一些简单的初始化 3. //------------------------------------------------------------------------- 4. BOOL Game_Init( HWND hwnd ) 5. { 6. srand((unsigned)time(NULL)); //用系统时间初始化随机种子 7. HBITMAP bmp; 8. 9. g_hdc = GetDC(hwnd); 10. g_mdc = CreateCompatibleDC(g_hdc); //创建一个和hdc兼容的内存DC 11. g_bufdc = CreateCompatibleDC(g_hdc);//再创建一个和hdc兼容的缓冲DC 12. bmp = CreateCompatibleBitmap(g_hdc,WINDOW_WIDTH,WINDOW_HEIGHT); //建一个和窗口兼容的空的位图对象 13. 14. SelectObject(g_mdc,bmp);//将空位图对象放到mdc中 15. 16. //加载各张跑动图及背景图 17. g_hBackGround= (HBITMAP)LoadImage(NULL,L"bg.bmp",IMAGE_BITMAP, WINDOW_WIDTH, WINDOW_HEIGHT,LR_LOADFROMFILE); 18. g_hSprite[0] = (HBITMAP)LoadImage(NULL,L"11.bmp",IMAGE_BITMAP, 384,96,LR_LOADFROMFILE); 19. g_hSprite[1] = (HBITMAP)LoadImage(NULL,L"22.bmp",IMAGE_BITMAP, 384,96,LR_LOADFROMFILE); 20. g_hSprite[2] = (HBITMAP)LoadImage(NULL,L"33.bmp",IMAGE_BITMAP, 384,96,LR_LOADFROMFILE); 21. g_hSprite[3] = (HBITMAP)LoadImage(NULL,L"44.bmp",IMAGE_BITMAP, 384,96,LR_LOADFROMFILE); 22. 23. 24. //设定初始的贴图坐标都为窗口内的任意坐标,初始的移动方向都为向左。 25. for(int i=0;i<SPRITE_NUMBER;i++) 26. { 27. Sprite[i].direction = 3; //起始方向 28. Sprite[i].x = rand()%WINDOW_WIDTH; //贴图的起始X坐标 29. Sprite[i].y = rand()%WINDOW_HEIGHT; //贴图的起始Y坐标 30. } 31. 32. Game_Paint(hwnd); 33. return TRUE; 34. } 第22~30行代码精析:用一个for循环来初始化所有人物的初始坐标和初始反向,其中初始坐标用两个rand()函数设定在窗口内部的任意位置,初始方向为确定的一个朝向。 程序代码片段三,Game_Paint()函数: 1. //-----------------------【Game_Paint( )函数】------------------------------ 2. // 描述:绘制函数,在此函数中进行绘制操作 3. //------------------------------------------------------------------------- 4. VOID Game_Paint( HWND hwnd ) 5. { 6. if(g_iPicNum == 4) //判断是否超过最大图号,若超过最大图号“3”,则将显示图号重设为"0"。 7. g_iPicNum = 0; 8. 9. //在mdc中贴上背景图 10. SelectObject(g_bufdc,g_hBackGround); 11. BitBlt(g_mdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_bufdc,0,0,SRCCOPY); 12. 13. BubSort(SPRITE_NUMBER); //贴上人物图之前调用BubSort()函数进行排序 14. 15. for(int i=0;i<SPRITE_NUMBER;i++) 16. { 17. SelectObject(g_bufdc,g_hSprite[Sprite[i].direction]); 18. TransparentBlt(g_mdc,Sprite[i].x,Sprite[i].y,96,96,g_bufdc,g_iPicNum *96, 0,96,96,RGB(0,0,0));//采用TransparentBlt透明色彩法,透明色RGB(0,0,0) 19. } 20. 21. //将最后的画面显示在窗口中 22. BitBlt(g_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_mdc,0,0,SRCCOPY); 23. 24. g_tPre = GetTickCount(); //记录此次绘图时间 25. g_iPicNum++; 26. 27. //下面这个for循环,决定每一只精灵下一次的移动方向及贴图坐标 28. for(int i=0;i<SPRITE_NUMBER;i++) 29. { 30. switch(rand()%4) //随机数除以4的余数来决定下次移动方向,余数0,1,2,3分别代表上,下,左,右 31. { 32. case 0: //上 33. Sprite[i].y -= 20; 34. //在计算出新的贴图坐标之后,还需判断此新的坐标会不会使得人物贴图超出窗口边界,若超出,则将该方向上的坐标设定为刚好等于临界值 35. if(Sprite[i].y < 0) 36. Sprite[i].y = 0; 37. Sprite[i].direction = 0; 38. break; 39. //其他方向按照和上面相同的方法计算 40. case 1: //下 41. Sprite[i].y += 20; 42. if(Sprite[i].y > WINDOW_HEIGHT-100) 43. Sprite[i].y = WINDOW_HEIGHT-100; 44. Sprite[i].direction = 1; 45. break; 46. case 2: //左 47. Sprite[i].x-= 20; 48. if(Sprite[i].x < 0) 49. Sprite[i].x = 0; 50. Sprite[i].direction = 2; 51. break; 52. case 3: //右 53. Sprite[i].x+= 20; 54. 55. if(Sprite[i].x >WINDOW_WIDTH-100) 56. Sprite[i].x = WINDOW_WIDTH-100; 57. Sprite[i].direction = 3; 58. break; 59. } 60. } 61. } 上面这段程序我们可以分为两个部分,前半段(第1~25行代码)将数组中各人物目前的坐标进行排序贴图,后半部分(第26~62行代码)随机决定下次人物的移动方向和所在坐标。这样就可以制选出人物每时每刻都在随机移动的效果。 第13行代码精析:在贴图之前调用冒泡排序实现函数对贴图进行排序,这是我们这个程序的核心思想所在。 第26~61行代码精析:就是一个大的for循环套一个switch语句,switch中用一个rand函数随机决定方向,即用随机数除以4的余数来决定下次移动方向,余数0、1、2、3分别代表上、下、左、右。且在计算出新的贴图坐标之后,还需判断此新的坐标会不会使得人物贴图超出窗口边界,若超出,则将该方向上的坐标设定为刚好等于临界值。 接下来看一下程序运行截图: 这是30个精灵的情况,精灵们跑得可欢了。我们还可以改变SPRITE_NUMBER宏的值,来使画面上的精灵数量更多或者更少。设定SPRITE_NUMBER=300就成如下这样了,非常疯狂: 我们可以看到,即时画面上的人物数达到了300个之多,利用排序贴图来达到的精灵间彼此遮掩的效果还是很逼真的。友情提示,有密集恐惧症的孩子们千万不要把SPRITE_NUMBER的值设成上千了,那样满屏幕密密麻麻的都会是小精灵,虽然程序是我写的,但是参数是你自己要改成这么变态的,有什么后果我可不会管哦,哈哈。

展开全文


推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《逐梦旅程:Windows游戏编程之从零开始》其他试读目录

• 6.1 定时器动画显示
• 6.1.2 WM_TIMER消息响应
• 6.1.3 删除定时器
• 6.1.4 示例程序GDIdemo6
• 6.2 游戏循环动画显示
• 6.3 透明动画
• 6.4 排序贴图 [当前]
• 6.5 章节小憩
  • 大家都在看
  • 小编推荐
  • 猜你喜欢
  •