source: 2013/30/MiskaK/MW2(My Warfare 2)/Paranneltu Jypeli/Effects/ParticleSystem.cs @ 4507

Revision 4507, 15.4 KB checked in by anlakane, 6 years ago (diff)

Talletus.

Line 
1using System;
2using System.Collections.Generic;
3using Microsoft.Xna.Framework;
4using Microsoft.Xna.Framework.Graphics;
5
6namespace Jypeli.Effects
7{
8    public enum BlendMode
9    {
10        Alpha,
11        Additive
12    }
13
14
15    /// <summary>
16    /// Järjestelmä partikkelien käsittelyyn
17    /// </summary>
18    public class ParticleSystem : GameObject
19    {
20        private Random random = new Random();
21
22        // Lista efektin partikkeleille
23        private LinkedList<Particle> particles;
24
25        // Jono efektin vapaille partikkeleille
26        private Queue<Particle> freeParticles;
27
28        /// <summary>
29        /// Yksittäisen partikkelin kuva.
30        /// </summary>
31        public Image ParticleImage { get; set; }
32
33        /// <summary>
34        /// Partikkelin toissijainen kuva. Jos <c>null</c> niin käytetään vain yhtä kuvaa.
35        /// </summary>
36        public Image OuterParticleImage { get; set; }
37
38        /// <summary>
39        /// Perivien luokkien täytyy määrittää nämä muuttujat
40        /// </summary>
41        #region Subclass variables
42
43        /// <summary>
44        /// Pienin skaalaus joka efektin partikkeleilla voi olla
45        /// </summary>
46        public double MinScale { get; set; }
47        /// <summary>
48        /// Suurin skaalaus joka efektin partikkeleilla voi olla
49        /// </summary>
50        public double MaxScale { get; set; }
51
52        /// <summary>
53        /// Määrä jonka partikkeli skaalautuu päivityksessä
54        /// </summary>
55        public double ScaleAmount { get; set; }
56
57        /// <summary>
58        /// Pienin nopeus joka efektin partikkelilla voi olla
59        /// </summary>
60        public double MinVelocity { get; set; }
61        /// <summary>
62        /// Suurin nopeus joka efektin partikkelilla voi olla
63        /// </summary>
64        public double MaxVelocity { get; set; }
65
66        /// <summary>
67        /// Efektin partikkelin lyhin mahdollinen elinaika
68        /// </summary>
69        public double MinLifetime { get; set; }
70       
71        /// <summary>
72        /// Efektin partikkelin pisin mahdollinen elinaika
73        /// </summary>
74        public double MaxLifetime { get; set; }
75
76        /// <summary>
77        /// Pienin kiihtyvyys joka efektin partikkelilla voi olla
78        /// </summary>
79        public double MinAcceleration { get; set; }
80
81        /// <summary>
82        /// Suurin kiihtyvyys joka efektin partikkelilla voi olla
83        /// </summary>
84        public double MaxAcceleration { get; set; }
85
86        /// <summary>
87        /// Pienin pyörimisnopeus joka efektin partikkelilla voi olla
88        /// </summary>
89        public double MinRotationSpeed { get; set; }
90
91        /// <summary>
92        /// Suurin pyörimisnopeus joka efektin partikkelilla voi olla
93        /// </summary>
94        public double MaxRotationSpeed { get; set; }
95
96        /// <summary>
97        /// Pienin kierre mikä efektin partikkelilla voi olla
98        /// </summary>
99        public double MinRotation { get; set; }
100
101        /// <summary>
102        /// Suurin kierre mikä efektin partikkelilla voi olla
103        /// </summary>
104        public double MaxRotation { get; set; }
105
106        /// <summary>
107        /// Efektin läpinäkyvyyskerroin (0.0-1.0)
108        /// </summary>
109        public double AlphaAmount { get; set; }
110
111        #endregion
112
113        public BlendMode BlendMode { get; set; }
114
115        /// <summary>
116        /// Vaikuttaako efektiin tuuli
117        /// </summary>
118        public Boolean IgnoreWind { get; set; }
119
120        private Boolean visible = true;
121        private Boolean fadeIn = false;
122        private Boolean fadeOut = false;
123        private double originalAlpha = 1.0;
124        private double fadeTime = 0.0;
125        private double fadeTimePassed = 0.0;
126
127        /// <summary>
128        /// Efekti tulee näkyviin tietyn sekuntimäärän aikana
129        /// </summary>
130        /// <param name="timeInSeconds">Aika joka kuluu että efekti on näkyvä</param>
131        public void FadeIn(double timeInSeconds)
132        {
133            if (!visible)
134            {
135                fadeTimePassed = 0.0;
136                fadeIn = true;
137                fadeTime = timeInSeconds;
138            }
139        }
140
141        /// <summary>
142        /// Efekti hiipuu näkyvistä tietyn sekuntimäärän aikana
143        /// </summary>
144        /// <param name="TimeInSeconds">Aika joka kuluu että efekti katoaa</param>
145        public void FadeOut(double TimeInSeconds)
146        {
147            if (visible)
148            {
149                originalAlpha = AlphaAmount;
150                fadeTimePassed = 0.0;
151                fadeOut = true;
152                fadeTime = TimeInSeconds;
153            }
154        }
155        /// <summary>
156        /// Muodostaja
157        /// </summary>
158        /// <param name="particleImage">Partikkelin kuva.</param>
159        /// <param name="maxAmountOfParticles">Suurin määrä partikkeleita mitä efektillä voi olla kerralla</param>
160        public ParticleSystem(Image particleImage, int maxAmountOfParticles)
161            : base(0, 0)
162        {
163            this.ParticleImage = particleImage;
164            AlphaAmount = 1.0;
165            IgnoreWind = false;
166            BlendMode = BlendMode.Alpha;
167
168            IsUpdated = true;
169            InitializeParticles();
170
171            particles = new LinkedList<Particle>();
172            freeParticles = new Queue<Particle>(maxAmountOfParticles);
173            for (int i = 0; i < maxAmountOfParticles; i++)
174            {
175                freeParticles.Enqueue(new Particle());
176            }
177        }
178
179        /// <summary>
180        /// Metodi joka asettaa partikkeleille attribuutit
181        /// Täytyy kutsua perityistä luokista
182        /// </summary>
183        protected virtual void InitializeParticles()
184        {
185        }
186
187        /// <summary>
188        /// Lisää efektin kentälle
189        /// </summary>
190        /// <param name="x">Efektin x-koordinaatti</param>
191        /// <param name="y">Efektin y-koordinaatti</param>
192        /// <param name="numberOfParticles">Partikkeleiden määrä efektissä</param>
193        public void AddEffect(double x, double y, int numberOfParticles)
194        {
195            AddEffect(new Vector(x, y), numberOfParticles);
196        }
197
198        public void AddEffect(Vector position, Angle angle, int numberOfParticles)
199        {
200            for (int i = 0; i < numberOfParticles && freeParticles.Count > 0; i++)
201            {
202                Particle p = freeParticles.Dequeue();
203                particles.AddLast(p);
204                InitializeParticle(p, position, angle);
205            }
206        }
207
208        /// <summary>
209        /// Lisää efektin kentälle
210        /// </summary>
211        /// <param name="position">Paikka.</param>
212        /// <param name="numberOfParticles">Partikkeleiden määrä efektissä</param>
213        public void AddEffect(Vector position, int numberOfParticles)
214        {
215            for (int i = 0; i < numberOfParticles && freeParticles.Count > 0; i++)
216            {
217                Particle p = freeParticles.Dequeue();
218                particles.AddLast(p);
219                InitializeParticle(p, position);
220            }
221        }
222
223        /// <summary>
224        /// Antaa satunnaisen suunnan
225        /// </summary>
226        /// <returns>Satunnainen suunta</returns>
227        protected virtual Vector GiveRandomDirection()
228        {
229            double angle = random.NextDouble() * MathHelper.TwoPi;
230            return new Vector(Math.Cos(angle), Math.Sin(angle));
231        }
232
233        /// <summary>
234        /// Alustaa yhden partikkelin
235        /// </summary>
236        /// <param name="p">Partikkeli joka alustetaan</param>
237        /// <param name="position">Sijainti johon alustetaan</param>
238        protected virtual void InitializeParticle(Particle p, Vector position)
239        {
240            Vector direction = GiveRandomDirection();
241
242            double scale = RandomGen.NextDouble(MinScale, MaxScale);
243            double rotation = RandomGen.NextDouble(MinRotation, MaxRotation);
244            double velocity = RandomGen.NextDouble(MinVelocity, MaxVelocity);
245            double lifetime = RandomGen.NextDouble(MinLifetime, MaxLifetime);
246            double acceleration = RandomGen.NextDouble(MinAcceleration, MaxAcceleration);
247            double rotationSpeed = RandomGen.NextDouble(MinRotationSpeed, MaxRotationSpeed);
248
249            p.Initialize(position, scale, rotation, rotationSpeed, velocity * direction, acceleration * direction, lifetime);
250        }
251
252        protected virtual void InitializeParticle(Particle p, Vector position, Angle angle)
253        {
254            Vector direction = GiveRandomDirection();
255
256            double rotation = angle.Degrees;
257            double scale = RandomGen.NextDouble(MinScale, MaxScale);
258            double velocity = RandomGen.NextDouble(MinVelocity, MaxVelocity);
259            double lifetime = RandomGen.NextDouble(MinLifetime, MaxLifetime);
260            double acceleration = RandomGen.NextDouble(MinAcceleration, MaxAcceleration);
261            double rotationSpeed = RandomGen.NextDouble(MinRotationSpeed, MaxRotationSpeed);
262
263            p.Initialize(position, scale, rotation, rotationSpeed, velocity * direction, acceleration * direction, lifetime);
264        }
265
266        /// <summary>
267        /// Kutsutaan kun luokka päivitetään
268        /// </summary>
269        /// <param name="time">Gametime</param>
270        public override void Update(Time time)
271        {
272            double t = time.SinceLastUpdate.TotalSeconds;
273
274            LinkedListNode<Particle> node = particles.First;
275            while (node != null)
276            {
277                Particle p = node.Value;
278                if (p.Alive)
279                {
280                    p.Update(t);
281                    node = node.Next;
282                }
283                else
284                {
285                    freeParticles.Enqueue(p);
286                    LinkedListNode<Particle> previous = node;
287                    node = node.Next;
288                    particles.Remove(previous);
289                }
290            }
291            if (fadeOut)
292            {
293                AlphaAmount = Math.Min(1 - fadeTimePassed / fadeTime, originalAlpha);
294                fadeTimePassed += t;
295                if (fadeTime <= fadeTimePassed)
296                {
297                    AlphaAmount = 0.0;
298                    fadeOut = false;
299                    visible = false;
300                }
301            }
302            if (fadeIn)
303            {
304                AlphaAmount = Math.Min(fadeTimePassed / fadeTime, originalAlpha);
305                fadeTimePassed += t;
306                if (fadeTime <= fadeTimePassed)
307                {
308                    AlphaAmount = originalAlpha;
309                    fadeIn = false;
310                    visible = true;
311                }
312            }
313        }
314
315
316        static readonly Vector2 textureTopLeft = new Vector2(0.0f, 0.0f);
317        static readonly Vector2 textureBottomLeft = new Vector2(0.0f, 1.0f);
318        static readonly Vector2 textureTopRight = new Vector2(1.0f, 0.0f);
319        static readonly Vector2 textureBottomRight = new Vector2(1.0f, 1.0f);
320
321        static readonly Vector3 topLeft = new Vector3(-0.5f, 0.5f, 0);
322        static readonly Vector3 bottomLeft = new Vector3(-0.5f, -0.5f, 0);
323        static readonly Vector3 topRight = new Vector3(0.5f, 0.5f, 0);
324        static readonly Vector3 bottomRight = new Vector3(0.5f, -0.5f, 0);
325
326        static readonly VertexPositionTexture[] vertices =
327        {
328            // Triangle 1
329            new VertexPositionTexture(topLeft, textureTopLeft),
330            new VertexPositionTexture(bottomLeft, textureBottomLeft),
331            new VertexPositionTexture(topRight, textureTopRight),
332
333            // Triangle 2
334            new VertexPositionTexture(bottomLeft, textureBottomLeft),
335            new VertexPositionTexture(bottomRight, textureBottomRight),
336            new VertexPositionTexture(topRight, textureTopRight),
337        };
338
339        private static BlendState ToXnaBlendState(BlendMode mode)
340        {
341            switch (mode)
342            {
343                case BlendMode.Alpha:
344                    return BlendState.AlphaBlend;
345                case BlendMode.Additive:
346                    return BlendState.Additive;
347                default:
348                    return BlendState.AlphaBlend;
349            }
350        }
351
352        internal void Draw(Matrix worldMatrix)
353        {
354            var device = Game.GraphicsDevice;
355            var effect = Graphics.BasicTextureEffect;
356
357            Texture2D texture = ParticleImage.XNATexture;
358
359            device.RasterizerState = RasterizerState.CullClockwise;
360            device.SamplerStates[0] = SamplerState.LinearClamp;
361            device.BlendState = ToXnaBlendState(BlendMode);
362           
363            effect.Texture = texture;
364            effect.CurrentTechnique.Passes[0].Apply();
365
366            if (OuterParticleImage == null)
367            {
368                foreach (Particle p in particles)
369                {
370                    double nTime = p.Lifetime.TotalMilliseconds / p.MaxLifetime.TotalMilliseconds;
371                    double alpha = 4 * nTime * (1 - nTime) * AlphaAmount;
372                    float scale = (float)(p.Scale * (.75f + .25f * nTime * ScaleAmount));
373
374                    Matrix matrix =
375                        Matrix.CreateScale(scale, scale, 1f)
376                        * Matrix.CreateRotationZ((float)p.Rotation)
377                        * Matrix.CreateTranslation((float)p.Position.X, (float)p.Position.Y, 0f)
378                        * worldMatrix
379                        ;
380
381                    effect.Alpha = (float)alpha;
382                    effect.World = matrix;
383                    effect.CurrentTechnique.Passes[0].Apply();
384
385                    device.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleList, vertices, 0, 2);
386                }
387            }
388            else
389            {
390                Texture2D outerTexture = OuterParticleImage.XNATexture;
391
392                foreach (Particle p in particles)
393                {
394                    double nTime = p.Lifetime.TotalMilliseconds / p.MaxLifetime.TotalMilliseconds;
395                    double alpha = 4 * nTime * (1 - nTime) * AlphaAmount;
396                    float scale = (float)(p.Scale * (.75f + .25f * nTime * ScaleAmount));
397
398                    Matrix matrix =
399                        Matrix.CreateScale(scale, scale, 1f)
400                        * Matrix.CreateRotationZ((float)p.Rotation)
401                        * Matrix.CreateTranslation((float)p.Position.X, (float)p.Position.Y, 0f)
402                        * worldMatrix
403                        ;
404                    Matrix matrix2 =
405                        Matrix.CreateScale(0.65f, 0.65f, 1f)
406                        * matrix;
407
408                    effect.Texture = texture;
409                    effect.Alpha = (float)alpha;
410                    effect.World = matrix;
411                    effect.CurrentTechnique.Passes[0].Apply();
412
413                    device.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleList, vertices, 0, 2);
414
415                    effect.Texture = outerTexture;
416                    effect.World = matrix2;
417                    effect.CurrentTechnique.Passes[0].Apply();
418
419                    device.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleList, vertices, 0, 2);
420                }
421            }
422        }
423    }
424}
Note: See TracBrowser for help on using the repository browser.