游戏基础物理建模-粒子系统模拟

一.基础知识讲解

 

 

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中的“一姐”蛇发女妖美杜莎

了。

 

 

 

 

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

由于这段时间事务繁杂,有接近一个多星期没更新博文了,在这里必须给一直期待着更新的朋友们说声抱歉,也需要给自己说

声抱歉,因为写博文不仅能分享知识和经验,也是整理自己思维的一个很好的途径。

浅墨以后会尽量做到一星期一更新,每周的新文章大概会在周一发表出来。为了保证文章的质量,浅墨觉得更新的节奏需要好

好把握,而且毕竟【Visual C++游戏开发】系列专栏的每一篇文章都基本上都是配有源码的(况且现在浅墨每次提供的都是

VC6.0和VS2010两个版本的源码-o-),每篇文章的配套源码也需要时间来写呢,可不能像小说一样每星期发表那么多章节,

况且浅墨平常事情很多,也没这精力呢~

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

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

时差问题~~ -o-

 

 

 

还有那些需要学习资料的朋友,留下邮箱后(最好在最近更新的或者【Visual C++】游戏开发笔记系列的第一篇文章 【Visual C++】游戏开发笔记之一——API函数、DirectX的关键系统 留下邮箱,方便浅墨及时看到你的留言),浅墨会在该周的周末做统一发送(不出特殊情况就是周末晚上发送,算上欧洲和国内5小时的时差,大家大概周一早

上既可以收到),由于附件的保质期是30天,希望收到邮件的朋友们及时下载。

 

posted @ 2012-05-27 22:58  hibernate3例子  阅读(424)  评论(0编辑  收藏  举报