粒子算法:烟花、喷泉、爆炸

参考:https://www.codeproject.com/articles/10003/a-basic-particles-system

如果需要实现一个喷泉粒子系统:

1)需要先定义每个粒子元素Particle类:它应包含有运动方向和速度,初始化位置(及运动一个生命值后的位置),生命值,粒子渲染的颜色,及一个生命值增加一个单位后对应的粒子属性变化。

Vector.cs--存储粒子位置信息

  1 /// <summary>
  2     /// 向量
  3     /// </summary>
  4     public class Vector
  5     {
  6         #region Private members
  7         /// <summary>
  8         /// X Coorination of the vector
  9         /// </summary>
 10         private float m_Xcoord;
 11         /// <summary>
 12         /// Y Coorination of the vector
 13         /// </summary>
 14         private float m_Ycoord;
 15         /// <summary>
 16         /// Z Coorination of the vector
 17         /// </summary>
 18         private float m_Zcoord;
 19         #endregion
 20 
 21         #region Constructors
 22         /// <summary>
 23         /// Default constructor. Initiate vector at the (0,0,0) location
 24         /// </summary>
 25         public Vector()
 26         { }
 27 
 28         /// <summary>
 29         /// Initiate vector with given parameters
 30         /// </summary>
 31         /// <param name="inX">X coordination of vector</param>
 32         /// <param name="inY">Y coordination of vector</param>
 33         /// <param name="inZ">Z coordination of vector</param>
 34         public Vector(float inX, float inY, float inZ)
 35         {
 36             m_Xcoord = inX;
 37             m_Ycoord = inY;
 38             m_Zcoord = inZ;
 39         }
 40 
 41         /// <summary>
 42         /// Initiate vector with given parameters
 43         /// </summary>
 44         /// <param name="coordination">Vector's coordinations as an array</param>
 45         public Vector(float[] coordination)
 46         {
 47             m_Xcoord = coordination[0];
 48             m_Ycoord = coordination[1];
 49             m_Zcoord = coordination[2];
 50         }
 51 
 52         /// <summary>
 53         /// Initiate vector with same values as given Vector
 54         /// </summary>
 55         /// <param name="v">Vector to copy coordinations</param>
 56         public Vector(Vector vector)
 57         {
 58             m_Xcoord = vector.X;
 59             m_Ycoord = vector.Y;
 60             m_Zcoord = vector.Z;
 61         }
 62         #endregion
 63 
 64         #region Public properties
 65         /// <summary>
 66         /// X Coordination of vector
 67         /// </summary>
 68         public float X
 69         {
 70             get { return m_Xcoord; }
 71             set { m_Xcoord = value; }
 72         }
 73         /// <summary>
 74         /// Y Coordination of vector
 75         /// </summary>
 76         public float Y
 77         {
 78             get { return m_Ycoord; }
 79             set { m_Ycoord = value; }
 80         }
 81         /// <summary>
 82         /// Z Coordination of vector
 83         /// </summary>
 84         public float Z
 85         {
 86             get { return m_Zcoord; }
 87             set { m_Zcoord = value; }
 88         }
 89         #endregion
 90 
 91         #region Methods
 92 
 93         /// <summary>
 94         /// Add 2 vectors and create a new one.
 95         /// </summary>
 96         /// <param name="vector1">First vector</param>
 97         /// <param name="vector2">Second vector</param>
 98         /// <returns>New vector that is the sum of the 2 vectors</returns>
 99         public static Vector Add(Vector vector1, Vector vector2)
100         {
101             if (((Object)vector1 == null) || ((Object)vector2 == null))
102                 return null;
103             return new Vector(vector1.X + vector2.X, vector1.Y + vector2.Y, vector1.Z + vector2.Z);
104         }
105         /// <summary>
106         /// Substract 2 vectors and create a new one.
107         /// </summary>
108         /// <param name="vector1">First vector</param>
109         /// <param name="vector2">Second vector</param>
110         /// <returns>New vector that is the difference of the 2 vectors</returns>
111         public static Vector Subtract(Vector vector1, Vector vector2)
112         {
113             if (((Object)vector1 == null) || ((Object)vector2 == null))
114                 return null;
115             return new Vector(vector1.X - vector2.X, vector1.Y - vector2.Y, vector1.Z - vector2.Z);
116         }
117         /// <summary>
118         /// Return a new vector with negative values.
119         /// </summary>
120         /// <param name="v">Original vector</param>
121         /// <returns>New vector that is the inversion of the original vector</returns>
122         public static Vector Neg(Vector vector)
123         {
124             if ((Object)vector == null)
125                 return null;
126             return new Vector(-vector.X, -vector.Y, -vector.Z);
127         }
128         /// <summary>
129         /// Multiply a vector with a scalar
130         /// </summary>
131         /// <param name="vector">Vector to be multiplied</param>
132         /// <param name="val">Scalar to multiply vector</param>
133         /// <returns>New vector that is the multiplication of the vector with the scalar</returns>
134         public static Vector Multiply(Vector vector, float val)
135         {
136             if ((Object)vector == null)
137                 return null;
138             return new Vector(vector.X * val, vector.Y * val, vector.Z * val);
139         }
140         #endregion
141 
142         #region Operators
143 
144         /// <summary>
145         /// Check equality of two vectors
146         /// </summary>
147         /// <param name="vector1">First vector</param>
148         /// <param name="vector2">Second vector</param>
149         /// <returns>True - if he 2 vectors are equal.
150         /// False - otherwise</returns>
151         public static bool operator ==(Vector vector1, Vector vector2)
152         {
153             if (((Object)vector1 == null) || ((Object)vector2 == null))
154                 return false;
155             return ((vector1.X.Equals(vector2.X))
156                 && (vector1.Y.Equals(vector2.Y))
157                 && (vector1.Z.Equals(vector2.Z)));
158         }
159 
160         /// <summary>
161         /// Check inequality of two vectors
162         /// </summary>
163         /// <param name="vector1">First vector</param>
164         /// <param name="vector2">Second vector</param>
165         /// <returns>True - if he 2 vectors are not equal.
166         /// False - otherwise</returns>
167         public static bool operator !=(Vector vector1, Vector vector2)
168         {
169             if (((Object)vector1 == null) || ((Object)vector2 == null))
170                 return false;
171             return ((!vector1.X.Equals(vector2.X))
172                 && (!vector1.Y.Equals(vector2.Y))
173                 && (!vector1.Z.Equals(vector2.Z)));
174         }
175 
176         /// <summary>
177         /// Calculate the sum of 2 vectors.
178         /// </summary>
179         /// <param name="vector1">First vector</param>
180         /// <param name="vector2">Second vector</param>
181         /// <returns>New vector that is the sum of the 2 vectors</returns>
182         public static Vector operator +(Vector vector1, Vector vector2)
183         {
184             if (((Object)vector1 == null) || ((Object)vector2 == null))
185                 return null;
186             return Vector.Add(vector1, vector2);
187         }
188         /// <summary>
189         /// Calculate the substraction of 2 vectors
190         /// </summary>
191         /// <param name="vector1">First vector</param>
192         /// <param name="vector2">Second vector</param>
193         /// <returns>New vector that is the difference of the 2 vectors</returns>
194         public static Vector operator -(Vector vector1, Vector vector2)
195         {
196             if (((Object)vector1 == null) || ((Object)vector2 == null))
197                 return null;
198             return Vector.Subtract(vector1, vector2);
199         }
200         /// <summary>
201         /// Calculate the negative (inverted) vector
202         /// </summary>
203         /// <param name="v">Original vector</param>
204         /// <returns>New vector that is the invertion of the original vector</returns>
205         public static Vector operator -(Vector vector)
206         {
207             if ((Object)vector == null)
208                 return null;
209             return Vector.Neg(vector);
210         }
211         /// <summary>
212         /// Calculate the multiplication of a vector with a scalar
213         /// </summary>
214         /// <param name="vector">Vector to be multiplied</param>
215         /// <param name="val">Scalar to multiply vector</param>
216         /// <returns>New vector that is the multiplication of the vector and the scalar</returns>
217         public static Vector operator *(Vector vector, float val)
218         {
219             if ((Object)vector == null)
220                 return null;
221             return Vector.Multiply(vector, val);
222         }
223         /// <summary>
224         /// Calculate the multiplication of a vector with a scalar
225         /// </summary>
226         /// <param name="val">Scalar to multiply vecto</param>
227         /// <param name="vector">Vector to be multiplied</param>
228         /// <returns>New vector that is the multiplication of the vector and the scalar</returns>
229         public static Vector operator *(float val, Vector vector)
230         {
231             if ((Object)vector == null)
232                 return null;
233             return Vector.Multiply(vector, val);
234         }
235 
236         #endregion
237 
238         #region Constants
239         /// <summary>
240         /// Standard (0,0,0) vector
241         /// </summary>
242         public static Vector Zero
243         {
244             get { return new Vector(0.0f, 0.0f, 0.0f); }
245         }
246         /// <summary>
247         /// Standard (1,0,0) vector
248         /// </summary>
249         public static Vector XAxis
250         {
251             get { return new Vector(1.0f, 0.0f, 0.0f); }
252         }
253         /// <summary>
254         /// Standard (0,1,0) vector
255         /// </summary>
256         public static Vector YAxis
257         {
258             get { return new Vector(0.0f, 1.0f, 0.0f); }
259         }
260         /// <summary>
261         /// Standard (0,0,1) vector
262         /// </summary>
263         public static Vector ZAxis
264         {
265             get { return new Vector(0.0f, 0.0f, 1.0f); }
266         }
267         #endregion
268 
269         #region Overides
270         public override bool Equals(object obj)
271         {
272             Vector vector = obj as Vector;
273             if ((Object)vector != null)
274                 return (m_Xcoord.Equals(vector.X))
275                     && (m_Ycoord.Equals(vector.Y))
276                     && (m_Zcoord.Equals(vector.Z));
277             return false;
278         }
279 
280         public override string ToString()
281         {
282             return string.Format(CultureInfo.InvariantCulture, "({0}, {1}, {2})", m_Xcoord, m_Ycoord, m_Zcoord);
283         }
284         public override int GetHashCode()
285         {
286             return m_Xcoord.GetHashCode() ^ m_Ycoord.GetHashCode() ^ m_Zcoord.GetHashCode();
287         }
288         #endregion
289     }
View Code

Particle.cs---粒子属性类

 1     /// <summary>
 2     /// 粒子
 3     /// </summary>
 4     public class Particle
 5     {
 6         /// <summary>
 7         /// 当前粒子所在的位置信息
 8         /// </summary>
 9         private Vector position;
10         /// <summary>
11         /// 当前粒子运行的方向和速度
12         /// </summary>
13         private Vector velocity;
14         /// <summary>
15         /// 当前离子的生命值
16         /// </summary>
17         private int life;
18         /// <summary>
19         /// 当前离子渲染的颜色
20         /// </summary>
21         private Color color;
22 
23         public Particle() : this(Vector.Zero, Vector.Zero, Color.Black, 0) { }
24 
25         public Particle(Vector position, Vector velocity, Color color, int life)
26         {
27             this.position = position;
28             this.velocity = velocity;
29             this.color = color;
30             if (life > 0)
31             {
32                 this.life = life;
33             }
34         }
35 
36         public virtual void Update()
37         {
38             // Update particle's movement according to environment
39             this.velocity = this.velocity - NativeEnvironment.GetInstance().Gravity
40                                     + NativeEnvironment.GetInstance().Wind;
41             // Update particle's position according to movement
42             this.position = this.position + this.velocity;
43             // Update particle's age
44             this.life++;
45         }
46 
47         /// <summary>
48         /// 获取 当前粒子的位置信息
49         /// </summary>
50         public Vector Position
51         {
52             get { return position; }
53         }
54 
55         /// <summary>
56         /// 获取 当前粒子的运行方向和速度
57         /// </summary>
58         public Vector Velocity
59         {
60             get { return velocity; }
61         }
62 
63         /// <summary>
64         /// 获取 当前粒子的生命值
65         /// </summary>
66         public int Life
67         {
68             get { return life; }
69         }
70 
71         /// <summary>
72         /// 获取 当前离子渲染的颜色
73         /// </summary>
74         public Color Color
75         {
76             get { return color; }
77         }
78     }

2)需要考虑自然环境因素NativeEnvironment,并设定以下两个属性:重力、风速。

NativeEnvironment.cs--自然环境因素

 1     /// <summary>
 2     /// 自然环境条件信息
 3     /// </summary>
 4     public class NativeEnvironment
 5     {
 6         /// <summary>
 7         /// 重力
 8         /// </summary>
 9         private Vector gravity = Vector.Zero;
10         /// <summary>
11         /// 风速
12         /// </summary>
13         private Vector wind = Vector.Zero;
14 
15         /// <summary>
16         /// 单例实体对象
17         /// </summary>
18         private static NativeEnvironment instance = null;
19 
20         private NativeEnvironment() { }
21 
22         /// <summary>
23         /// 获取 自然条件信息对象实例
24         /// </summary>
25         public static NativeEnvironment GetInstance()
26         {
27             if (instance == null)
28             {
29                 instance = new NativeEnvironment();
30             }
31             return instance;
32         }
33 
34         /// <summary>
35         /// 获取或设置 重力
36         /// </summary>
37         public Vector Gravity
38         {
39             get { return gravity; }
40             set { gravity = value; }
41         }
42 
43         /// <summary>
44         /// 获取或设置 风速
45         /// </summary>
46         public Vector Wind
47         {
48             get { return wind; }
49             set { wind = value; }
50         }
51     }

3)定义一个喷泉粒子系统.类,它应该包含以下属性:存储当前粒子系统中所有粒子的集合容器、初始化系统时所有粒子的起始位置、最大生命值、粒子渲染的颜色、最大粒子个数,还应该包含以下几个方法:新增粒子,一个生命值单位变化后批量修改所有粒子的方法。

Particles.cs --- 粒子系统抽象类

 1     /// <summary>
 2     /// 粒子系统
 3     /// </summary>
 4     public abstract class Particles
 5     {
 6         /// <summary>
 7         /// 当前粒子系统内所有的粒子元素存储集合
 8         /// </summary>
 9         protected List<Particle> particles = new List<Particle>();
10         /// <summary>
11         /// 粒子系统的中心位置
12         /// </summary>
13         protected Vector position;
14         /// <summary>
15         /// 默认粒子的最大生命值
16         /// </summary>
17         protected int maxLife = 150;
18         /// <summary>
19         /// 默认粒子的渲染颜色
20         /// </summary>
21         protected Color color;
22 
23         /// <summary>
24         /// 生成(新增)粒子到粒子系统中
25         /// </summary>
26         /// <returns></returns>
27         protected abstract Particle Create();
28 
29         /// <summary>
30         /// 修改粒子系统中所有粒子的状态位置等信息
31         /// </summary>
32         /// <returns></returns>
33         public abstract bool Update();
34 
35         public virtual void Draw(Graphics g)
36         {
37             Pen pen;
38             int intense;
39             Particle part;
40 
41             for (int i = 0; i < this.particles.Count; i++)
42             {
43                 part = this[i];
44                 // Calculate particle intensity
45                 intense = (int)((float)part.Life / this.MaxLife);
46                 // Generate pen for the particle
47                 pen = new Pen(Color.FromArgb(intense * this.color.R,
48                                              intense * this.color.G,
49                                              intense * this.color.B));
50                 // Draw particle
51                 g.DrawEllipse(pen, part.Position.X, part.Position.Y,
52                     Math.Max(1, 4 * part.Life / this.MaxLife),
53                     Math.Max(1, 4 * part.Life / this.MaxLife));
54                 pen.Dispose();
55             }
56         }
57 
58         /// <summary>
59         /// 根据index访问系统中某个粒子对象
60         /// </summary>
61         /// <param name="index"></param>
62         /// <returns></returns>
63         public Particle this[int index]
64         {
65             get { return (Particle)this.particles[index]; }
66         }
67 
68         /// <summary>
69         /// 返回系统中所有粒子个数
70         /// </summary>
71         public int Count
72         {
73             get { return this.particles.Count; }
74         }
75 
76         /// <summary>
77         /// 返回系统中粒子最大生命值
78         /// </summary>
79         public int MaxLife
80         {
81             get { return this.maxLife; }
82         }
83 
84     }

FountainParticlesSystem.cs---喷泉粒子系统定义

  1     public class FountainParticlesSystem : Particles
  2     {
  3         private static readonly int DEFAULT_NUM_PARTICLES = 500;
  4 
  5         // Random numbers generator
  6         private Random m_rand = new Random();
  7 
  8         /// <summary>
  9         /// Default constructor
 10         /// </summary>
 11         public FountainParticlesSystem()
 12             : this(Vector.Zero, Color.Black)
 13         { }
 14 
 15         /// <summary>
 16         /// Constructor
 17         /// </summary>
 18         /// <param name="pos">Starting position of system</param>
 19         public FountainParticlesSystem(Vector pos)
 20             : this(pos, Color.Black)
 21         { }
 22 
 23         /// <summary>
 24         /// Constructor
 25         /// </summary>
 26         /// <param name="pos">Starting position of system</param>
 27         /// <param name="col">Color of the particles in the system</param>
 28         public FountainParticlesSystem(Vector pos, Color col)
 29         {
 30             // Set system's position at given position
 31             this.position = pos;
 32             // Set system color to given color
 33             this.color = col;
 34             // Create ONLY 5 particles
 35             for (int i = 0; i < 5; i++)
 36             {
 37                 // Create particle, and add it to the list of particles
 38                 this.particles.Add(Create());
 39             }
 40         }
 41 
 42         /// <summary>
 43         /// Generate a single particle in the system.
 44         /// This function is used when particles are first created, and when they are regenerated
 45         /// </summary>
 46         /// <returns>New particle</returns>
 47         protected override Particle Create()
 48         {
 49             // 生成随机方向和速度的新粒子
 50             // 在一个喷泉,粒子移动几乎直
 51             float rndX = 2.0f * ((float)m_rand.NextDouble() - 0.4f);
 52             float rndY = -1.0f - 1 * (float)m_rand.NextDouble();
 53             float rndZ = 1.5f * ((float)m_rand.NextDouble() - 0.4f);
 54 
 55             // Create new particle at system's starting position
 56             Particle part = new Particle(this.position,
 57                 // With generated direction and speed
 58                 new Vector(rndX, rndY, rndZ), this.color,
 59                 // And a random starting life
 60                 m_rand.Next(50));
 61 
 62             // Return newly created particle
 63             return part;
 64         }
 65 
 66         /// <summary>
 67         /// Update all the particles in the system
 68         /// </summary>
 69         /// <returns>False - if there are no more particles in system
 70         /// True - otherwise</returns>
 71         public override bool Update()
 72         {
 73             Particle part;
 74             int count = this.Count;
 75 
 76             // For each particle
 77             for (int i = 0; i < count; i++)
 78             {
 79                 // Get particle from list
 80                 part = (Particle)this.particles[i];
 81                 // Update particle and check age
 82                 part.Update();
 83 
 84                 /*part.Life > this.maxLife && */
 85                 if (part.Position.Y >= this.position.Y)
 86                 {
 87                     // Remove old particles
 88                     this.particles.RemoveAt(i);
 89                     // Update counter and index
 90                     i--;
 91                     count = this.particles.Count;
 92                 }
 93             }
 94 
 95             // If there aren't enough particles
 96             if (this.particles.Count < DEFAULT_NUM_PARTICLES)
 97             {
 98                 // Add another particles
 99                 this.particles.Add(Create());
100             }
101 
102             // Always return true, since system is regenerating
103             return true;
104         }
105     }

4)设定粒子一个生命值增长需要耗费的时间、一个用来渲染粒子系统当前生命时刻的画布。

 1     public partial class Main : Form
 2     {
 3         private static readonly Vector MIDDLE_OF_VIEW = new Vector(250, 250, 250);
 4         private Particles ps = null;
 5 
 6         public Main()
 7         {
 8             InitializeComponent();
 9 
10             this.timer.Interval = 20;//ms
11 
12             NativeEnvironment.GetInstance().Gravity = new Vector(0.0f, -0.02f, 0.0f);
13             NativeEnvironment.GetInstance().Wind = new Vector(0.0f, 0.0f, 0.0f);
14 
15             if (NativeEnvironment.GetInstance().Gravity == NativeEnvironment.GetInstance().Wind)
16                 return;
17         }
18 
19         private void btnStart_Click(object sender, EventArgs e)
20         {
21             ps = new FountainParticlesSystem(MIDDLE_OF_VIEW, Color.FromArgb(0, 0, 255));
22             this.timer.Enabled = true;
23         }
24 
25         private void timer_Tick(object sender, System.EventArgs e)
26         {
27             if (!ps.Update())
28             {
29                 picDisplay.Refresh();
30                 ps = null;
31                 this.timer.Enabled = false;
32             }
33             else
34             {
35                 picDisplay.Refresh();
36             }
37         }
38 
39         private void picDisplay_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
40         {
41             if (ps == null)
42                 return;
43             ps.Draw(e.Graphics);
44         }
45     }

效果:

代码下载位置:http://pan.baidu.com/s/1gfaODF1

原文地址:https://www.cnblogs.com/yy3b2007com/p/7414311.html