| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 | //-----------------------------------------------------------------------------// ParticleDemo.cs//// Microsoft XNA Community Game Platform// Copyright (C) Microsoft Corporation. All rights reserved.//-----------------------------------------------------------------------------using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Graphics;using SkinnedModel;namespace XnaGraphicsDemo{    /// <summary>    /// Demo shows how to use SpriteBatch.    /// </summary>    class ParticleDemo : MenuComponent    {        const int MaxParticles = 5000;        /// <summary>        /// Represents a single particle in the particle system.        /// </summary>        struct Particle        {            public Vector2 Position;            public Vector2 Velocity;            public float Size;            public float Rotation;            public float Spin;            public Color Color;        }        Particle[] particles = new Particle[MaxParticles];        int firstParticle;        int particleCount;        FloatMenuEntry spawnRate;        float spawnCounter;        Texture2D cat;        Random random = new Random();        /// <summary>        /// Initializes a new instance of the <see cref="ParticleDemo"/> class and sets up menu entries.        /// </summary>        /// <param name="game">The game instance.</param>        public ParticleDemo(DemoGame game)            : base(game)        {            Entries.Add(spawnRate = new FloatMenuEntry() { Text = "spawn rate" });            // This menu option for changing the resolution is currently disabled,            // because the image scaler feature is not yet implemented in the CTP release.            /*            Entries.Add(new ResolutionMenu(game.graphics));            */            Entries.Add(new MenuEntry            {                Text = "back",                Clicked = delegate                {                    // Before we quit back out of this menu, reset back to the default resolution.                    if (game.Graphics.PreferredBackBufferWidth != 480)                    {                        game.Graphics.PreferredBackBufferWidth = 480;                        game.Graphics.PreferredBackBufferHeight = 800;                        game.Graphics.ApplyChanges();                    }                                        Game.SetActiveMenu(0);            } });        }        /// <summary>        /// Resets the menu state and particle system to defaults.        /// </summary>        public override void Reset()        {            firstParticle = 0;            particleCount = 0;            spawnRate.Value = 0.2f;            base.Reset();        }        /// <summary>        /// Loads content for this demo, including the cat texture.        /// </summary>        protected override void LoadContent()        {            cat = Game.Content.Load<Texture2D>("cat");        }        /// <summary>        /// Updates the particle system and spawns new particles as needed.        /// </summary>        /// <param name="gameTime">The current game time.</param>        public override void Update(GameTime gameTime)        {            int i = firstParticle;            for (int j = particleCount; j > 0; j--)            {                // Move a particle.                particles[i].Position += particles[i].Velocity;                particles[i].Rotation += particles[i].Spin;                particles[i].Velocity.Y += 0.1f;                // Retire old particles?                const float borderPadding = 96;                if (i == firstParticle)                {                    if ((particles[i].Position.X < -borderPadding) ||                        (particles[i].Position.X > 480 + borderPadding) ||                        (particles[i].Position.Y < -borderPadding) ||                        (particles[i].Position.Y > 800 + borderPadding))                    {                        if (++firstParticle >= MaxParticles)                            firstParticle = 0;                        particleCount--;                    }                }                if (++i >= MaxParticles)                    i = 0;            }            // Spawn new particles?            spawnCounter += spawnRate.Value * 10;            while (spawnCounter > 1)            {                SpawnParticle(null);                spawnCounter--;            }            base.Update(gameTime);        }        /// <summary>        /// Helper method to create a new cat particle at the given position, or at a random position if null.        /// </summary>        /// <param name="position">The position to spawn the particle, or null for random.</param>        void SpawnParticle(Vector2? position)        /// <returns>Nothing. Spawns a new particle if possible.</returns>        {            if (particleCount >= MaxParticles)                return;            int i = firstParticle + particleCount;            if (i >= MaxParticles)                i -= MaxParticles;            particles[i].Position = position ?? new Vector2((float)random.NextDouble() * 480, (float)random.NextDouble() * 800);            particles[i].Velocity = new Vector2((float)random.NextDouble() - 0.5f, (float)random.NextDouble() - 0.5f) * 10f;            particles[i].Size = (float)random.NextDouble() * 0.5f + 0.5f;            particles[i].Rotation = 0;            particles[i].Spin = ((float)random.NextDouble() - 0.5f) * 0.1f;            if (position.HasValue)            {                // Explicitly positioned particles have no tint.                particles[i].Color = Color.White;            }            else            {                // Randomly positioned particles have random tint colors.                byte r = (byte)(128 + random.NextDouble() * 127);                byte g = (byte)(128 + random.NextDouble() * 127);                byte b = (byte)(128 + random.NextDouble() * 127);                particles[i].Color = new Color(r, g, b);            }            particleCount++;        }        /// <summary>        /// Draws the cat particle system and menu.        /// </summary>        /// <param name="gameTime">The current game time.</param>        public override void Draw(GameTime gameTime)        {            DrawTitle("particles", Color.CornflowerBlue, Color.Lerp(Color.Blue, Color.CornflowerBlue, 0.85f));            SpriteBatch.Begin(0, null, null, null, null, null, Game.ScaleMatrix);            Vector2 origin = new Vector2(cat.Width, cat.Height) / 2;            int i = firstParticle + particleCount - 1;            if (i >= MaxParticles)                i -= MaxParticles;            for (int j = 0; j < particleCount; j++)            {                SpriteBatch.Draw(cat, particles[i].Position, null, particles[i].Color, particles[i].Rotation, origin, particles[i].Size, 0, 0);                if (--i < 0)                    i = MaxParticles - 1;            }            SpriteBatch.End();            base.Draw(gameTime);        }        /// <summary>        /// Dragging on the menu background creates new particles at the last touch point.        /// </summary>        /// <param name="delta">The amount the pointer has moved since the last drag event.</param>        protected override void OnDrag(Vector2 delta)        {            SpawnParticle(LastTouchPoint);        }        /// <summary>        /// Custom menu entry subclass for cycling through different backbuffer resolutions.        /// </summary>        class ResolutionMenu : MenuEntry        {            GraphicsDeviceManager graphics;        /// <summary>        /// Initializes a new instance of the <see cref="ResolutionMenu"/> class for changing resolutions.        /// </summary>        /// <param name="graphics">The graphics device manager to modify.</param>        public ResolutionMenu(GraphicsDeviceManager graphics)            {                this.graphics = graphics;            }            /// <summary>            /// Gets the display text for the current resolution.            /// </summary>            public override string Text            {                get { return string.Format("{0}x{1}", graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight); }                set { }            }            /// <summary>            /// Cycles through available resolutions and applies the change.            /// </summary>            public override void OnClicked()            {                switch (graphics.PreferredBackBufferWidth)                {                    case 480:                        graphics.PreferredBackBufferWidth = 360;                        graphics.PreferredBackBufferHeight = 600;                        break;                    case 360:                        graphics.PreferredBackBufferWidth = 240;                        graphics.PreferredBackBufferHeight = 400;                        break;                    case 240:                        graphics.PreferredBackBufferWidth = 480;                        graphics.PreferredBackBufferHeight = 800;                        break;                }                graphics.ApplyChanges();                base.OnClicked();            }        }    }}
 |