参考:https://www.codeproject.com/articles/10003/a-basic-particles-system
如果需要实现一个喷泉粒子系统:
1)需要先定义每个粒子元素Particle类:它应包含有运动方向和速度,初始化位置(及运动一个生命值后的位置),生命值,粒子渲染的颜色,及一个生命值增加一个单位后对应的粒子属性变化。
Vector.cs--存储粒子位置信息
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
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