VC++游戏开发随记之二十二.docx
《VC++游戏开发随记之二十二.docx》由会员分享,可在线阅读,更多相关《VC++游戏开发随记之二十二.docx(16页珍藏版)》请在冰点文库上搜索。
VC++游戏开发随记之二十二
【VisualC++】游戏开发笔记二十二游戏基础物理建模(四)粒子系统模拟
(一)
本节内容主要讲解了在GDI中粒子的运用,为后续DirectX中粒子系统的讲解提供一个初步的认识。
一.基础知识讲解
1.基本概念
粒子是一种微小的物体,在数学上通常用点来表示其模型。
我们可以把粒子想象成颗粒状的物体,如雪花,雨滴,沙尘,烟雾
等特殊的事物。
又比如游戏中的怪物,晶体,材料,在需要的时候,也可以通过粒子来实现。
俗话说“不积跬步,无以至千里,
不积小流,何以成江海”,单个的粒子是比较平凡的存在,但是如果将大量的粒子聚到一起,就可以实现很多神奇的效果了。
在C/C++中想要定义一个粒子是非常容易的。
基本功扎实的朋友们肯定马上就可以想到,“结构体“是用来定义粒子类型的绝
佳武器。
原则上用“类”也可以实现,但是在这里采用“结构体”将更加合适。
2.实现方法
如下面的这个结构体snow便是用来定义“雪花”粒子的:
[cpp] viewplaincopyprint?
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>在结构体的尾部加上我们需要实例化的对象
[cpp] viewplaincopyprint?
1.struct snow
2.{
3. int x; //雪花的 X坐标
4. int y; //雪花的 Y坐标
5. BOOL exist; //雪花是否存在
6.}snowfly[50];
<2>单独定义
[cpp] viewplaincopyprint?
1.snow snowfly[50];
定义完之后,就可以在这个粒子数组的基础上,用代码进行相关功能的实现了。
以上就是粒子系统概念的一个简明扼要的讲解。
而下面我们依旧是通过一个实例来巩固本节所学。
二、详细注释的源代码欣赏
在贴出全部的源代码之前,我们先把最关键的部分提出来先剖析一下,下面是本节实例的核心代码:
[cpp] viewplaincopyprint?
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版的:
[cpp] viewplaincopyprint?
1.#include "stdafx.h"
2.#include
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(HINSTANCE, int);
26.LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
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-
好了,本篇就写到这里吧,谢谢大家的观赏,下面依旧是放出两个版本的源代码供大家下载学习)(当然,必须是零资