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

本节内容主要讲解了在GDI中粒子的运用,为后续DirectX中粒子系统的讲解提供一个初步的认识。


 



一.基础知识讲解



1.基本概念


粒子是一种微小的物体,在数学上通常用点来表示其模型。我们可以把粒子想象成颗粒状的物体,如雪花,雨滴,沙尘,烟雾

等特殊的事物。又比如游戏中的怪物,晶体,材料,在需要的时候,也可以通过粒子来实现。俗话说“不积跬步,无以至千里,

不积小流,何以成江海”,单个的粒子是比较平凡的存在,但是如果将大量的粒子聚到一起,就可以实现很多神奇的效果了。

 

在C/C++中想要定义一个粒子是非常容易的。基本功扎实的朋友们肯定马上就可以想到,“结构体“是用来定义粒子类型的绝

佳武器。原则上用“类”也可以实现,但是在这里采用“结构体”将更加合适。



 

2.实现方法


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


  1. struct snow  
  2. {  
  3.        int x;        //雪花的 X坐标  
  4.        int y;        //雪花的 Y坐标  
  5.        BOOL exist; //雪花是否存在  
  6. };  



可以看出,上述结构体中有3个成员,分别是代表X坐标的x,代表Y坐标的y,与表示雪花是否存在的布尔型变量exist。


定义完粒子的结构体后,便可以实例化一个粒子数组了。

 



如果我们需要一个大小为50的snowfly数组,则可用一下两种方法来进行:


<1>在结构体的尾部加上我们需要实例化的对象


  1. struct snow  
  2. {  
  3.        int x;        //雪花的 X坐标  
  4.        int y;        //雪花的 Y坐标  
  5.        BOOL exist; //雪花是否存在  
  6. }snowfly[50];  


 

<2>单独定义


  1. snow snowfly[50];  


 

定义完之后,就可以在这个粒子数组的基础上,用代码进行相关功能的实现了。

以上就是粒子系统概念的一个简明扼要的讲解。而下面我们依旧是通过一个实例来巩固本节所学。

 

 




二、详细注释的源代码欣赏


 

在贴出全部的源代码之前,我们先把最关键的部分提出来先剖析一下,下面是本节实例的核心代码:

 

  1. //全局变量声明  
  2. HINSTANCE hInst;  
  3. HBITMAP bg,snow,mask;  //用于贴图的三个HBITMAP变量  
  4. HDC hdc,mdc,bufdc;  
  5. HWND    hWnd;  
  6. RECT    rect;  
  7. int i,count; //定义count用于计数  
  8.   
  9. //****自定义绘图函数*********************************  
  10. // 1.窗口贴图  
  11. // 2.实现雪花纷飞的效果  
  12. void MyPaint(HDC hdc)  
  13. {  
  14.   
  15. //创建粒子  
  16.     if(count<50)  //当粒子数小于50时,产生新的粒子,设定每个粒子的属性值  
  17.     {  
  18.         drop[count].x = rand()%rect.right; //将粒子的X坐标设为窗口中水平方向上的任意位置  
  19.         drop[count].y = 0;    //将每个粒子的Y坐标都设为"0",即从窗口上沿往下落  
  20.         drop[count].exist = true//设定粒子存在  
  21.         count++;   //每产生一个粒子后进行累加计数  
  22.     }  
  23.   
  24.   
  25. //贴上背景图到mdc中  
  26.     SelectObject(bufdc,bg);  
  27.     BitBlt(mdc,0,0,640,480,bufdc,0,0,SRCCOPY);  
  28.   
  29. //首先判断粒子是否存在,若存在,进行透明贴图操作  
  30.     for(i=0;i<50;i++)  
  31.     {  
  32.           
  33.         Sleep(1);  
  34.         if(drop[i].exist)  
  35.         {  
  36.             SelectObject(bufdc,mask);  
  37.             BitBlt(mdc,drop[i].x,drop[i].y,20,20,bufdc,0,0,SRCAND);  
  38.   
  39.             SelectObject(bufdc,snow);  
  40.             BitBlt(mdc,drop[i].x,drop[i].y,20,20,bufdc,0,0,SRCPAINT);  
  41.             if(rand()%2==0)  
  42.                 drop[i].x+=5;  
  43.             else   
  44.                 drop[i].x-=5;  
  45.             drop[i].y+=10;  
  46.             if(drop[i].y > rect.bottom)  
  47.             {  
  48.                 drop[i].x = rand()%rect.right;  
  49.                 drop[i].y = 0;  
  50.             }  
  51.         }  
  52.       
  53.     }  
  54.       
  55.   
  56. //将mdc中的全部内容贴到hdc中  
  57.     BitBlt(hdc,0,0,640,480,mdc,0,0,SRCCOPY);  
  58.   
  59. }  



MyPaint函数的书写思路是,先初始化每个粒子,这里是共50个粒子。然后贴上背景图到mdc中,再用循环将各个粒子也贴

到mdc中,循环完成之后,再统一将mdc中的内容直接贴到hdc中。这样做的优点是比较直观,提高了贴图的效率。

 

 




下面就贴出全部详细注释的源代码,供大家学习,需要在自己机器上运行并学习提高的朋友,请点击文章末尾处贴出的地址进

行下载。源代码依旧是分为VC6.0和VS2010两个版本。这里贴出的是VC6.0版的:


  1. #include "stdafx.h"  
  2. #include <stdio.h>  
  3.   
  4. //全局变量声明  
  5. HINSTANCE hInst;  
  6. HBITMAP bg,snow,mask;  //用于贴图的三个HBITMAP变量  
  7. HDC hdc,mdc,bufdc;  
  8. HWND    hWnd;  
  9. RECT    rect;  
  10. int i,count; //定义count用于计数  
  11.   
  12.   
  13.   
  14.   
  15. struct snow  
  16. {  
  17.     int x;  
  18.     int y;  
  19.     BOOL exist;  
  20. }drop[50];  
  21.   
  22.   
  23. //全局函数声明  
  24. ATOM                MyRegisterClass(HINSTANCE hInstance);  
  25. BOOL                InitInstance(HINSTANCEint);  
  26. LRESULT CALLBACK    WndProc(HWNDUINTWPARAMLPARAM);  
  27. void                MyPaint(HDC hdc);  
  28.   
  29. //****WinMain函数,程序入口点函数**************************************   
  30. int APIENTRY WinMain(HINSTANCE hInstance,  
  31.                      HINSTANCE hPrevInstance,  
  32.                      LPSTR     lpCmdLine,  
  33.                      int       nCmdShow)  
  34. {  
  35.     MSG msg;  
  36.   
  37.     MyRegisterClass(hInstance);  
  38.   
  39.     //初始化  
  40.     if (!InitInstance (hInstance, nCmdShow))   
  41.     {  
  42.         return FALSE;  
  43.     }  
  44.   
  45.            
  46.     //消息循环    
  47.     while (GetMessage(&msg, NULL, 0, 0))     
  48.     {    
  49.         TranslateMessage(&msg);    
  50.         DispatchMessage(&msg);    
  51.     }    
  52.   
  53.     return msg.wParam;  
  54. }  
  55.   
  56. //****设计一个窗口类,类似填空题,使用窗口结构体*********************   
  57. ATOM MyRegisterClass(HINSTANCE hInstance)  
  58. {  
  59.     WNDCLASSEX wcex;  
  60.   
  61.     wcex.cbSize = sizeof(WNDCLASSEX);   
  62.     wcex.style          = CS_HREDRAW | CS_VREDRAW;  
  63.     wcex.lpfnWndProc    = (WNDPROC)WndProc;  
  64.     wcex.cbClsExtra     = 0;  
  65.     wcex.cbWndExtra     = 0;  
  66.     wcex.hInstance      = hInstance;  
  67.     wcex.hIcon          = NULL;  
  68.     wcex.hCursor        = NULL;  
  69.     wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);  
  70.     wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);  
  71.     wcex.lpszMenuName   = NULL;  
  72.     wcex.lpszClassName  = "maple";  
  73.     wcex.hIconSm        = NULL;  
  74.   
  75.     return RegisterClassEx(&wcex);  
  76. }  
  77.   
  78. //****初始化函数*************************************    
  79. // 1.加载位图资源  
  80. // 2.取得内部窗口区域信息    
  81. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)  
  82. {  
  83.     HBITMAP bmp;  
  84.     hInst = hInstance;  
  85.   
  86.     hWnd = CreateWindow("maple""浅墨的绘图窗口" , WS_OVERLAPPEDWINDOW,  
  87.         CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);  
  88.   
  89.     if (!hWnd)  
  90.     {  
  91.         return FALSE;  
  92.     }  
  93.   
  94.     MoveWindow(hWnd,10,10,600,450,true);  
  95.     ShowWindow(hWnd, nCmdShow);  
  96.     UpdateWindow(hWnd);  
  97.   
  98.     hdc = GetDC(hWnd);  
  99.     mdc = CreateCompatibleDC(hdc);  
  100.   
  101.     bufdc = CreateCompatibleDC(hdc);  
  102.     bmp = CreateCompatibleBitmap(hdc,640,480);  
  103.   
  104.     SelectObject(mdc,bmp);  
  105.   
  106.   
  107.       
  108.   
  109.     bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,rect.right,rect.bottom,LR_LOADFROMFILE);   
  110.     snow = (HBITMAP)LoadImage(NULL,"snow.bmp",IMAGE_BITMAP,20,20,LR_LOADFROMFILE);   
  111.     mask = (HBITMAP)LoadImage(NULL,"mask.bmp",IMAGE_BITMAP,20,20,LR_LOADFROMFILE);   
  112.     GetClientRect(hWnd,&rect);  
  113.   
  114.   
  115.       
  116.     SetTimer(hWnd,1,0,NULL);  
  117.   
  118.     MyPaint(hdc);  
  119.   
  120.     return TRUE;  
  121. }  
  122.   
  123. //****自定义绘图函数*********************************  
  124. // 1.窗口贴图  
  125. // 2.实现雪花纷飞的效果  
  126. void MyPaint(HDC hdc)  
  127. {  
  128.   
  129. //创建粒子  
  130.     if(count<50)  //当粒子数小于50时,产生新的粒子,设定每个粒子的属性值  
  131.     {  
  132.         drop[count].x = rand()%rect.right; //将粒子的X坐标设为窗口中水平方向上的任意位置  
  133.         drop[count].y = 0;    //将每个粒子的Y坐标都设为"0",即从窗口上沿往下落  
  134.         drop[count].exist = true//设定粒子存在  
  135.         count++;   //每产生一个粒子后进行累加计数  
  136.     }  
  137.   
  138.   
  139. //贴上背景图到mdc中  
  140.     SelectObject(bufdc,bg);  
  141.     BitBlt(mdc,0,0,640,480,bufdc,0,0,SRCCOPY);  
  142.   
  143. //首先判断粒子是否存在,若存在,进行透明贴图操作  
  144.     for(i=0;i<50;i++)  
  145.     {  
  146.           
  147.         Sleep(1);  
  148.         if(drop[i].exist)  
  149.         {  
  150.             SelectObject(bufdc,mask);  
  151.             BitBlt(mdc,drop[i].x,drop[i].y,20,20,bufdc,0,0,SRCAND);  
  152.   
  153.             SelectObject(bufdc,snow);  
  154.             BitBlt(mdc,drop[i].x,drop[i].y,20,20,bufdc,0,0,SRCPAINT);  
  155.             if(rand()%2==0)  
  156.                 drop[i].x+=5;  
  157.             else   
  158.                 drop[i].x-=5;  
  159.             drop[i].y+=10;  
  160.             if(drop[i].y > rect.bottom)  
  161.             {  
  162.                 drop[i].x = rand()%rect.right;  
  163.                 drop[i].y = 0;  
  164.             }  
  165.         }  
  166.       
  167.     }  
  168.       
  169.   
  170. //将mdc中的全部内容贴到hdc中  
  171.     BitBlt(hdc,0,0,640,480,mdc,0,0,SRCCOPY);  
  172.   
  173. }  
  174.   
  175.   
  176.   
  177. //****消息处理函数***********************************  
  178. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
  179. {  
  180.     switch (message)  
  181.     {  
  182.         case WM_TIMER:                      //时间消息    
  183.             MyPaint(hdc);                   //在消息循环中加入处理WM_TIMER消息,当接收到此消息时便调用MyPaint()函数进行窗口绘图    
  184.             break;    
  185.         case WM_KEYDOWN:                     //按键消息    
  186.             if(wParam==VK_ESCAPE)            //按下【Esc】键  
  187.                 PostQuitMessage(0);  
  188.             break;  
  189.         case WM_DESTROY:                     //窗口结束消息   
  190.             DeleteDC(mdc);  
  191.             DeleteDC(bufdc);  
  192.             DeleteObject(bg);  
  193.             DeleteObject(snow);  
  194.             DeleteObject(mask);  
  195.             KillTimer(hWnd,1);             //窗口结束时,删除所建立的定时器         
  196.             ReleaseDC(hWnd,hdc);  
  197.             PostQuitMessage(0);  
  198.             break;  
  199.         default:                            //其他消息  
  200.             return DefWindowProc(hWnd, message, wParam, lParam);  
  201.    }  
  202.    return 0;  
  203. }  




下面是运行后的截图效果:



 



 

可以看到窗口中有漫天飞舞的雪花,我们可以调节数组大小,及几处设定的数值的大小,来使雪花来得更猛烈些。


这张背景图是否有些熟悉呢?哈哈,喜欢打Dota的朋友们应该可以发现,这张图就是Dota的游戏原画(或者说是魔兽争霸Ⅲ

冰封王座的原画,因为Dota其实就是基于这款游戏的一张自定义多人对战地图罢了),左边的显然就是恐怖利刃TerroBlade

(咦,他的双刀呢?),中间远远在背后摆造型的是召唤师卡尔,而最右边的当然就是目前Dota中的“一姐”蛇发女妖美杜莎

了。

 

 



在文章末尾说点题外话吧。

关于大家提到的浅墨总是熬夜的问题,其实浅墨从来都不熬夜的- -。因为浅墨目前是在欧洲,经常是在当地时间晚上10点左右

把最新的文章发表出来,所以大家在中国看到的都是半夜3点4点左右发表的文章,所以不要以为浅墨是熬夜码字啦,那是因为

时差问题~~ -o-

 



好了,本篇就写到这里吧,谢谢大家的观赏,下面依旧是放出两个版本的源代码供大家下载学习)(当然,必须是零资源分下载^^):

 

本篇文章VS2010版的配套源码请点击这里下载:【VS2010版】【Visual C++】

Code_Note_22

 

 

本篇文章VC6.0版的配套源码请点击这里下载:  【VC6.0版】【Visual C++】

Code_Note_22

 

 

 

 

 

感谢一直支持【Visual C++】游戏开发笔记系列专栏的朋友们,也恳请大家继续关注我的专栏。

目前在讲的GDI只是前奏。DirectX 11会在GDI梳理完后进行深入讲解,敬请期待~~

【Visual C++】游戏开发 系列文章才刚刚展开一点而已,因为游戏世界实在是太博大精深了~

但我们不能着急,得慢慢打好基础。做学问最忌好高骛远,不是吗?

 

浅墨希望看到大家的留言,希望与大家共同交流,希望得到睿智的评论(即使是批评)。

你们的支持是我写下去的动力~

 

精通游戏开发的路还很长很长,非常希望能和大家一起交流,共同学习,共同进步。

大家看过后觉得值得一看的话,可以顶一下这篇文章,你们的支持是我继续写下去的动力~

如果文章中有什么疏漏的地方,也请大家指正。也希望大家可以多留言来和我探讨相关的问题。

最后,谢谢你们一直的支持~~~

                                               

 

                                                 ——————————浅墨于2012年5月20日

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值