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

               

本系列文章由zhmxy555(毛星云)编写,转载请注明出处。

http://blog.csdn.net/zhmxy555/article/details/7607916

作者:毛星云    邮箱: [email protected]    期待着与志同道合的朋友们相互交流




本节在笔记二十二的基础上继续讲解了例子系统的模拟与实际运用,着重讲解和分析了基于例子系统的一个“星光绽放

demo”,最后盘点了史诗级游戏作品《暗黑破坏神3》上市首周所创下的传奇。

 

 

一.基础知识讲解

 

1.    概念与思路

 

基本的粒子系统概念在笔记二十二中已经讲过了,还不太清楚的朋友请移步前篇:


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

 

本节讲解的星光绽放demo相当于是一个模拟爆炸(或者说是烟花)特效的demo,浅墨认为这个特效拿出来讲解很多必要性,它可以为很多问题带来的思路的火花。这个demo之中,绽放(爆炸)点为在窗口中由随机数产生的一个位置,绽放(爆炸)后,会出现很多星光以不同的速度向四方飞散而去,当粒子飞出窗口后或者超出时间后便会消失。每一次爆炸所出现的粒子全部消失后,便会重新出现绽放(爆炸)的画面,以产生不断绽放星光的效果。

 


 

2.“星光”粒子的构造


首先我们来看一下这次如何用结构体来构造出星光粒子:

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

6个成员分别为,粒子坐标两个值,粒子方向两个值,持续时间lasted,和粒子是否存在的标识exist。



3.核心代码讲解


最重要的当然是我们的MyPaint()绘图函数:

//全局变量声明HINSTANCE hInst;HBITMAP bg,star,mask;  //用于贴图的三个HBITMAP变量HDC hdc,mdc,bufdc;HWND hWnd;RECT rect;int i,count; //定义count用于计数//****自定义绘图函数*********************************// 1.窗口贴图// 2.实现星光绽放的效果void MyPaint(HDC hdc){//创建粒子 if(count == 0)              //随机设置爆炸点 { int x=rand()%rect.right; int y=rand()%rect.bottom;  for(i=0;i<50;i++)       //产生星光粒子  {   flystar[i].x = x;   flystar[i].y = y;   flystar[i].lasted = 0//设定该粒子存在的时间为零   if(i%2==0)       //按粒子编号i来决定粒子在哪个象限运动,且x,y方向的移动速度随机为1—15之间的一个值,由1+rand()%15来完成。   {    flystar[i].vx =  -(1+rand()%15);    flystar[i].vy =  -(1+rand()%15);   }   if(i%2==1)   {    flystar[i].vx = 1+rand()%15;    flystar[i].vy = 1+rand()%15;   }   if(i%4==2)   {    flystar[i].vx = -(1+rand()%15);    flystar[i].vy = 1+rand()%15;   }   if(i%4==3)   {    flystar[i].vx = 1+rand()%15;    flystar[i].vy = -(1+rand()%15);   }   flystar[i].exist = true//设定粒子存在  }  count = 50;   //50个粒子由for循环设置完成后,我们将粒子数量设为50,代表目前有50颗星光 }  //先在内存dc中贴上背景图片 SelectObject(bufdc,bg); BitBlt(mdc,0,0,rect.right,rect.bottom,bufdc,0,0,SRCCOPY); for(i=0;i<50;i++) {  if(flystar[i].exist)   //判断粒子是否还存在,若存在,则根据其坐标(flystar[i].x,flystar[i].y)进行贴图操作  {   SelectObject(bufdc,mask);   BitBlt(mdc,flystar[i].x,flystar[i].y,30,30,bufdc,0,0,SRCAND);   SelectObject(bufdc,star);   BitBlt(mdc,flystar[i].x,flystar[i].y,30,30,bufdc,0,0,SRCPAINT);   //计算下一次贴图的坐标   flystar[i].x+=flystar[i].vx;   flystar[i].y+=flystar[i].vy;   //在每进行一次贴图后,将粒子的存在时间累加1.   flystar[i].lasted++;   //进行条件判断,若某粒子跑出窗口区域一定的范围,则将该粒子设为不存在,且粒子数随之递减   if(flystar[i].x<=-10 || flystar[i].x>rect.right || flystar[i].y<=-10 || flystar[i].y>rect.bottom || flystar[i].lasted>50)   {    flystar[i].exist = false//删除星光粒子     count--;                    //递减星光总数   }  } } //将mdc中的全部内容贴到hdc中 BitBlt(hdc,0,0,640,480,mdc,0,0,SRCCOPY);}



相关的书写思路在代码注释中浅墨已经写得比较清晰了。

这段代码的书写整体思路即:

第一步,判断粒子是否创建,若星光数量count不为0,则直接跳到第四步进行相关贴图操作。否则需按每步顺序完成粒子的初始化。

第二步,随机设置绽放点。

第三步,创建各个粒子(为结构体各属性赋值)。

第四步,在内存dc上贴上背景图片。

第五步,对各个粒子进行贴图操作并

第六步,对某些值,如count,exist进行特殊的处理

第七步,将mdc(内存dc)中的内容贴到hdc中,完成最后在屏幕上的显示。





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


OK,讲解完成,现在我们就贴出详细注释的源代码:



#include "stdafx.h"#include <stdio.h>//全局变量声明HINSTANCE hInst;HBITMAP bg,star,mask;  //用于贴图的三个HBITMAP变量HDC hdc,mdc,bufdc;HWND hWnd;RECT rect;int i,count; //定义count用于计数struct flystar{ int x;       //星光所在的x坐标 int y;       //星光所在的y坐标 int vx;      //星光x方向的速度 int vy;      //星光y方向的速度 int lasted;  //星光存在的时间 BOOL exist;  //星光是否存在}flystar[50];//全局函数声明ATOM    MyRegisterClass(HINSTANCE hInstance);BOOL    InitInstance(HINSTANCE, int);LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);void    MyPaint(HDC hdc);//****WinMain函数,程序入口点函数************************************** int APIENTRY WinMain(HINSTANCE hInstance,                     HINSTANCE hPrevInstance,                     LPSTR     lpCmdLine,                     int       nCmdShow){ MSG msg; MyRegisterClass(hInstance); //初始化 if (!InitInstance (hInstance, nCmdShow))  {  return FALSE; }       //消息循环      while (GetMessage(&msg, NULL, 0, 0))       {          TranslateMessage(&msg);          DispatchMessage(&msg);      }   return msg.wParam;}//****设计一个窗口类,类似填空题,使用窗口结构体********************* ATOM MyRegisterClass(HINSTANCE hInstance){ WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX);  wcex.style   = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra  = 0; wcex.cbWndExtra  = 0; wcex.hInstance  = hInstance; wcex.hIcon   = NULL; wcex.hCursor  = NULL; wcex.hCursor  = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = "maple"; wcex.hIconSm  = NULLreturn RegisterClassEx(&wcex);}//****初始化函数*************************************  // 1.加载位图资源// 2.取得内部窗口区域信息  BOOL InitInstance(HINSTANCE hInstance, int nCmdShow){ HBITMAP bmp; hInst = hInstance; hWnd = CreateWindow("maple", "浅墨的绘图窗口" , WS_OVERLAPPEDWINDOW,  CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) {  return FALSE; } MoveWindow(hWnd,10,10,600,450,true); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); hdc = GetDC(hWnd); mdc = CreateCompatibleDC(hdc); bufdc = CreateCompatibleDC(hdc); bmp = CreateCompatibleBitmap(hdc,640,480); SelectObject(mdc,bmp);  bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,rect.right,rect.bottom,LR_LOADFROMFILE);  star = (HBITMAP)LoadImage(NULL,"star.bmp",IMAGE_BITMAP,30,30,LR_LOADFROMFILE);  mask = (HBITMAP)LoadImage(NULL,"mask.bmp",IMAGE_BITMAP,30,30,LR_LOADFROMFILE);  GetClientRect(hWnd,&rect);  SetTimer(hWnd,1,0,NULL); MyPaint(hdc); return TRUE;}//****自定义绘图函数*********************************// 1.窗口贴图// 2.实现星光绽放的效果void MyPaint(HDC hdc){//创建粒子 if(count == 0)              //随机设置爆炸点 { int x=rand()%rect.right; int y=rand()%rect.bottom;  for(i=0;i<50;i++)       //产生星光粒子  {   flystar[i].x = x;   flystar[i].y = y;   flystar[i].lasted = 0//设定该粒子存在的时间为零   if(i%2==0)       //按粒子编号i来决定粒子在哪个象限运动,且x,y方向的移动速度随机为1—15之间的一个值,由1+rand()%15来完成。   {    flystar[i].vx =  -(1+rand()%15);    flystar[i].vy =  -(1+rand()%15);   }   if(i%2==1)   {    flystar[i].vx = 1+rand()%15;    flystar[i].vy = 1+rand()%15;   }   if(i%4==2)   {    flystar[i].vx = -(1+rand()%15);    flystar[i].vy = 1+rand()%15;   }   if(i%4==3)   {    flystar[i].vx = 1+rand()%15;    flystar[i].vy = -(1+rand()%15);   }   flystar[i].exist = true//设定粒子存在  }  count = 50;   //50个粒子由for循环设置完成后,我们将粒子数量设为50,代表目前有50颗星光 }  //先在内存dc中贴上背景图片 SelectObject(bufdc,bg); BitBlt(mdc,0,0,rect.right,rect.bottom,bufdc,0,0,SRCCOPY); for(i=0;i<50;i++) {  if(flystar[i].exist)   //判断粒子是否还存在,若存在,则根据其坐标(flystar[i].x,flystar[i].y)进行贴图操作  {   SelectObject(bufdc,mask);   BitBlt(mdc,flystar[i].x,flystar[i].y,30,30,bufdc,0,0,SRCAND);   SelectObject(bufdc,star);   BitBlt(mdc,flystar[i].x,flystar[i].y,30,30,bufdc,0,0,SRCPAINT);   //计算下一次贴图的坐标   flystar[i].x+=flystar[i].vx;   flystar[i].y+=flystar[i].vy;   //在每进行一次贴图后,将粒子的存在时间累加1.   flystar[i].lasted++;   //进行条件判断,若某粒子跑出窗口区域一定的范围,则将该粒子设为不存在,且粒子数随之递减   if(flystar[i].x<=-10 || flystar[i].x>rect.right || flystar[i].y<=-10 || flystar[i].y>rect.bottom || flystar[i].lasted>50)   {    flystar[i].exist = false//删除星光粒子     count--;                    //递减星光总数   }  } } //将mdc中的全部内容贴到hdc中 BitBlt(hdc,0,0,640,480,mdc,0,0,SRCCOPY);}//****消息处理函数***********************************LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)switch (message) {  case WM_TIMER:                      //时间消息     MyPaint(hdc);                   //在消息循环中加入处理WM_TIMER消息,当接收到此消息时便调用MyPaint()函数进行窗口绘图              break;    case WM_KEYDOWN:      //按键消息     if(wParam==VK_ESCAPE)    //按下【Esc】键    PostQuitMessage(0);   break;  case WM_DESTROY:      //窗口结束消息    DeleteDC(mdc);   DeleteDC(bufdc);   DeleteObject(bg);   DeleteObject(star);   DeleteObject(mask);   KillTimer(hWnd,1);             //窗口结束时,删除所建立的定时器          ReleaseDC(hWnd,hdc);   PostQuitMessage(0);   break;  default:       //其他消息   return DefWindowProc(hWnd, message, wParam, lParam);   }   return 0;}


这个“星光绽放”demo运行的截图如下:













背景图片是盛大旗下网络游戏《龙之谷》的游戏原画。


这节的代码可以与之前讲解的重力系统,摩擦力系统相结合,创造出更为逼真的爆炸特效,有兴趣的朋友可以尝试一下,做出真实的受重力影响的烟花盛放的效果出来。





 三、史诗级游戏作品《暗黑破坏神3》上市首周所创下的传奇


在文章末尾,我们来盘点一下暴雪大作《暗黑破坏神3》不俗的销售成绩。



5月15日《暗黑破坏神3》发售至今已经过了一周多,全球暗黑粉丝在激情中度过了美妙的一个星期。

《暗黑破坏神3》发布以来,受到了广大玩家的热情追捧。暴雪公布的数据显示,《暗黑破坏神3》开服24小时内共卖出了350万份拷贝。加上120万的魔兽世界年费用户,游戏发售当天共有470万玩家通过战网进入到庇护所世界。游戏发布第一周,游戏总销量为630万份(不包含WOW年费赠送和韩国数据)。  




这份数据刷新了PC游戏史最高数据,并且大大超越去年暴雪另一款热卖游戏《星际争霸2》所创下的销售记录。《暗黑3》有望在一个月内突破《星际争霸》14年来1100万份的累计销量。





这是电子游戏界上的一个传奇!

暴雪花十几年时间交出的这份答卷可谓非常的出色~~!!


还是那句话,暴雪出品,必属精品。浅墨觉得,采用精品战略的公司一般都会有不俗的发展,比如说苹果,比如说暴雪,它们都是依靠着几款精雕细琢出来的精品,统治着他们各自所在的领域。

对于期待了8年的《暗黑破坏神3》,浅墨在5月15号“大菠萝3”发售之后的第一时间就购买了CD-KEY,但买过来之后一直比较忙,没有第一时间开始体验,也就是近几天才开始玩的。是在台服玩的一个“狩魔猎人”,每天也就花一个小时左右,浅墨打算慢慢玩,沿途看下风景,期待了这么多年的作品,如果很快就打通了就枉费了这么多年的期待了,是吧。(浅墨记得有一个在15号“大菠萝3”刚发售,开服8小时之后候就满级60级的家伙……)所以浅墨的升级速度不算快,目前才22级,不过身上目前基本上一身稀有装备了,敏捷堆了200+了,对于一个22级的狩魔猎人,已经很高了吧。我的BattleTag是浅墨#3762,也在台服玩的朋友可以加我好友一起玩哈。

同时在这里浅墨也提醒大家,“大菠萝3”虽然经典,但是一定要适度游戏,千万不要影响学习和生活。

前几天就爆出了美国一位32岁的男子连续玩了3天3夜的《暗黑破坏神3》后猝死,与世长辞的新闻,可真让人惋惜。

 

 

好了,本节笔记就到这里吧,关于源码的提供,浅墨越来越觉得没有提供两个版本的必要了,但是一定会满足不同IDE的朋友都可以把源码运行起来。下面是通用版的源码:


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

 

(VC6.0直接可以打开工程,VS2005,VS2008,VS2010,VS2011等版本的朋友可以双击其中的工程文件进行版本转化,然后即可打开工程)




感谢一直支持【Visual C++】游戏开发笔记系列专栏的朋友们。

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

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

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

 

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

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

 

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

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

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

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

                                               

 

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

------------------------------------------------------------------------------------------------------------------------------

浅墨历时一年为游戏编程爱好者锻造的入门宝典《逐梦旅程:Windows游戏编程之从零开始》

如果你喜欢浅墨写的【Visual C++】游戏开发系列博客文章,那么你一定会爱上这本书。

这是浅墨专门为热爱游戏编程的朋友们写的入门级游戏编程宝典。



------------------------------------------------------------------------------------------------------------------------------





           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

猜你喜欢

转载自blog.csdn.net/yttyffggh/article/details/87715754
今日推荐