Sfoglia il codice sorgente

ParticleSample update to SDK and MG 3.8.*

CartBlanche 6 giorni fa
parent
commit
9295f769ce
36 ha cambiato i file con 1827 aggiunte e 1440 eliminazioni
  1. 49 0
      ParticleSample/.vscode/launch.json
  2. 179 0
      ParticleSample/.vscode/tasks.json
  3. 0 0
      ParticleSample/Core/Content/Background.png
  4. 0 0
      ParticleSample/Core/Content/Default.png
  5. 0 0
      ParticleSample/Core/Content/Game.ico
  6. 0 0
      ParticleSample/Core/Content/GameThumbnail.png
  7. 0 0
      ParticleSample/Core/Content/explosion.png
  8. 0 0
      ParticleSample/Core/Content/explosion.xnb
  9. 47 47
      ParticleSample/Core/Content/font.spritefont
  10. 0 0
      ParticleSample/Core/Content/font.xnb
  11. 0 0
      ParticleSample/Core/Content/smoke.bmp
  12. 0 0
      ParticleSample/Core/Content/smoke.xnb
  13. 89 93
      ParticleSample/Core/ExplosionParticleSystem.cs
  14. 75 79
      ParticleSample/Core/ExplosionSmokeParticleSystem.cs
  15. 112 116
      ParticleSample/Core/Particle.cs
  16. 9 0
      ParticleSample/Core/ParticleSample.Core.csproj
  17. 299 311
      ParticleSample/Core/ParticleSampleGame.cs
  18. 362 368
      ParticleSample/Core/ParticleSystem.cs
  19. 105 109
      ParticleSample/Core/SmokePlumeParticleSystem.cs
  20. 0 16
      ParticleSample/Info.plist
  21. 0 91
      ParticleSample/ParticleSample.Linux.csproj
  22. 0 88
      ParticleSample/ParticleSample.MacOS.csproj
  23. 0 120
      ParticleSample/ParticleSample.iOS.csproj
  24. 44 0
      ParticleSample/ParticleSample.sln
  25. 19 0
      ParticleSample/Platforms/Android/AndroidManifest.xml
  26. 26 0
      ParticleSample/Platforms/Android/MainActivity.cs
  27. 35 0
      ParticleSample/Platforms/Android/ParticleSample.Android.csproj
  28. 26 0
      ParticleSample/Platforms/Desktop/ParticleSample.DesktopGL.csproj
  29. 15 0
      ParticleSample/Platforms/Desktop/Program.cs
  30. 31 0
      ParticleSample/Platforms/Windows/ParticleSample.Windows.csproj
  31. 0 2
      ParticleSample/Platforms/Windows/Program.cs
  32. 34 0
      ParticleSample/Platforms/Windows/app.manifest
  33. 40 0
      ParticleSample/Platforms/iOS/Info.plist
  34. 27 0
      ParticleSample/Platforms/iOS/ParticleSample.iOS.csproj
  35. 14 0
      ParticleSample/Platforms/iOS/Program.cs
  36. 190 0
      ParticleSample/README.md

+ 49 - 0
ParticleSample/.vscode/launch.json

@@ -0,0 +1,49 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Launch Windows",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-windows",
+            "program": "${workspaceFolder}/Platforms/Windows/bin/Debug/net8.0-windows/ParticleSample.exe",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "console": "internalConsole",
+            "stopAtEntry": false
+        },
+        {
+            "name": "Launch DesktopGL",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-desktopgl",
+            "program": "${workspaceFolder}/Platforms/Desktop/bin/Debug/net8.0/ParticleSample.exe",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "console": "internalConsole",
+            "stopAtEntry": false
+        },
+        {
+            "name": "Launch Android",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-android",
+            "program": "${workspaceFolder}/Platforms/Android/bin/Debug/net8.0-android/ParticleSample.dll",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "console": "internalConsole",
+            "stopAtEntry": false
+        },
+        {
+            "name": "Launch iOS",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-ios",
+            "program": "${workspaceFolder}/Platforms/iOS/bin/Debug/net8.0-ios/iossimulator-x64/ParticleSample.dll",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "console": "internalConsole",
+            "stopAtEntry": false
+        }
+    ]
+}

+ 179 - 0
ParticleSample/.vscode/tasks.json

@@ -0,0 +1,179 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "build-windows",
+            "command": "dotnet",
+            "args": [
+                "build",
+                "Platforms/Windows/ParticleSample.Windows.csproj",
+                "-c", "Debug"
+            ],
+            "type": "shell",
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "silent",
+                "focus": false,
+                "panel": "shared"
+            },
+            "problemMatcher": ["$msCompile"]
+        },
+        {
+            "label": "build-desktopgl",
+            "command": "dotnet",
+            "args": [
+                "build",
+                "Platforms/Desktop/ParticleSample.DesktopGL.csproj",
+                "-c", "Debug"
+            ],
+            "type": "shell",
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "silent",
+                "focus": false,
+                "panel": "shared"
+            },
+            "problemMatcher": ["$msCompile"]
+        },
+        {
+            "label": "build-android",
+            "command": "dotnet",
+            "args": [
+                "build",
+                "Platforms/Android/ParticleSample.Android.csproj",
+                "-c", "Debug"
+            ],
+            "type": "shell",
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "silent",
+                "focus": false,
+                "panel": "shared"
+            },
+            "problemMatcher": ["$msCompile"]
+        },
+        {
+            "label": "build-ios",
+            "command": "dotnet",
+            "args": [
+                "build",
+                "Platforms/iOS/ParticleSample.iOS.csproj",
+                "-c", "Debug"
+            ],
+            "type": "shell",
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "silent",
+                "focus": false,
+                "panel": "shared"
+            },
+            "problemMatcher": ["$msCompile"]
+        },
+        {
+            "label": "run-windows",
+            "command": "dotnet",
+            "args": [
+                "run",
+                "--project",
+                "Platforms/Windows/ParticleSample.Windows.csproj",
+                "-c", "Debug"
+            ],
+            "type": "shell",
+            "group": "test",
+            "presentation": {
+                "echo": true,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared"
+            },
+            "dependsOn": "build-windows"
+        },
+        {
+            "label": "run-desktopgl",
+            "command": "dotnet",
+            "args": [
+                "run",
+                "--project",
+                "Platforms/Desktop/ParticleSample.DesktopGL.csproj",
+                "-c", "Debug"
+            ],
+            "type": "shell",
+            "group": "test",
+            "presentation": {
+                "echo": true,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared"
+            },
+            "dependsOn": "build-desktopgl"
+        },
+        {
+            "label": "run-android",
+            "command": "dotnet",
+            "args": [
+                "run",
+                "--project",
+                "Platforms/Android/ParticleSample.Android.csproj",
+                "-c", "Debug"
+            ],
+            "type": "shell",
+            "group": "test",
+            "presentation": {
+                "echo": true,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared"
+            },
+            "dependsOn": "build-android"
+        },
+        {
+            "label": "run-ios",
+            "command": "dotnet",
+            "args": [
+                "run",
+                "--project",
+                "Platforms/iOS/ParticleSample.iOS.csproj",
+                "-c", "Debug"
+            ],
+            "type": "shell",
+            "group": "test",
+            "presentation": {
+                "echo": true,
+                "reveal": "always",
+                "focus": false,
+                "panel": "shared"
+            },
+            "dependsOn": "build-ios"
+        },
+        {
+            "label": "clean",
+            "command": "dotnet",
+            "args": ["clean"],
+            "type": "shell",
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "silent",
+                "focus": false,
+                "panel": "shared"
+            }
+        },
+        {
+            "label": "restore",
+            "command": "dotnet",
+            "args": ["restore"],
+            "type": "shell",
+            "group": "build",
+            "presentation": {
+                "echo": true,
+                "reveal": "silent",
+                "focus": false,
+                "panel": "shared"
+            }
+        }
+    ]
+}

+ 0 - 0
ParticleSample/Background.png → ParticleSample/Core/Content/Background.png


+ 0 - 0
ParticleSample/Default.png → ParticleSample/Core/Content/Default.png


+ 0 - 0
ParticleSample/Game.ico → ParticleSample/Core/Content/Game.ico


+ 0 - 0
ParticleSample/GameThumbnail.png → ParticleSample/Core/Content/GameThumbnail.png


+ 0 - 0
ParticleSample/Content/explosion.png → ParticleSample/Core/Content/explosion.png


+ 0 - 0
ParticleSample/Content/explosion.xnb → ParticleSample/Core/Content/explosion.xnb


+ 47 - 47
ParticleSample/Content/font.spritefont → ParticleSample/Core/Content/font.spritefont

@@ -1,48 +1,48 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-This file contains an xml description of a font, and will be read by the XNA
-Framework Content Pipeline. Follow the comments to customize the appearance
-of the font in your game, and to change the characters which are available to draw
-with.
--->
-<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
-  <Asset Type="Graphics:FontDescription">
-
-    <!--
-    Modify this string to change the font that will be imported.
-    -->
-    <FontName>Segoe UI</FontName>
-
-    <!--
-    Size is a float value, measured in points. Modify this value to change
-    the size of the font.
-    -->
-    <Size>14</Size>
-
-    <!--
-    Spacing is a float value, measured in pixels. Modify this value to change
-    the amount of spacing in between characters.
-    -->
-    <Spacing>2</Spacing>
-
-    <!--
-    Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
-    and "Bold, Italic", and are case sensitive.
-    -->
-    <Style>Regular</Style>
-
-    <!--
-    CharacterRegions control what letters are available in the font. Every
-    character from Start to End will be built and made available for drawing. The
-    default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
-    character set. The characters are ordered according to the Unicode standard.
-    See the documentation for more information.
-    -->
-    <CharacterRegions>
-      <CharacterRegion>
-        <Start>&#32;</Start>
-        <End>&#126;</End>
-      </CharacterRegion>
-    </CharacterRegions>
-  </Asset>
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+This file contains an xml description of a font, and will be read by the XNA
+Framework Content Pipeline. Follow the comments to customize the appearance
+of the font in your game, and to change the characters which are available to draw
+with.
+-->
+<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
+  <Asset Type="Graphics:FontDescription">
+
+    <!--
+    Modify this string to change the font that will be imported.
+    -->
+    <FontName>Segoe UI</FontName>
+
+    <!--
+    Size is a float value, measured in points. Modify this value to change
+    the size of the font.
+    -->
+    <Size>14</Size>
+
+    <!--
+    Spacing is a float value, measured in pixels. Modify this value to change
+    the amount of spacing in between characters.
+    -->
+    <Spacing>2</Spacing>
+
+    <!--
+    Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
+    and "Bold, Italic", and are case sensitive.
+    -->
+    <Style>Regular</Style>
+
+    <!--
+    CharacterRegions control what letters are available in the font. Every
+    character from Start to End will be built and made available for drawing. The
+    default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
+    character set. The characters are ordered according to the Unicode standard.
+    See the documentation for more information.
+    -->
+    <CharacterRegions>
+      <CharacterRegion>
+        <Start>&#32;</Start>
+        <End>&#126;</End>
+      </CharacterRegion>
+    </CharacterRegions>
+  </Asset>
 </XnaContent>

+ 0 - 0
ParticleSample/Content/font.xnb → ParticleSample/Core/Content/font.xnb


+ 0 - 0
ParticleSample/Content/smoke.bmp → ParticleSample/Core/Content/smoke.bmp


+ 0 - 0
ParticleSample/Content/smoke.xnb → ParticleSample/Core/Content/smoke.xnb


+ 89 - 93
ParticleSample/ExplosionParticleSystem.cs → ParticleSample/Core/ExplosionParticleSystem.cs

@@ -1,93 +1,89 @@
-#region File Description
-//-----------------------------------------------------------------------------
-// ExplosionParticleSystem.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-#endregion
-
-#region Using Statements
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-#endregion
-
-namespace ParticleSample
-{
-    /// <summary>
-    /// ExplosionParticleSystem is a specialization of ParticleSystem which creates a
-    /// fiery explosion. It should be combined with ExplosionSmokeParticleSystem for
-    /// best effect.
-    /// </summary>
-    public class ExplosionParticleSystem : ParticleSystem
-    {
-        public ExplosionParticleSystem(ParticleSampleGame game, int howManyEffects)
-            : base(game, howManyEffects)
-        {
-        }
-
-        /// <summary>
-        /// Set up the constants that will give this particle system its behavior and
-        /// properties.
-        /// </summary>
-        protected override void InitializeConstants()
-        {
-            textureFilename = "explosion";
-
-            // high initial speed with lots of variance.  make the values closer
-            // together to have more consistently circular explosions.
-            minInitialSpeed = 40;
-            maxInitialSpeed = 500;
-
-            // doesn't matter what these values are set to, acceleration is tweaked in
-            // the override of InitializeParticle.
-            minAcceleration = 0;
-            maxAcceleration = 0;
-
-            // explosions should be relatively short lived
-            minLifetime = .5f;
-            maxLifetime = 1.0f;
-
-            minScale = .3f;
-            maxScale = 1.0f;
-
-            // we need to reduce the number of particles on Windows Phone in order to keep
-            // a good framerate
-#if WINDOWS_PHONE
-            minNumParticles = 10;
-            maxNumParticles = 12;
-#else
-            minNumParticles = 20;
-            maxNumParticles = 25;
-#endif
-
-            minRotationSpeed = -MathHelper.PiOver4;
-            maxRotationSpeed = MathHelper.PiOver4;
-
-            // additive blending is very good at creating fiery effects.
-			blendState = BlendState.Additive;
-
-            DrawOrder = AdditiveDrawOrder;
-        }
-
-        protected override void InitializeParticle(Particle p, Vector2 where)
-        {
-            base.InitializeParticle(p, where);
-            
-            // The base works fine except for acceleration. Explosions move outwards,
-            // then slow down and stop because of air resistance. Let's change
-            // acceleration so that when the particle is at max lifetime, the velocity
-            // will be zero.
-
-            // We'll use the equation vt = v0 + (a0 * t). (If you're not familar with
-            // this, it's one of the basic kinematics equations for constant
-            // acceleration, and basically says:
-            // velocity at time t = initial velocity + acceleration * t)
-            // We'll solve the equation for a0, using t = p.Lifetime and vt = 0.
-            p.Acceleration = -p.Velocity / p.Lifetime;
-        }
-    }
-}
+//-----------------------------------------------------------------------------
+// ExplosionParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace ParticleSample
+{
+    /// <summary>
+    /// ExplosionParticleSystem is a specialization of ParticleSystem which creates a
+    /// fiery explosion. It should be combined with ExplosionSmokeParticleSystem for
+    /// best effect.
+    /// </summary>
+    public class ExplosionParticleSystem : ParticleSystem
+    {
+        public ExplosionParticleSystem(ParticleSampleGame game, int howManyEffects)
+            : base(game, howManyEffects)
+        {
+        }
+
+        /// <summary>
+        /// Set up the constants that will give this particle system its behavior and
+        /// properties.
+        /// </summary>
+        protected override void InitializeConstants()
+        {
+            textureFilename = "explosion";
+
+            // high initial speed with lots of variance.  make the values closer
+            // together to have more consistently circular explosions.
+            minInitialSpeed = 40;
+            maxInitialSpeed = 500;
+
+            // doesn't matter what these values are set to, acceleration is tweaked in
+            // the override of InitializeParticle.
+            minAcceleration = 0;
+            maxAcceleration = 0;
+
+            // explosions should be relatively short lived
+            minLifetime = .5f;
+            maxLifetime = 1.0f;
+
+            minScale = .3f;
+            maxScale = 1.0f;
+
+            // we need to reduce the number of particles on Windows Phone in order to keep
+            // a good framerate
+#if WINDOWS_PHONE
+            minNumParticles = 10;
+            maxNumParticles = 12;
+#else
+            minNumParticles = 20;
+            maxNumParticles = 25;
+#endif
+
+            minRotationSpeed = -MathHelper.PiOver4;
+            maxRotationSpeed = MathHelper.PiOver4;
+
+            // additive blending is very good at creating fiery effects.
+			blendState = BlendState.Additive;
+
+            DrawOrder = AdditiveDrawOrder;
+        }
+
+        protected override void InitializeParticle(Particle p, Vector2 where)
+        {
+            base.InitializeParticle(p, where);
+            
+            // The base works fine except for acceleration. Explosions move outwards,
+            // then slow down and stop because of air resistance. Let's change
+            // acceleration so that when the particle is at max lifetime, the velocity
+            // will be zero.
+
+            // We'll use the equation vt = v0 + (a0 * t). (If you're not familar with
+            // this, it's one of the basic kinematics equations for constant
+            // acceleration, and basically says:
+            // velocity at time t = initial velocity + acceleration * t)
+            // We'll solve the equation for a0, using t = p.Lifetime and vt = 0.
+            p.Acceleration = -p.Velocity / p.Lifetime;
+        }
+    }
+}

+ 75 - 79
ParticleSample/ExplosionSmokeParticleSystem.cs → ParticleSample/Core/ExplosionSmokeParticleSystem.cs

@@ -1,79 +1,75 @@
-#region File Description
-//-----------------------------------------------------------------------------
-// ExplosionSmokeParticleSystem.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-#endregion
-
-#region Using Statements
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-#endregion
-
-
-namespace ParticleSample
-{
-    /// <summary>
-    /// ExplosionSmokeParticleSystem is a specialization of ParticleSystem which
-    /// creates a circular pattern of smoke. It should be combined with
-    /// ExplosionParticleSystem for best effect.
-    /// </summary>
-    public class ExplosionSmokeParticleSystem : ParticleSystem
-    {
-        public ExplosionSmokeParticleSystem(ParticleSampleGame game, int howManyEffects)
-            : base(game, howManyEffects)
-        {            
-        }
-
-        /// <summary>
-        /// Set up the constants that will give this particle system its behavior and
-        /// properties.
-        /// </summary>
-        protected override void InitializeConstants()
-        {
-            textureFilename = "smoke";
-
-            // less initial speed than the explosion itself
-            minInitialSpeed = 20;
-            maxInitialSpeed = 200;
-
-            // acceleration is negative, so particles will accelerate away from the
-            // initial velocity.  this will make them slow down, as if from wind
-            // resistance. we want the smoke to linger a bit and feel wispy, though,
-            // so we don't stop them completely like we do ExplosionParticleSystem
-            // particles.
-            minAcceleration = -10;
-            maxAcceleration = -50;
-
-            // explosion smoke lasts for longer than the explosion itself, but not
-            // as long as the plumes do.
-            minLifetime = 1.0f;
-            maxLifetime = 2.5f;
-
-            minScale = 1.0f;
-            maxScale = 2.0f;
-
-            // we need to reduce the number of particles on Windows Phone in order to keep
-            // a good framerate
-#if WINDOWS_PHONE
-            minNumParticles = 5;
-            maxNumParticles = 10;
-#else
-            minNumParticles = 10;
-            maxNumParticles = 20;
-#endif
-
-            minRotationSpeed = -MathHelper.PiOver4;
-            maxRotationSpeed = MathHelper.PiOver4;
-
-			blendState = BlendState.AlphaBlend;
-
-            DrawOrder = AlphaBlendDrawOrder;
-        }
-    }
-}
+//-----------------------------------------------------------------------------
+// ExplosionSmokeParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+
+namespace ParticleSample
+{
+    /// <summary>
+    /// ExplosionSmokeParticleSystem is a specialization of ParticleSystem which
+    /// creates a circular pattern of smoke. It should be combined with
+    /// ExplosionParticleSystem for best effect.
+    /// </summary>
+    public class ExplosionSmokeParticleSystem : ParticleSystem
+    {
+        public ExplosionSmokeParticleSystem(ParticleSampleGame game, int howManyEffects)
+            : base(game, howManyEffects)
+        {            
+        }
+
+        /// <summary>
+        /// Set up the constants that will give this particle system its behavior and
+        /// properties.
+        /// </summary>
+        protected override void InitializeConstants()
+        {
+            textureFilename = "smoke";
+
+            // less initial speed than the explosion itself
+            minInitialSpeed = 20;
+            maxInitialSpeed = 200;
+
+            // acceleration is negative, so particles will accelerate away from the
+            // initial velocity.  this will make them slow down, as if from wind
+            // resistance. we want the smoke to linger a bit and feel wispy, though,
+            // so we don't stop them completely like we do ExplosionParticleSystem
+            // particles.
+            minAcceleration = -10;
+            maxAcceleration = -50;
+
+            // explosion smoke lasts for longer than the explosion itself, but not
+            // as long as the plumes do.
+            minLifetime = 1.0f;
+            maxLifetime = 2.5f;
+
+            minScale = 1.0f;
+            maxScale = 2.0f;
+
+            // we need to reduce the number of particles on Windows Phone in order to keep
+            // a good framerate
+#if WINDOWS_PHONE
+            minNumParticles = 5;
+            maxNumParticles = 10;
+#else
+            minNumParticles = 10;
+            maxNumParticles = 20;
+#endif
+
+            minRotationSpeed = -MathHelper.PiOver4;
+            maxRotationSpeed = MathHelper.PiOver4;
+
+			blendState = BlendState.AlphaBlend;
+
+            DrawOrder = AlphaBlendDrawOrder;
+        }
+    }
+}

+ 112 - 116
ParticleSample/Particle.cs → ParticleSample/Core/Particle.cs

@@ -1,116 +1,112 @@
-#region File Description
-//-----------------------------------------------------------------------------
-// Particle.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-#endregion
-
-#region Using Statements
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-#endregion
-
-namespace ParticleSample
-{
-    /// <summary>
-    /// particles are the little bits that will make up an effect. each effect will
-    /// be comprised of many of these particles. They have basic physical properties,
-    /// such as position, velocity, acceleration, and rotation. They'll be drawn as
-    /// sprites, all layered on top of one another, and will be very pretty.
-    /// </summary>
-    public class Particle
-    {
-        // Position, Velocity, and Acceleration represent exactly what their names
-        // indicate. They are public fields rather than properties so that users
-        // can directly access their .X and .Y properties.
-        public Vector2 Position;
-        public Vector2 Velocity;
-        public Vector2 Acceleration;
-
-        // how long this particle will "live"
-        private float lifetime;
-        public float Lifetime
-        {
-            get { return lifetime; }
-            set { lifetime = value; }
-        }
-
-        // how long it has been since initialize was called
-        private float timeSinceStart;
-        public float TimeSinceStart
-        {
-            get { return timeSinceStart; }
-            set { timeSinceStart = value; }
-        }
-
-        // the scale of this particle
-        private float scale;
-        public float Scale
-        {
-            get { return scale; }
-            set { scale = value; }
-        }
-
-        // its rotation, in radians
-        private float rotation;
-        public float Rotation
-        {
-            get { return rotation; }
-            set { rotation = value; }
-        }
-
-        // how fast does it rotate?
-        private float rotationSpeed;
-        public float RotationSpeed
-        {
-            get { return rotationSpeed; }
-            set { rotationSpeed = value; }
-        }
-
-        // is this particle still alive? once TimeSinceStart becomes greater than
-        // Lifetime, the particle should no longer be drawn or updated.
-        public bool Active
-        {
-            get { return TimeSinceStart < Lifetime; }
-        }
-
-        
-        // initialize is called by ParticleSystem to set up the particle, and prepares
-        // the particle for use.
-        public void Initialize(Vector2 position, Vector2 velocity, Vector2 acceleration,
-            float lifetime, float scale, float rotationSpeed)
-        {
-            // set the values to the requested values
-            this.Position = position;
-            this.Velocity = velocity;
-            this.Acceleration = acceleration;
-            this.Lifetime = lifetime;
-            this.Scale = scale;
-            this.RotationSpeed = rotationSpeed;
-            
-            // reset TimeSinceStart - we have to do this because particles will be
-            // reused.
-            this.TimeSinceStart = 0.0f;
-
-            // set rotation to some random value between 0 and 360 degrees.
-            this.Rotation = ParticleSampleGame.RandomBetween(0, MathHelper.TwoPi);
-        }
-
-        // update is called by the ParticleSystem on every frame. This is where the
-        // particle's position and that kind of thing get updated.
-        public void Update(float dt)
-        {
-            Velocity += Acceleration * dt;
-            Position += Velocity * dt;
-
-            Rotation += RotationSpeed * dt;
-
-            TimeSinceStart += dt;
-        }
-    }
-}
+//-----------------------------------------------------------------------------
+// Particle.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace ParticleSample
+{
+    /// <summary>
+    /// particles are the little bits that will make up an effect. each effect will
+    /// be comprised of many of these particles. They have basic physical properties,
+    /// such as position, velocity, acceleration, and rotation. They'll be drawn as
+    /// sprites, all layered on top of one another, and will be very pretty.
+    /// </summary>
+    public class Particle
+    {
+        // Position, Velocity, and Acceleration represent exactly what their names
+        // indicate. They are public fields rather than properties so that users
+        // can directly access their .X and .Y properties.
+        public Vector2 Position;
+        public Vector2 Velocity;
+        public Vector2 Acceleration;
+
+        // how long this particle will "live"
+        private float lifetime;
+        public float Lifetime
+        {
+            get { return lifetime; }
+            set { lifetime = value; }
+        }
+
+        // how long it has been since initialize was called
+        private float timeSinceStart;
+        public float TimeSinceStart
+        {
+            get { return timeSinceStart; }
+            set { timeSinceStart = value; }
+        }
+
+        // the scale of this particle
+        private float scale;
+        public float Scale
+        {
+            get { return scale; }
+            set { scale = value; }
+        }
+
+        // its rotation, in radians
+        private float rotation;
+        public float Rotation
+        {
+            get { return rotation; }
+            set { rotation = value; }
+        }
+
+        // how fast does it rotate?
+        private float rotationSpeed;
+        public float RotationSpeed
+        {
+            get { return rotationSpeed; }
+            set { rotationSpeed = value; }
+        }
+
+        // is this particle still alive? once TimeSinceStart becomes greater than
+        // Lifetime, the particle should no longer be drawn or updated.
+        public bool Active
+        {
+            get { return TimeSinceStart < Lifetime; }
+        }
+
+        
+        // initialize is called by ParticleSystem to set up the particle, and prepares
+        // the particle for use.
+        public void Initialize(Vector2 position, Vector2 velocity, Vector2 acceleration,
+            float lifetime, float scale, float rotationSpeed)
+        {
+            // set the values to the requested values
+            this.Position = position;
+            this.Velocity = velocity;
+            this.Acceleration = acceleration;
+            this.Lifetime = lifetime;
+            this.Scale = scale;
+            this.RotationSpeed = rotationSpeed;
+            
+            // reset TimeSinceStart - we have to do this because particles will be
+            // reused.
+            this.TimeSinceStart = 0.0f;
+
+            // set rotation to some random value between 0 and 360 degrees.
+            this.Rotation = ParticleSampleGame.RandomBetween(0, MathHelper.TwoPi);
+        }
+
+        // update is called by the ParticleSystem on every frame. This is where the
+        // particle's position and that kind of thing get updated.
+        public void Update(float dt)
+        {
+            Velocity += Acceleration * dt;
+            Position += Velocity * dt;
+
+            Rotation += RotationSpeed * dt;
+
+            TimeSinceStart += dt;
+        }
+    }
+}

+ 9 - 0
ParticleSample/Core/ParticleSample.Core.csproj

@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <RootNamespace>ParticleSample.Core</RootNamespace>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*" />
+  </ItemGroup>
+</Project>

+ 299 - 311
ParticleSample/ParticleSampleGame.cs → ParticleSample/Core/ParticleSampleGame.cs

@@ -1,311 +1,299 @@
-#region File Description
-//-----------------------------------------------------------------------------
-// ParticleSampleGame.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-#endregion
-
-#region Using Statements
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-using Microsoft.Xna.Framework.Content;
-using Microsoft.Xna.Framework.Input;
-using Microsoft.Xna.Framework.Input.Touch;
-#endregion
-
-namespace ParticleSample
-{
-    /// <summary>
-    /// This is the main type for the ParticleSample, and inherits from the Framework's
-    /// Game class. It creates three different kinds of ParticleSystems, and then adds
-    /// them to its components collection. It also has keeps a random number generator,
-    /// a SpriteBatch, and a ContentManager that the different classes in this sample
-    /// can share.
-    /// </summary>
-    public class ParticleSampleGame : Microsoft.Xna.Framework.Game
-    {
-        #region Fields and Properties
-
-        GraphicsDeviceManager graphics;
-
-        // The particle systems will all need a SpriteBatch to draw their particles,
-        // so let's make one they can share. We'll use this to draw our SpriteFont
-        // too.
-        SpriteBatch spriteBatch;
-        public SpriteBatch SpriteBatch
-        {
-            get { return spriteBatch; }
-        }
-
-        // Used to draw the instructions on the screen.
-        SpriteFont font;
-        
-        // a random number generator that the whole sample can share.
-        private static Random random = new Random();
-        public static Random Random
-        {
-            get { return random; }
-        }
-
-        // Here's the really fun part of the sample, the particle systems! These are
-        // drawable game components, so we can just add them to the components
-        // collection. Read more about each particle system in their respective source
-        // files.
-        ExplosionParticleSystem explosion;
-        ExplosionSmokeParticleSystem smoke;
-        SmokePlumeParticleSystem smokePlume;
-
-        // State is an enum that represents which effect we're currently demoing.
-        enum State
-        {
-            Explosions,
-            SmokePlume
-        };
-        // the number of values in the "State" enum.
-        const int NumStates = 2;
-        State currentState = State.Explosions;
-
-        // a timer that will tell us when it's time to trigger another explosion.
-        const float TimeBetweenExplosions = 2.0f;
-        float timeTillExplosion = 0.0f;
-
-        // keep a timer that will tell us when it's time to add more particles to the
-        // smoke plume.
-        const float TimeBetweenSmokePlumePuffs = .5f;
-        float timeTillPuff = 0.0f;
-
-        // keep track of the last frame's keyboard and gamepad state, so that we know
-        // if the user has pressed a button.
-        KeyboardState lastKeyboardState;
-        GamePadState lastGamepadState;
-
-        #endregion
-
-        #region Initialization
-
-        public ParticleSampleGame()
-        {
-            graphics = new GraphicsDeviceManager(this);
-
-#if WINDOWS_PHONE
-			graphics.IsFullScreen = true;
-
-            // Frame rate is 30 fps by default for Windows Phone.
-            TargetElapsedTime = TimeSpan.FromTicks(333333);
-#endif
-
-            Content.RootDirectory = "Content";
-
-            // create the particle systems and add them to the components list.
-            // we should never see more than one explosion at once
-            explosion = new ExplosionParticleSystem(this, 1);
-            Components.Add(explosion);
-
-            // but the smoke from the explosion lingers a while.
-            smoke = new ExplosionSmokeParticleSystem(this, 2);
-            Components.Add(smoke);
-
-            // we'll see lots of these effects at once; this is ok
-            // because they have a fairly small number of particles per effect.
-            smokePlume = new SmokePlumeParticleSystem(this, 9);
-            Components.Add(smokePlume);
-
-			// enable the tap gesture for changing particle effects
-			TouchPanel.EnabledGestures = GestureType.Tap;
-        }
-
-        /// <summary>
-        /// Load your graphics content. 
-        /// </summary>
-        protected override void LoadContent()
-        {
-            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
-            font = Content.Load<SpriteFont>("font");
-        }
-
-        #endregion
-
-        #region Update and Draw
-
-        /// <summary>
-        /// Allows the game to run logic such as updating the world,
-        /// checking for collisions, gathering input and playing audio.
-        /// </summary>
-        /// <param name="gameTime">Provides a snapshot of timing values.</param>
-        protected override void Update(GameTime gameTime)
-        {
-            // check the input devices to see if someone has decided they want to see
-            // the other effect, if they want to quit.
-            HandleInput();
-
-            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
-            switch (currentState)
-            {
-                // if we should be demoing the explosions effect, check to see if it's
-                // time for a new explosion.
-                case State.Explosions:
-                    UpdateExplosions(dt);
-                    break;
-                // if we're showing off the smoke plume, check to see if it's time for a
-                // new puff of smoke.
-                case State.SmokePlume:
-                    UpdateSmokePlume(dt);
-                    break;
-            }
-
-            // the base update will handle updating the particle systems themselves,
-            // because we added them to the components collection.
-            base.Update(gameTime);
-        }
-        
-        // this function is called when we want to demo the smoke plume effect. it
-        // updates the timeTillPuff timer, and adds more particles to the plume when
-        // necessary.
-        private void UpdateSmokePlume(float dt)
-        {
-            timeTillPuff -= dt;
-            if (timeTillPuff < 0)
-            {
-                Vector2 where = Vector2.Zero;
-                // add more particles at the bottom of the screen, halfway across.
-                where.X = graphics.GraphicsDevice.Viewport.Width / 2;
-                where.Y = graphics.GraphicsDevice.Viewport.Height;
-                smokePlume.AddParticles(where);
-
-                // and then reset the timer.
-                timeTillPuff = TimeBetweenSmokePlumePuffs;
-            }
-        }
-
-        // this function is called when we want to demo the explosion effect. it
-        // updates the timeTillExplosion timer, and starts another explosion effect
-        // when the timer reaches zero.
-        private void UpdateExplosions(float dt)
-        {
-            timeTillExplosion -= dt;
-            if (timeTillExplosion < 0)
-            {
-                Vector2 where = Vector2.Zero;
-                // create the explosion at some random point on the screen.
-                where.X = RandomBetween(0, graphics.GraphicsDevice.Viewport.Width);
-                where.Y = RandomBetween(0, graphics.GraphicsDevice.Viewport.Height);
-
-                // the overall explosion effect is actually comprised of two particle
-                // systems: the fiery bit, and the smoke behind it. add particles to
-                // both of those systems.
-                explosion.AddParticles(where);
-                smoke.AddParticles(where);
-
-                // reset the timer.
-                timeTillExplosion = TimeBetweenExplosions;
-            }
-        }
-
-        /// <summary>
-        /// This is called when the game should draw itself.
-        /// </summary>
-        /// <param name="gameTime">Provides a snapshot of timing values.</param>
-        protected override void Draw(GameTime gameTime)
-        {
-            graphics.GraphicsDevice.Clear(Color.Black);
-
-            spriteBatch.Begin();
-
-            // draw some instructions on the screen
-            string message = string.Format("Current effect: {0}!\n" + 
-                "Hit the A button or space bar, or tap the screen, to switch.\n" +
-				"Hit the F key to toggle full screen.\n\n" +
-                "Free particles:\n" +
-                "    ExplosionParticleSystem:      {1}\n" +
-                "    ExplosionSmokeParticleSystem: {2}\n" +
-                "    SmokePlumeParticleSystem:     {3}",
-                currentState, explosion.FreeParticleCount,
-                smoke.FreeParticleCount, smokePlume.FreeParticleCount );
-            spriteBatch.DrawString(font, message, new Vector2(50, 50), Color.White);
-
-            spriteBatch.End();
-
-            base.Draw(gameTime);
-        }
-
-        // This function will check to see if the user has just pushed the A button or
-        // the space bar. If so, we should go to the next effect.
-        private void HandleInput()
-        {
-            KeyboardState currentKeyboardState = Keyboard.GetState();
-            GamePadState currentGamePadState = GamePad.GetState(PlayerIndex.One);
-
-            // Allows the game to exit
-            if (currentGamePadState.Buttons.Back == ButtonState.Pressed || 
-                currentKeyboardState.IsKeyDown(Keys.Escape))
-                this.Exit();
-
-
-            // check to see if someone has just released the space bar.            
-            bool keyboardSpace =
-                currentKeyboardState.IsKeyUp(Keys.Space) &&
-                lastKeyboardState.IsKeyDown(Keys.Space);
-
-            // check to see if someone has just released the 'F' key.
-            bool keyboardF =
-                currentKeyboardState.IsKeyUp(Keys.F) &&
-                lastKeyboardState.IsKeyDown(Keys.F);
-
-            // check the gamepad to see if someone has just released the A button.
-            bool gamepadA =
-                currentGamePadState.Buttons.A == ButtonState.Pressed &&
-                lastGamepadState.Buttons.A == ButtonState.Released;
-
-			// check our gestures to see if someone has tapped the screen. we want
-			// to read all available gestures even if a tap occurred so we clear
-			// the queue.
-			bool tapGesture = false;
-			while (TouchPanel.IsGestureAvailable)
-			{
-				GestureSample sample = TouchPanel.ReadGesture();
-				if (sample.GestureType == GestureType.Tap)
-				{
-					tapGesture = true;
-				}
-			}
-            
-
-            // if either the A button or the space bar was just released, or the screen
-			// was tapped, move to the next state. Doing modulus by the number of 
-			// states lets us wrap back around to the first state.
-            if (keyboardSpace || gamepadA || tapGesture)
-            {
-                currentState = (State)((int)(currentState + 1) % NumStates);
-            }
-
-			if (keyboardF)
-			{
-				graphics.ToggleFullScreen();
-				//Window.Window.IsVisible = true;
-				//Window.Window.MakeKeyAndOrderFront(Window);
-			}
-
-            lastKeyboardState = currentKeyboardState;
-            lastGamepadState = currentGamePadState;
-        }
-
-        #endregion
-
-        #region Helper Functions
-
-        //  a handy little function that gives a random float between two
-        // values. This will be used in several places in the sample, in particilar in
-        // ParticleSystem.InitializeParticle.
-        public static float RandomBetween(float min, float max)
-        {
-            return min + (float)random.NextDouble() * (max - min);
-        }
-
-        #endregion
-    }
-}
+//-----------------------------------------------------------------------------
+// ParticleSampleGame.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Input.Touch;
+
+namespace ParticleSample
+{
+    /// <summary>
+    /// This is the main type for the ParticleSample, and inherits from the Framework's
+    /// Game class. It creates three different kinds of ParticleSystems, and then adds
+    /// them to its components collection. It also has keeps a random number generator,
+    /// a SpriteBatch, and a ContentManager that the different classes in this sample
+    /// can share.
+    /// </summary>
+    public class ParticleSampleGame : Microsoft.Xna.Framework.Game
+    {
+
+        GraphicsDeviceManager graphics;
+
+        // The particle systems will all need a SpriteBatch to draw their particles,
+        // so let's make one they can share. We'll use this to draw our SpriteFont
+        // too.
+        SpriteBatch spriteBatch;
+        public SpriteBatch SpriteBatch
+        {
+            get { return spriteBatch; }
+        }
+
+        // Used to draw the instructions on the screen.
+        SpriteFont font;
+        
+        // a random number generator that the whole sample can share.
+        private static Random random = new Random();
+        public static Random Random
+        {
+            get { return random; }
+        }
+
+        // Here's the really fun part of the sample, the particle systems! These are
+        // drawable game components, so we can just add them to the components
+        // collection. Read more about each particle system in their respective source
+        // files.
+        ExplosionParticleSystem explosion;
+        ExplosionSmokeParticleSystem smoke;
+        SmokePlumeParticleSystem smokePlume;
+
+        // State is an enum that represents which effect we're currently demoing.
+        enum State
+        {
+            Explosions,
+            SmokePlume
+        };
+        // the number of values in the "State" enum.
+        const int NumStates = 2;
+        State currentState = State.Explosions;
+
+        // a timer that will tell us when it's time to trigger another explosion.
+        const float TimeBetweenExplosions = 2.0f;
+        float timeTillExplosion = 0.0f;
+
+        // keep a timer that will tell us when it's time to add more particles to the
+        // smoke plume.
+        const float TimeBetweenSmokePlumePuffs = .5f;
+        float timeTillPuff = 0.0f;
+
+        // keep track of the last frame's keyboard and gamepad state, so that we know
+        // if the user has pressed a button.
+        KeyboardState lastKeyboardState;
+        GamePadState lastGamepadState;
+
+
+
+        public ParticleSampleGame()
+        {
+            graphics = new GraphicsDeviceManager(this);
+
+#if WINDOWS_PHONE
+            graphics.IsFullScreen = false;
+
+            // Frame rate is 30 fps by default for Windows Phone.
+            TargetElapsedTime = TimeSpan.FromTicks(333333);
+#endif
+
+            Content.RootDirectory = "Content";
+
+            // create the particle systems and add them to the components list.
+            // we should never see more than one explosion at once
+            explosion = new ExplosionParticleSystem(this, 1);
+            Components.Add(explosion);
+
+            // but the smoke from the explosion lingers a while.
+            smoke = new ExplosionSmokeParticleSystem(this, 2);
+            Components.Add(smoke);
+
+            // we'll see lots of these effects at once; this is ok
+            // because they have a fairly small number of particles per effect.
+            smokePlume = new SmokePlumeParticleSystem(this, 9);
+            Components.Add(smokePlume);
+
+            // enable the tap gesture for changing particle effects
+            TouchPanel.EnabledGestures = GestureType.Tap;
+        }
+
+        /// <summary>
+        /// Load your graphics content. 
+        /// </summary>
+        protected override void LoadContent()
+        {
+            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
+            font = Content.Load<SpriteFont>("font");
+        }
+
+
+
+        /// <summary>
+        /// Allows the game to run logic such as updating the world,
+        /// checking for collisions, gathering input and playing audio.
+        /// </summary>
+        /// <param name="gameTime">Provides a snapshot of timing values.</param>
+        protected override void Update(GameTime gameTime)
+        {
+            // check the input devices to see if someone has decided they want to see
+            // the other effect, if they want to quit.
+            HandleInput();
+
+            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
+            switch (currentState)
+            {
+                // if we should be demoing the explosions effect, check to see if it's
+                // time for a new explosion.
+                case State.Explosions:
+                    UpdateExplosions(dt);
+                    break;
+                // if we're showing off the smoke plume, check to see if it's time for a
+                // new puff of smoke.
+                case State.SmokePlume:
+                    UpdateSmokePlume(dt);
+                    break;
+            }
+
+            // the base update will handle updating the particle systems themselves,
+            // because we added them to the components collection.
+            base.Update(gameTime);
+        }
+        
+        // this function is called when we want to demo the smoke plume effect. it
+        // updates the timeTillPuff timer, and adds more particles to the plume when
+        // necessary.
+        private void UpdateSmokePlume(float dt)
+        {
+            timeTillPuff -= dt;
+            if (timeTillPuff < 0)
+            {
+                Vector2 where = Vector2.Zero;
+                // add more particles at the bottom of the screen, halfway across.
+                where.X = graphics.GraphicsDevice.Viewport.Width / 2;
+                where.Y = graphics.GraphicsDevice.Viewport.Height;
+                smokePlume.AddParticles(where);
+
+                // and then reset the timer.
+                timeTillPuff = TimeBetweenSmokePlumePuffs;
+            }
+        }
+
+        // this function is called when we want to demo the explosion effect. it
+        // updates the timeTillExplosion timer, and starts another explosion effect
+        // when the timer reaches zero.
+        private void UpdateExplosions(float dt)
+        {
+            timeTillExplosion -= dt;
+            if (timeTillExplosion < 0)
+            {
+                Vector2 where = Vector2.Zero;
+                // create the explosion at some random point on the screen.
+                where.X = RandomBetween(0, graphics.GraphicsDevice.Viewport.Width);
+                where.Y = RandomBetween(0, graphics.GraphicsDevice.Viewport.Height);
+
+                // the overall explosion effect is actually comprised of two particle
+                // systems: the fiery bit, and the smoke behind it. add particles to
+                // both of those systems.
+                explosion.AddParticles(where);
+                smoke.AddParticles(where);
+
+                // reset the timer.
+                timeTillExplosion = TimeBetweenExplosions;
+            }
+        }
+
+        /// <summary>
+        /// This is called when the game should draw itself.
+        /// </summary>
+        /// <param name="gameTime">Provides a snapshot of timing values.</param>
+        protected override void Draw(GameTime gameTime)
+        {
+            graphics.GraphicsDevice.Clear(Color.Black);
+
+            spriteBatch.Begin();
+
+            // draw some instructions on the screen
+            string message = string.Format("Current effect: {0}!\n" + 
+                "Hit the A button or space bar, or tap the screen, to switch.\n" +
+                "Hit the F key to toggle full screen.\n\n" +
+                "Free particles:\n" +
+                "    ExplosionParticleSystem:      {1}\n" +
+                "    ExplosionSmokeParticleSystem: {2}\n" +
+                "    SmokePlumeParticleSystem:     {3}",
+                currentState, explosion.FreeParticleCount,
+                smoke.FreeParticleCount, smokePlume.FreeParticleCount );
+            spriteBatch.DrawString(font, message, new Vector2(50, 50), Color.White);
+
+            spriteBatch.End();
+
+            base.Draw(gameTime);
+        }
+
+        // This function will check to see if the user has just pushed the A button or
+        // the space bar. If so, we should go to the next effect.
+        private void HandleInput()
+        {
+            KeyboardState currentKeyboardState = Keyboard.GetState();
+            GamePadState currentGamePadState = GamePad.GetState(PlayerIndex.One);
+
+            // Allows the game to exit
+            if (currentGamePadState.Buttons.Back == ButtonState.Pressed || 
+                currentKeyboardState.IsKeyDown(Keys.Escape))
+                this.Exit();
+
+
+            // check to see if someone has just released the space bar.            
+            bool keyboardSpace =
+                currentKeyboardState.IsKeyUp(Keys.Space) &&
+                lastKeyboardState.IsKeyDown(Keys.Space);
+
+            // check to see if someone has just released the 'F' key.
+            bool keyboardF =
+                currentKeyboardState.IsKeyUp(Keys.F) &&
+                lastKeyboardState.IsKeyDown(Keys.F);
+
+            // check the gamepad to see if someone has just released the A button.
+            bool gamepadA =
+                currentGamePadState.Buttons.A == ButtonState.Pressed &&
+                lastGamepadState.Buttons.A == ButtonState.Released;
+
+            // check our gestures to see if someone has tapped the screen. we want
+            // to read all available gestures even if a tap occurred so we clear
+            // the queue.
+            bool tapGesture = false;
+            while (TouchPanel.IsGestureAvailable)
+            {
+                GestureSample sample = TouchPanel.ReadGesture();
+                if (sample.GestureType == GestureType.Tap)
+                {
+                    tapGesture = true;
+                }
+            }
+            
+
+            // if either the A button or the space bar was just released, or the screen
+            // was tapped, move to the next state. Doing modulus by the number of 
+            // states lets us wrap back around to the first state.
+            if (keyboardSpace || gamepadA || tapGesture)
+            {
+                currentState = (State)((int)(currentState + 1) % NumStates);
+            }
+
+            if (keyboardF)
+            {
+                graphics.ToggleFullScreen();
+                //Window.Window.IsVisible = true;
+                //Window.Window.MakeKeyAndOrderFront(Window);
+            }
+
+            lastKeyboardState = currentKeyboardState;
+            lastGamepadState = currentGamePadState;
+        }
+
+
+
+        //  a handy little function that gives a random float between two
+        // values. This will be used in several places in the sample, in particilar in
+        // ParticleSystem.InitializeParticle.
+        public static float RandomBetween(float min, float max)
+        {
+            return min + (float)random.NextDouble() * (max - min);
+        }
+
+    }
+}

+ 362 - 368
ParticleSample/ParticleSystem.cs → ParticleSample/Core/ParticleSystem.cs

@@ -1,368 +1,362 @@
-#region File Description
-//-----------------------------------------------------------------------------
-// SmokePlumeParticleSystem.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-#endregion
-
-#region Using Statements
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-#endregion
-
-namespace ParticleSample
-{
-    /// <summary>
-    /// ParticleSystem is an abstract class that provides the basic functionality to
-    /// create a particle effect. Different subclasses will have different effects,
-    /// such as fire, explosions, and plumes of smoke. To use these subclasses, 
-    /// simply call AddParticles, and pass in where the particles should exist
-    /// </summary>
-    public abstract class ParticleSystem : DrawableGameComponent
-    {
-        // these two values control the order that particle systems are drawn in.
-        // typically, particles that use additive blending should be drawn on top of
-        // particles that use regular alpha blending. ParticleSystems should therefore
-        // set their DrawOrder to the appropriate value in InitializeConstants, though
-        // it is possible to use other values for more advanced effects.
-        public const int AlphaBlendDrawOrder = 100;
-        public const int AdditiveDrawOrder = 200;
-
-        // a reference to the main game; we'll keep this around because it exposes a
-        // content manager and a sprite batch for us to use.
-        private ParticleSampleGame game;
-
-        // the texture this particle system will use.
-        private Texture2D texture;
-
-        // the origin when we're drawing textures. this will be the middle of the
-        // texture.
-        private Vector2 origin;
-
-        // this number represents the maximum number of effects this particle system
-        // will be expected to draw at one time. this is set in the constructor and is
-        // used to calculate how many particles we will need.
-        private int howManyEffects;
-        
-        // the array of particles used by this system. these are reused, so that calling
-        // AddParticles will not cause any allocations.
-        Particle[] particles;
-
-        // the queue of free particles keeps track of particles that are not curently
-        // being used by an effect. when a new effect is requested, particles are taken
-        // from this queue. when particles are finished they are put onto this queue.
-        Queue<Particle> freeParticles;
-        /// <summary>
-        /// returns the number of particles that are available for a new effect.
-        /// </summary>
-        public int FreeParticleCount
-        {
-            get { return freeParticles.Count; }
-        }
-
-
-        // This region of values control the "look" of the particle system, and should 
-        // be set by deriving particle systems in the InitializeConstants method. The
-        // values are then used by the virtual function InitializeParticle. Subclasses
-        // can override InitializeParticle for further
-        // customization.
-        #region constants to be set by subclasses
-
-        /// <summary>
-        /// minNumParticles and maxNumParticles control the number of particles that are
-        /// added when AddParticles is called. The number of particles will be a random
-        /// number between minNumParticles and maxNumParticles.
-        /// </summary>
-        protected int minNumParticles;
-        protected int maxNumParticles;
-       
-        /// <summary>
-        /// this controls the texture that the particle system uses. It will be used as
-        /// an argument to ContentManager.Load.
-        /// </summary>
-        protected string textureFilename;
-
-        /// <summary>
-        /// minInitialSpeed and maxInitialSpeed are used to control the initial velocity
-        /// of the particles. The particle's initial speed will be a random number 
-        /// between these two. The direction is determined by the function 
-        /// PickRandomDirection, which can be overriden.
-        /// </summary>
-        protected float minInitialSpeed;
-        protected float maxInitialSpeed;
-
-        /// <summary>
-        /// minAcceleration and maxAcceleration are used to control the acceleration of
-        /// the particles. The particle's acceleration will be a random number between
-        /// these two. By default, the direction of acceleration is the same as the
-        /// direction of the initial velocity.
-        /// </summary>
-        protected float minAcceleration;
-        protected float maxAcceleration;
-
-        /// <summary>
-        /// minRotationSpeed and maxRotationSpeed control the particles' angular
-        /// velocity: the speed at which particles will rotate. Each particle's rotation
-        /// speed will be a random number between minRotationSpeed and maxRotationSpeed.
-        /// Use smaller numbers to make particle systems look calm and wispy, and large 
-        /// numbers for more violent effects.
-        /// </summary>
-        protected float minRotationSpeed;
-        protected float maxRotationSpeed;
-
-        /// <summary>
-        /// minLifetime and maxLifetime are used to control the lifetime. Each
-        /// particle's lifetime will be a random number between these two. Lifetime
-        /// is used to determine how long a particle "lasts." Also, in the base
-        /// implementation of Draw, lifetime is also used to calculate alpha and scale
-        /// values to avoid particles suddenly "popping" into view
-        /// </summary>
-        protected float minLifetime;
-        protected float maxLifetime;
-
-        /// <summary>
-        /// to get some additional variance in the appearance of the particles, we give
-        /// them all random scales. the scale is a value between minScale and maxScale,
-        /// and is additionally affected by the particle's lifetime to avoid particles
-        /// "popping" into view.
-        /// </summary>
-        protected float minScale;
-        protected float maxScale;
-
-        /// <summary>
-        /// different effects can use different blend states. fire and explosions work
-        /// well with additive blending, for example.
-        /// </summary>
-		protected BlendState blendState;
-
-        #endregion
-        
-        /// <summary>
-        /// Constructs a new ParticleSystem.
-        /// </summary>
-        /// <param name="game">The host for this particle system. The game keeps the 
-        /// content manager and sprite batch for us.</param>
-        /// <param name="howManyEffects">the maximum number of particle effects that
-        /// are expected on screen at once.</param>
-        /// <remarks>it is tempting to set the value of howManyEffects very high.
-        /// However, this value should be set to the minimum possible, because
-        /// it has a large impact on the amount of memory required, and slows down the
-        /// Update and Draw functions.</remarks>
-        protected ParticleSystem(ParticleSampleGame game, int howManyEffects)
-            : base(game)
-        {            
-            this.game = game;
-            this.howManyEffects = howManyEffects;
-        }
-
-        /// <summary>
-        /// override the base class's Initialize to do some additional work; we want to
-        /// call InitializeConstants to let subclasses set the constants that we'll use.
-        /// 
-        /// also, the particle array and freeParticles queue are set up here.
-        /// </summary>
-        public override void Initialize()
-        {
-            InitializeConstants();
-            
-            // calculate the total number of particles we will ever need, using the
-            // max number of effects and the max number of particles per effect.
-            // once these particles are allocated, they will be reused, so that
-            // we don't put any pressure on the garbage collector.
-            particles = new Particle[howManyEffects * maxNumParticles];
-            freeParticles = new Queue<Particle>(howManyEffects * maxNumParticles);
-            for (int i = 0; i < particles.Length; i++)
-            {
-                particles[i] = new Particle();
-                freeParticles.Enqueue(particles[i]);
-            }
-            base.Initialize();
-        }
-
-        /// <summary>
-        /// this abstract function must be overriden by subclasses of ParticleSystem.
-        /// It's here that they should set all the constants marked in the region
-        /// "constants to be set by subclasses", which give each ParticleSystem its
-        /// specific flavor.
-        /// </summary>
-        protected abstract void InitializeConstants();
-
-        /// <summary>
-        /// Override the base class LoadContent to load the texture. once it's
-        /// loaded, calculate the origin.
-        /// </summary>
-        protected override void LoadContent()
-        {
-            // make sure sub classes properly set textureFilename.
-            if (string.IsNullOrEmpty(textureFilename))
-            {
-                string message = "textureFilename wasn't set properly, so the " +
-                    "particle system doesn't know what texture to load. Make " +
-                    "sure your particle system's InitializeConstants function " +
-                    "properly sets textureFilename.";
-                throw new InvalidOperationException(message);
-            }
-            // load the texture....
-            texture = game.Content.Load<Texture2D>(textureFilename);
-
-            // ... and calculate the center. this'll be used in the draw call, we
-            // always want to rotate and scale around this point.
-            origin.X = texture.Width / 2;
-            origin.Y = texture.Height / 2;
-
-            base.LoadContent();
-        }
-
-        /// <summary>
-        /// AddParticles's job is to add an effect somewhere on the screen. If there 
-        /// aren't enough particles in the freeParticles queue, it will use as many as 
-        /// it can. This means that if there not enough particles available, calling
-        /// AddParticles will have no effect.
-        /// </summary>
-        /// <param name="where">where the particle effect should be created</param>
-        public void AddParticles(Vector2 where)
-        {
-            // the number of particles we want for this effect is a random number
-            // somewhere between the two constants specified by the subclasses.
-            int numParticles = 
-                ParticleSampleGame.Random.Next(minNumParticles, maxNumParticles);
-
-            // create that many particles, if you can.
-            for (int i = 0; i < numParticles && freeParticles.Count > 0; i++)
-            {
-                // grab a particle from the freeParticles queue, and Initialize it.
-                Particle p = freeParticles.Dequeue();
-                InitializeParticle(p, where);               
-            }
-        }
-
-        /// <summary>
-        /// InitializeParticle randomizes some properties for a particle, then
-        /// calls initialize on it. It can be overriden by subclasses if they 
-        /// want to modify the way particles are created. For example, 
-        /// SmokePlumeParticleSystem overrides this function make all particles
-        /// accelerate to the right, simulating wind.
-        /// </summary>
-        /// <param name="p">the particle to initialize</param>
-        /// <param name="where">the position on the screen that the particle should be
-        /// </param>
-        protected virtual void InitializeParticle(Particle p, Vector2 where)
-        {
-            // first, call PickRandomDirection to figure out which way the particle
-            // will be moving. velocity and acceleration's values will come from this.
-            Vector2 direction = PickRandomDirection();
-
-            // pick some random values for our particle
-            float velocity = 
-                ParticleSampleGame.RandomBetween(minInitialSpeed, maxInitialSpeed);
-            float acceleration = 
-                ParticleSampleGame.RandomBetween(minAcceleration, maxAcceleration);
-            float lifetime =
-                ParticleSampleGame.RandomBetween(minLifetime, maxLifetime);
-            float scale =
-                ParticleSampleGame.RandomBetween(minScale, maxScale);
-            float rotationSpeed =
-                ParticleSampleGame.RandomBetween(minRotationSpeed, maxRotationSpeed);
-
-            // then initialize it with those random values. initialize will save those,
-            // and make sure it is marked as active.
-            p.Initialize(
-                where, velocity * direction, acceleration * direction,
-                lifetime, scale, rotationSpeed);
-        }
-
-        /// <summary>
-        /// PickRandomDirection is used by InitializeParticles to decide which direction
-        /// particles will move. The default implementation is a random vector in a
-        /// circular pattern.
-        /// </summary>
-        protected virtual Vector2 PickRandomDirection()
-        {
-            float angle = ParticleSampleGame.RandomBetween(0, MathHelper.TwoPi);
-            return new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
-        }
-
-        /// <summary>
-        /// overriden from DrawableGameComponent, Update will update all of the active
-        /// particles.
-        /// </summary>
-        public override void Update(GameTime gameTime)
-        {
-            // calculate dt, the change in the since the last frame. the particle
-            // updates will use this value.
-            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
-
-            // go through all of the particles...
-            foreach (Particle p in particles)
-            {
-                
-                if (p.Active)
-                {
-                    // ... and if they're active, update them.
-                    p.Update(dt);
-                    // if that update finishes them, put them onto the free particles
-                    // queue.
-                    if (!p.Active)
-                    {
-                        freeParticles.Enqueue(p);
-                    }
-                }   
-            }
-
-            base.Update(gameTime);
-        }
-
-        /// <summary>
-        /// overriden from DrawableGameComponent, Draw will use ParticleSampleGame's 
-        /// sprite batch to render all of the active particles.
-        /// </summary>
-        public override void Draw(GameTime gameTime)
-        {
-            // tell sprite batch to begin, using the spriteBlendMode specified in
-            // initializeConstants
-			game.SpriteBatch.Begin(SpriteSortMode.Deferred, blendState);
-            
-            foreach (Particle p in particles)
-            {
-                // skip inactive particles
-                if (!p.Active)
-                    continue;
-
-                // normalized lifetime is a value from 0 to 1 and represents how far
-                // a particle is through its life. 0 means it just started, .5 is half
-                // way through, and 1.0 means it's just about to be finished.
-                // this value will be used to calculate alpha and scale, to avoid 
-                // having particles suddenly appear or disappear.
-                float normalizedLifetime = p.TimeSinceStart / p.Lifetime;
-
-                // we want particles to fade in and fade out, so we'll calculate alpha
-                // to be (normalizedLifetime) * (1-normalizedLifetime). this way, when
-                // normalizedLifetime is 0 or 1, alpha is 0. the maximum value is at
-                // normalizedLifetime = .5, and is
-                // (normalizedLifetime) * (1-normalizedLifetime)
-                // (.5)                 * (1-.5)
-                // .25
-                // since we want the maximum alpha to be 1, not .25, we'll scale the 
-                // entire equation by 4.
-                float alpha = 4 * normalizedLifetime * (1 - normalizedLifetime);
-				Color color = Color.White * alpha;
-
-                // make particles grow as they age. they'll start at 75% of their size,
-                // and increase to 100% once they're finished.
-                float scale = p.Scale * (.75f + .25f * normalizedLifetime);
-
-                game.SpriteBatch.Draw(texture, p.Position, null, color,
-                    p.Rotation, origin, scale, SpriteEffects.None, 0.0f);
-            }
-
-            game.SpriteBatch.End();
-
-            base.Draw(gameTime);
-        }
-    }
-}
+//-----------------------------------------------------------------------------
+// SmokePlumeParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace ParticleSample
+{
+    /// <summary>
+    /// ParticleSystem is an abstract class that provides the basic functionality to
+    /// create a particle effect. Different subclasses will have different effects,
+    /// such as fire, explosions, and plumes of smoke. To use these subclasses, 
+    /// simply call AddParticles, and pass in where the particles should exist
+    /// </summary>
+    public abstract class ParticleSystem : DrawableGameComponent
+    {
+        // these two values control the order that particle systems are drawn in.
+        // typically, particles that use additive blending should be drawn on top of
+        // particles that use regular alpha blending. ParticleSystems should therefore
+        // set their DrawOrder to the appropriate value in InitializeConstants, though
+        // it is possible to use other values for more advanced effects.
+        public const int AlphaBlendDrawOrder = 100;
+        public const int AdditiveDrawOrder = 200;
+
+        // a reference to the main game; we'll keep this around because it exposes a
+        // content manager and a sprite batch for us to use.
+        private ParticleSampleGame game;
+
+        // the texture this particle system will use.
+        private Texture2D texture;
+
+        // the origin when we're drawing textures. this will be the middle of the
+        // texture.
+        private Vector2 origin;
+
+        // this number represents the maximum number of effects this particle system
+        // will be expected to draw at one time. this is set in the constructor and is
+        // used to calculate how many particles we will need.
+        private int howManyEffects;
+        
+        // the array of particles used by this system. these are reused, so that calling
+        // AddParticles will not cause any allocations.
+        Particle[] particles;
+
+        // the queue of free particles keeps track of particles that are not curently
+        // being used by an effect. when a new effect is requested, particles are taken
+        // from this queue. when particles are finished they are put onto this queue.
+        Queue<Particle> freeParticles;
+        /// <summary>
+        /// returns the number of particles that are available for a new effect.
+        /// </summary>
+        public int FreeParticleCount
+        {
+            get { return freeParticles.Count; }
+        }
+
+
+        // This region of values control the "look" of the particle system, and should 
+        // be set by deriving particle systems in the InitializeConstants method. The
+        // values are then used by the virtual function InitializeParticle. Subclasses
+        // can override InitializeParticle for further
+        // customization.
+
+        /// <summary>
+        /// minNumParticles and maxNumParticles control the number of particles that are
+        /// added when AddParticles is called. The number of particles will be a random
+        /// number between minNumParticles and maxNumParticles.
+        /// </summary>
+        protected int minNumParticles;
+        protected int maxNumParticles;
+       
+        /// <summary>
+        /// this controls the texture that the particle system uses. It will be used as
+        /// an argument to ContentManager.Load.
+        /// </summary>
+        protected string textureFilename;
+
+        /// <summary>
+        /// minInitialSpeed and maxInitialSpeed are used to control the initial velocity
+        /// of the particles. The particle's initial speed will be a random number 
+        /// between these two. The direction is determined by the function 
+        /// PickRandomDirection, which can be overriden.
+        /// </summary>
+        protected float minInitialSpeed;
+        protected float maxInitialSpeed;
+
+        /// <summary>
+        /// minAcceleration and maxAcceleration are used to control the acceleration of
+        /// the particles. The particle's acceleration will be a random number between
+        /// these two. By default, the direction of acceleration is the same as the
+        /// direction of the initial velocity.
+        /// </summary>
+        protected float minAcceleration;
+        protected float maxAcceleration;
+
+        /// <summary>
+        /// minRotationSpeed and maxRotationSpeed control the particles' angular
+        /// velocity: the speed at which particles will rotate. Each particle's rotation
+        /// speed will be a random number between minRotationSpeed and maxRotationSpeed.
+        /// Use smaller numbers to make particle systems look calm and wispy, and large 
+        /// numbers for more violent effects.
+        /// </summary>
+        protected float minRotationSpeed;
+        protected float maxRotationSpeed;
+
+        /// <summary>
+        /// minLifetime and maxLifetime are used to control the lifetime. Each
+        /// particle's lifetime will be a random number between these two. Lifetime
+        /// is used to determine how long a particle "lasts." Also, in the base
+        /// implementation of Draw, lifetime is also used to calculate alpha and scale
+        /// values to avoid particles suddenly "popping" into view
+        /// </summary>
+        protected float minLifetime;
+        protected float maxLifetime;
+
+        /// <summary>
+        /// to get some additional variance in the appearance of the particles, we give
+        /// them all random scales. the scale is a value between minScale and maxScale,
+        /// and is additionally affected by the particle's lifetime to avoid particles
+        /// "popping" into view.
+        /// </summary>
+        protected float minScale;
+        protected float maxScale;
+
+        /// <summary>
+        /// different effects can use different blend states. fire and explosions work
+        /// well with additive blending, for example.
+        /// </summary>
+		protected BlendState blendState;
+
+        
+        /// <summary>
+        /// Constructs a new ParticleSystem.
+        /// </summary>
+        /// <param name="game">The host for this particle system. The game keeps the 
+        /// content manager and sprite batch for us.</param>
+        /// <param name="howManyEffects">the maximum number of particle effects that
+        /// are expected on screen at once.</param>
+        /// <remarks>it is tempting to set the value of howManyEffects very high.
+        /// However, this value should be set to the minimum possible, because
+        /// it has a large impact on the amount of memory required, and slows down the
+        /// Update and Draw functions.</remarks>
+        protected ParticleSystem(ParticleSampleGame game, int howManyEffects)
+            : base(game)
+        {            
+            this.game = game;
+            this.howManyEffects = howManyEffects;
+        }
+
+        /// <summary>
+        /// override the base class's Initialize to do some additional work; we want to
+        /// call InitializeConstants to let subclasses set the constants that we'll use.
+        /// 
+        /// also, the particle array and freeParticles queue are set up here.
+        /// </summary>
+        public override void Initialize()
+        {
+            InitializeConstants();
+            
+            // calculate the total number of particles we will ever need, using the
+            // max number of effects and the max number of particles per effect.
+            // once these particles are allocated, they will be reused, so that
+            // we don't put any pressure on the garbage collector.
+            particles = new Particle[howManyEffects * maxNumParticles];
+            freeParticles = new Queue<Particle>(howManyEffects * maxNumParticles);
+            for (int i = 0; i < particles.Length; i++)
+            {
+                particles[i] = new Particle();
+                freeParticles.Enqueue(particles[i]);
+            }
+            base.Initialize();
+        }
+
+        /// <summary>
+        /// this abstract function must be overriden by subclasses of ParticleSystem.
+        /// It's here that they should set all the constants marked in the region
+        /// "constants to be set by subclasses", which give each ParticleSystem its
+        /// specific flavor.
+        /// </summary>
+        protected abstract void InitializeConstants();
+
+        /// <summary>
+        /// Override the base class LoadContent to load the texture. once it's
+        /// loaded, calculate the origin.
+        /// </summary>
+        protected override void LoadContent()
+        {
+            // make sure sub classes properly set textureFilename.
+            if (string.IsNullOrEmpty(textureFilename))
+            {
+                string message = "textureFilename wasn't set properly, so the " +
+                    "particle system doesn't know what texture to load. Make " +
+                    "sure your particle system's InitializeConstants function " +
+                    "properly sets textureFilename.";
+                throw new InvalidOperationException(message);
+            }
+            // load the texture....
+            texture = game.Content.Load<Texture2D>(textureFilename);
+
+            // ... and calculate the center. this'll be used in the draw call, we
+            // always want to rotate and scale around this point.
+            origin.X = texture.Width / 2;
+            origin.Y = texture.Height / 2;
+
+            base.LoadContent();
+        }
+
+        /// <summary>
+        /// AddParticles's job is to add an effect somewhere on the screen. If there 
+        /// aren't enough particles in the freeParticles queue, it will use as many as 
+        /// it can. This means that if there not enough particles available, calling
+        /// AddParticles will have no effect.
+        /// </summary>
+        /// <param name="where">where the particle effect should be created</param>
+        public void AddParticles(Vector2 where)
+        {
+            // the number of particles we want for this effect is a random number
+            // somewhere between the two constants specified by the subclasses.
+            int numParticles = 
+                ParticleSampleGame.Random.Next(minNumParticles, maxNumParticles);
+
+            // create that many particles, if you can.
+            for (int i = 0; i < numParticles && freeParticles.Count > 0; i++)
+            {
+                // grab a particle from the freeParticles queue, and Initialize it.
+                Particle p = freeParticles.Dequeue();
+                InitializeParticle(p, where);               
+            }
+        }
+
+        /// <summary>
+        /// InitializeParticle randomizes some properties for a particle, then
+        /// calls initialize on it. It can be overriden by subclasses if they 
+        /// want to modify the way particles are created. For example, 
+        /// SmokePlumeParticleSystem overrides this function make all particles
+        /// accelerate to the right, simulating wind.
+        /// </summary>
+        /// <param name="p">the particle to initialize</param>
+        /// <param name="where">the position on the screen that the particle should be
+        /// </param>
+        protected virtual void InitializeParticle(Particle p, Vector2 where)
+        {
+            // first, call PickRandomDirection to figure out which way the particle
+            // will be moving. velocity and acceleration's values will come from this.
+            Vector2 direction = PickRandomDirection();
+
+            // pick some random values for our particle
+            float velocity = 
+                ParticleSampleGame.RandomBetween(minInitialSpeed, maxInitialSpeed);
+            float acceleration = 
+                ParticleSampleGame.RandomBetween(minAcceleration, maxAcceleration);
+            float lifetime =
+                ParticleSampleGame.RandomBetween(minLifetime, maxLifetime);
+            float scale =
+                ParticleSampleGame.RandomBetween(minScale, maxScale);
+            float rotationSpeed =
+                ParticleSampleGame.RandomBetween(minRotationSpeed, maxRotationSpeed);
+
+            // then initialize it with those random values. initialize will save those,
+            // and make sure it is marked as active.
+            p.Initialize(
+                where, velocity * direction, acceleration * direction,
+                lifetime, scale, rotationSpeed);
+        }
+
+        /// <summary>
+        /// PickRandomDirection is used by InitializeParticles to decide which direction
+        /// particles will move. The default implementation is a random vector in a
+        /// circular pattern.
+        /// </summary>
+        protected virtual Vector2 PickRandomDirection()
+        {
+            float angle = ParticleSampleGame.RandomBetween(0, MathHelper.TwoPi);
+            return new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
+        }
+
+        /// <summary>
+        /// overriden from DrawableGameComponent, Update will update all of the active
+        /// particles.
+        /// </summary>
+        public override void Update(GameTime gameTime)
+        {
+            // calculate dt, the change in the since the last frame. the particle
+            // updates will use this value.
+            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+            // go through all of the particles...
+            foreach (Particle p in particles)
+            {
+                
+                if (p.Active)
+                {
+                    // ... and if they're active, update them.
+                    p.Update(dt);
+                    // if that update finishes them, put them onto the free particles
+                    // queue.
+                    if (!p.Active)
+                    {
+                        freeParticles.Enqueue(p);
+                    }
+                }   
+            }
+
+            base.Update(gameTime);
+        }
+
+        /// <summary>
+        /// overriden from DrawableGameComponent, Draw will use ParticleSampleGame's 
+        /// sprite batch to render all of the active particles.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            // tell sprite batch to begin, using the spriteBlendMode specified in
+            // initializeConstants
+			game.SpriteBatch.Begin(SpriteSortMode.Deferred, blendState);
+            
+            foreach (Particle p in particles)
+            {
+                // skip inactive particles
+                if (!p.Active)
+                    continue;
+
+                // normalized lifetime is a value from 0 to 1 and represents how far
+                // a particle is through its life. 0 means it just started, .5 is half
+                // way through, and 1.0 means it's just about to be finished.
+                // this value will be used to calculate alpha and scale, to avoid 
+                // having particles suddenly appear or disappear.
+                float normalizedLifetime = p.TimeSinceStart / p.Lifetime;
+
+                // we want particles to fade in and fade out, so we'll calculate alpha
+                // to be (normalizedLifetime) * (1-normalizedLifetime). this way, when
+                // normalizedLifetime is 0 or 1, alpha is 0. the maximum value is at
+                // normalizedLifetime = .5, and is
+                // (normalizedLifetime) * (1-normalizedLifetime)
+                // (.5)                 * (1-.5)
+                // .25
+                // since we want the maximum alpha to be 1, not .25, we'll scale the 
+                // entire equation by 4.
+                float alpha = 4 * normalizedLifetime * (1 - normalizedLifetime);
+				Color color = Color.White * alpha;
+
+                // make particles grow as they age. they'll start at 75% of their size,
+                // and increase to 100% once they're finished.
+                float scale = p.Scale * (.75f + .25f * normalizedLifetime);
+
+                game.SpriteBatch.Draw(texture, p.Position, null, color,
+                    p.Rotation, origin, scale, SpriteEffects.None, 0.0f);
+            }
+
+            game.SpriteBatch.End();
+
+            base.Draw(gameTime);
+        }
+    }
+}

+ 105 - 109
ParticleSample/SmokePlumeParticleSystem.cs → ParticleSample/Core/SmokePlumeParticleSystem.cs

@@ -1,109 +1,105 @@
-#region File Description
-//-----------------------------------------------------------------------------
-// SmokePlumeParticleSystem.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
-#endregion
-
-#region Using Statements
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-#endregion
-
-namespace ParticleSample
-{
-    /// <summary>
-    /// SmokePlumeParticleSystem is a specialization of ParticleSystem which sends up a
-    /// plume of smoke. The smoke is blown to the right by the wind.
-    /// </summary>
-    public class SmokePlumeParticleSystem : ParticleSystem
-    {
-        public SmokePlumeParticleSystem(ParticleSampleGame game, int howManyEffects)
-            : base(game,howManyEffects)
-        {
-        }
-
-        /// <summary>
-        /// Set up the constants that will give this particle system its behavior and
-        /// properties.
-        /// </summary>
-        protected override void InitializeConstants()
-        {
-            textureFilename = "smoke";
-
-            minInitialSpeed = 20;
-            maxInitialSpeed = 100;
-
-            // we don't want the particles to accelerate at all, aside from what we
-            // do in our overriden InitializeParticle.
-            minAcceleration = 0;
-            maxAcceleration = 0;
-
-            // long lifetime, this can be changed to create thinner or thicker smoke.
-            // tweak minNumParticles and maxNumParticles to complement the effect.
-            minLifetime = 5.0f;
-            maxLifetime = 7.0f;
-
-            minScale = .5f;
-            maxScale = 1.0f;
-
-            // we need to reduce the number of particles on Windows Phone in order to keep
-            // a good framerate
-#if WINDOWS_PHONE
-            minNumParticles = 3;
-            maxNumParticles = 8;
-#else
-            minNumParticles = 7;
-            maxNumParticles = 15;
-#endif
-
-            // rotate slowly, we want a fairly relaxed effect
-            minRotationSpeed = -MathHelper.PiOver4 / 2.0f;
-            maxRotationSpeed = MathHelper.PiOver4 / 2.0f;
-
-			blendState = BlendState.AlphaBlend;
-
-            DrawOrder = AlphaBlendDrawOrder;
-        }
-
-        /// <summary>
-        /// PickRandomDirection is overriden so that we can make the particles always 
-        /// move have an initial velocity pointing up.
-        /// </summary>
-        /// <returns>a random direction which points basically up.</returns>
-        protected override Vector2 PickRandomDirection()
-        {
-            // Point the particles somewhere between 80 and 100 degrees.
-            // tweak this to make the smoke have more or less spread.
-            float radians = ParticleSampleGame.RandomBetween(
-                MathHelper.ToRadians(80), MathHelper.ToRadians(100));
-
-            Vector2 direction = Vector2.Zero;
-            // from the unit circle, cosine is the x coordinate and sine is the
-            // y coordinate. We're negating y because on the screen increasing y moves
-            // down the monitor.
-            direction.X = (float)Math.Cos(radians);
-            direction.Y = -(float)Math.Sin(radians);
-            return direction;
-        }
-
-        /// <summary>
-        /// InitializeParticle is overridden to add the appearance of wind.
-        /// </summary>
-        /// <param name="p">the particle to set up</param>
-        /// <param name="where">where the particle should be placed</param>
-        protected override void InitializeParticle(Particle p, Vector2 where)
-        {
-            base.InitializeParticle(p, where);
-
-            // the base is mostly good, but we want to simulate a little bit of wind
-            // heading to the right.
-            p.Acceleration.X += ParticleSampleGame.RandomBetween(10, 50);
-        }
-    }
-}
+//-----------------------------------------------------------------------------
+// SmokePlumeParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace ParticleSample
+{
+    /// <summary>
+    /// SmokePlumeParticleSystem is a specialization of ParticleSystem which sends up a
+    /// plume of smoke. The smoke is blown to the right by the wind.
+    /// </summary>
+    public class SmokePlumeParticleSystem : ParticleSystem
+    {
+        public SmokePlumeParticleSystem(ParticleSampleGame game, int howManyEffects)
+            : base(game,howManyEffects)
+        {
+        }
+
+        /// <summary>
+        /// Set up the constants that will give this particle system its behavior and
+        /// properties.
+        /// </summary>
+        protected override void InitializeConstants()
+        {
+            textureFilename = "smoke";
+
+            minInitialSpeed = 20;
+            maxInitialSpeed = 100;
+
+            // we don't want the particles to accelerate at all, aside from what we
+            // do in our overriden InitializeParticle.
+            minAcceleration = 0;
+            maxAcceleration = 0;
+
+            // long lifetime, this can be changed to create thinner or thicker smoke.
+            // tweak minNumParticles and maxNumParticles to complement the effect.
+            minLifetime = 5.0f;
+            maxLifetime = 7.0f;
+
+            minScale = .5f;
+            maxScale = 1.0f;
+
+            // we need to reduce the number of particles on Windows Phone in order to keep
+            // a good framerate
+#if WINDOWS_PHONE
+            minNumParticles = 3;
+            maxNumParticles = 8;
+#else
+            minNumParticles = 7;
+            maxNumParticles = 15;
+#endif
+
+            // rotate slowly, we want a fairly relaxed effect
+            minRotationSpeed = -MathHelper.PiOver4 / 2.0f;
+            maxRotationSpeed = MathHelper.PiOver4 / 2.0f;
+
+			blendState = BlendState.AlphaBlend;
+
+            DrawOrder = AlphaBlendDrawOrder;
+        }
+
+        /// <summary>
+        /// PickRandomDirection is overriden so that we can make the particles always 
+        /// move have an initial velocity pointing up.
+        /// </summary>
+        /// <returns>a random direction which points basically up.</returns>
+        protected override Vector2 PickRandomDirection()
+        {
+            // Point the particles somewhere between 80 and 100 degrees.
+            // tweak this to make the smoke have more or less spread.
+            float radians = ParticleSampleGame.RandomBetween(
+                MathHelper.ToRadians(80), MathHelper.ToRadians(100));
+
+            Vector2 direction = Vector2.Zero;
+            // from the unit circle, cosine is the x coordinate and sine is the
+            // y coordinate. We're negating y because on the screen increasing y moves
+            // down the monitor.
+            direction.X = (float)Math.Cos(radians);
+            direction.Y = -(float)Math.Sin(radians);
+            return direction;
+        }
+
+        /// <summary>
+        /// InitializeParticle is overridden to add the appearance of wind.
+        /// </summary>
+        /// <param name="p">the particle to set up</param>
+        /// <param name="where">where the particle should be placed</param>
+        protected override void InitializeParticle(Particle p, Vector2 where)
+        {
+            base.InitializeParticle(p, where);
+
+            // the base is mostly good, but we want to simulate a little bit of wind
+            // heading to the right.
+            p.Acceleration.X += ParticleSampleGame.RandomBetween(10, 50);
+        }
+    }
+}

+ 0 - 16
ParticleSample/Info.plist

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleIdentifier</key>
-	<string>com.yourcompany.ParticleSample</string>
-	<key>CFBundleName</key>
-	<string>ParticleSample</string>
-	<key>CFBundleVersion</key>
-	<string>1</string>
-	<key>LSMinimumSystemVersion</key>
-	<string>10.6</string>
-	<key>NSPrincipalClass</key>
-	<string>NSApplication</string>
-</dict>
-</plist>

+ 0 - 91
ParticleSample/ParticleSample.Linux.csproj

@@ -1,91 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>10.0.0</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{EB1F36EC-5DB8-41AC-AB99-E9337239A9F9}</ProjectGuid>
-    <OutputType>Exe</OutputType>
-    <RootNamespace>ParticleSample</RootNamespace>
-    <AssemblyName>ParticleSample</AssemblyName>
-    <StartupObject>ParticleSample.Program</StartupObject>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>True</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>False</ConsolePause>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>none</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>False</ConsolePause>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <ItemGroup>
-    <None Include="Background.png">
-      <Link>Background.png</Link>
-    </None>
-    <None Include="Game.ico">
-      <Link>Game.ico</Link>
-    </None>
-    <None Include="GameThumbnail.png">
-      <Link>GameThumbnail.png</Link>
-    </None>
-    <None Include="Content\explosion.png">
-      <Link>Content\explosion.png</Link>
-    </None>
-    <None Include="Content\font.spritefont">
-      <Link>Content\font.spritefont</Link>
-    </None>
-    <None Include="Content\smoke.bmp">
-      <Link>Content\smoke.bmp</Link>
-    </None>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="ExplosionParticleSystem.cs">
-      <Link>ExplosionParticleSystem.cs</Link>
-    </Compile>
-    <Compile Include="ExplosionSmokeParticleSystem.cs">
-      <Link>ExplosionSmokeParticleSystem.cs</Link>
-    </Compile>
-    <Compile Include="Particle.cs">
-      <Link>Particle.cs</Link>
-    </Compile>
-    <Compile Include="ParticleSampleGame.cs">
-      <Link>ParticleSampleGame.cs</Link>
-    </Compile>
-    <Compile Include="ParticleSystem.cs">
-      <Link>ParticleSystem.cs</Link>
-    </Compile>
-    <Compile Include="SmokePlumeParticleSystem.cs">
-      <Link>SmokePlumeParticleSystem.cs</Link>
-    </Compile>
-    <Compile Include="Program.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <Content Include="Content\explosion.xnb">
-      <Link>Content\explosion.xnb</Link>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\font.xnb">
-      <Link>Content\font.xnb</Link>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\smoke.xnb">
-      <Link>Content\smoke.xnb</Link>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-  </ItemGroup>
-</Project>

+ 0 - 88
ParticleSample/ParticleSample.MacOS.csproj

@@ -1,88 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
-    <ProductVersion>10.0.0</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{5CA0180F-C404-431A-87B5-03A26E5BFA23}</ProjectGuid>
-    <ProjectTypeGuids>{948B3504-5B70-4649-8FE4-BDE1FB46EC69};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
-    <OutputType>Exe</OutputType>
-    <RootNamespace>ParticleSample</RootNamespace>
-    <AssemblyName>ParticleSample</AssemblyName>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
-    <DebugSymbols>True</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG;MONOMAC</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <PlatformTarget>x86</PlatformTarget>
-    <ConsolePause>False</ConsolePause>
-    <EnableCodeSigning>False</EnableCodeSigning>
-    <CreatePackage>False</CreatePackage>
-    <EnablePackageSigning>False</EnablePackageSigning>
-    <IncludeMonoRuntime>False</IncludeMonoRuntime>
-    <UseSGen>False</UseSGen>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
-    <DebugType>none</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <PlatformTarget>x86</PlatformTarget>
-    <ConsolePause>False</ConsolePause>
-    <EnableCodeSigning>False</EnableCodeSigning>
-    <CreatePackage>False</CreatePackage>
-    <EnablePackageSigning>False</EnablePackageSigning>
-    <IncludeMonoRuntime>False</IncludeMonoRuntime>
-    <UseSGen>False</UseSGen>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="System.Xml" />
-    <Reference Include="System.Core" />
-    <Reference Include="System.Xml.Linq" />
-    <Reference Include="System.Drawing" />
-    <Reference Include="MonoMac" />
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="Info.plist">
-    </None>
-    <None Include="Background.png" />
-    <None Include="Game.ico" />
-    <None Include="GameThumbnail.png" />
-    <None Include="Content\explosion.png" />
-    <None Include="Content\font.spritefont" />
-    <None Include="Content\smoke.bmp" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <Import Project="$(MSBuildExtensionsPath)\Mono\MonoMac\v0.0\Mono.MonoMac.targets" />
-  <ItemGroup>
-    <Compile Include="ExplosionParticleSystem.cs" />
-    <Compile Include="ExplosionSmokeParticleSystem.cs" />
-    <Compile Include="Particle.cs" />
-    <Compile Include="ParticleSampleGame.cs" />
-    <Compile Include="ParticleSystem.cs" />
-    <Compile Include="Program.cs" />
-    <Compile Include="SmokePlumeParticleSystem.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <Content Include="Content\explosion.xnb" />
-    <Content Include="Content\font.xnb" />
-    <Content Include="Content\smoke.xnb" />
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\..\ThirdParty\Lidgren.Network\Lidgren.Network.MacOS.csproj">
-      <Project>{AE483C29-042E-4226-BA52-D247CE7676DA}</Project>
-      <Name>Lidgren.Network.MacOS</Name>
-    </ProjectReference>
-    <ProjectReference Include="..\..\MonoGame.Framework\MonoGame.Framework.MacOS.csproj">
-      <Project>{36C538E6-C32A-4A8D-A39C-566173D7118E}</Project>
-      <Name>MonoGame.Framework.MacOS</Name>
-    </ProjectReference>
-  </ItemGroup>
-</Project>

+ 0 - 120
ParticleSample/ParticleSample.iOS.csproj

@@ -1,120 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
-    <ProductVersion>9.0.21022</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{4D0C3C5D-B4A5-4C6F-8756-4A05C50ECC3A}</ProjectGuid>
-    <ProjectTypeGuids>{6BC8ED88-2882-458C-8E55-DFD12B67127B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
-    <OutputType>Exe</OutputType>
-    <RootNamespace>MonoGame.Samples.ParticleSample</RootNamespace>
-    <AssemblyName>ParticleSample</AssemblyName>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
-    <DebugSymbols>True</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
-    <DefineConstants>DEBUG;IPHONE</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <MtouchDebug>True</MtouchDebug>
-    <CodesignKey>iPhone Developer</CodesignKey>
-    <MtouchLink>None</MtouchLink>
-    <MtouchI18n />
-    <MtouchUseArmv7>false</MtouchUseArmv7>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
-    <DebugType>none</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\iPhoneSimulator\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <CodesignKey>iPhone Developer</CodesignKey>
-    <MtouchLink>None</MtouchLink>
-    <DefineConstants>IPHONE;</DefineConstants>
-    <MtouchI18n />
-    <MtouchUseArmv7>false</MtouchUseArmv7>
-    <MtouchSdkVersion>3.0</MtouchSdkVersion>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
-    <DebugSymbols>True</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\iPhone\Debug</OutputPath>
-    <DefineConstants>DEBUG;IPHONE;</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <MtouchDebug>True</MtouchDebug>
-    <CodesignKey>iPhone Developer</CodesignKey>
-    <MtouchLink>None</MtouchLink>
-    <MtouchI18n />
-    <MtouchUseArmv7>false</MtouchUseArmv7>
-    <MtouchSdkVersion>3.0</MtouchSdkVersion>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
-    <DebugType>none</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\iPhone\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <CodesignKey>iPhone Developer</CodesignKey>
-    <DefineConstants>IPHONE;</DefineConstants>
-    <MtouchUseArmv7>false</MtouchUseArmv7>
-    <MtouchSdkVersion>3.0</MtouchSdkVersion>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Distribution|iPhone' ">
-    <DebugType>none</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin</OutputPath>
-    <WarningLevel>4</WarningLevel>
-    <CodesignKey>iPhone Developer</CodesignKey>
-    <MtouchI18n />
-    <DefineConstants>IPHONE;</DefineConstants>
-    <MtouchUseArmv7>false</MtouchUseArmv7>
-    <MtouchSdkVersion>3.0</MtouchSdkVersion>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Distribution|iPhoneSimulator' ">
-    <DebugType>none</DebugType>
-    <Optimize>False</Optimize>
-    <OutputPath>bin\Distribution</OutputPath>
-    <WarningLevel>4</WarningLevel>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="System.Xml" />
-    <Reference Include="System.Core" />
-    <Reference Include="monotouch" />
-    <Reference Include="OpenTK" />
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="ExplosionParticleSystem.cs" />
-    <Compile Include="ExplosionSmokeParticleSystem.cs" />
-    <Compile Include="Particle.cs" />
-    <Compile Include="ParticleSampleGame.cs" />
-    <Compile Include="ParticleSystem.cs" />
-    <Compile Include="SmokePlumeParticleSystem.cs" />
-    <Compile Include="Program.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <Folder Include="Content\" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <ItemGroup>
-    <Content Include="Default.png">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="Content\explosion.xnb" />
-    <Content Include="Content\smoke.xnb" />
-    <Content Include="Content\font.xnb" />
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="GameThumbnail.png" />
-    <None Include="Info.iOS.plist">
-      <Link>Info.plist</Link>
-    </None>
-    <None Include="Content\explosion.png" />
-    <None Include="Content\smoke.bmp" />
-  </ItemGroup>
-</Project>

+ 44 - 0
ParticleSample/ParticleSample.sln

@@ -0,0 +1,44 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParticleSample.Core", "Core\ParticleSample.Core.csproj", "{A0123456-0000-0000-0000-000000000000}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParticleSample.Windows", "Platforms\Windows\ParticleSample.Windows.csproj", "{B1234567-1234-1234-1234-123456789012}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParticleSample.DesktopGL", "Platforms\Desktop\ParticleSample.DesktopGL.csproj", "{C2345678-2345-2345-2345-234567890123}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParticleSample.Android", "Platforms\Android\ParticleSample.Android.csproj", "{D3456789-3456-3456-3456-345678901234}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParticleSample.iOS", "Platforms\iOS\ParticleSample.iOS.csproj", "{E4567890-4567-4567-4567-456789012345}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{B1234567-1234-1234-1234-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B1234567-1234-1234-1234-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B1234567-1234-1234-1234-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B1234567-1234-1234-1234-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C2345678-2345-2345-2345-234567890123}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C2345678-2345-2345-2345-234567890123}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C2345678-2345-2345-2345-234567890123}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C2345678-2345-2345-2345-234567890123}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D3456789-3456-3456-3456-345678901234}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D3456789-3456-3456-3456-345678901234}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D3456789-3456-3456-3456-345678901234}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D3456789-3456-3456-3456-345678901234}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E4567890-4567-4567-4567-456789012345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E4567890-4567-4567-4567-456789012345}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E4567890-4567-4567-4567-456789012345}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E4567890-4567-4567-4567-456789012345}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {602BAF3F-21F6-4872-A973-BE524425802F}
+	EndGlobalSection
+EndGlobal

+ 19 - 0
ParticleSample/Platforms/Android/AndroidManifest.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          android:versionCode="1"
+          android:versionName="1.0"
+          package="com.monogame.particlesample">
+  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
+  <application android:label="ParticleSample">
+    <activity android:name="particlesample.Activity1"
+              android:label="ParticleSample"
+              android:exported="true"
+              android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|screenLayout|uiMode" >
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+      </intent-filter>
+    </activity>
+  </application>
+  <uses-feature android:glEsVersion="0x00020000" android:required="true" />
+</manifest>

+ 26 - 0
ParticleSample/Platforms/Android/MainActivity.cs

@@ -0,0 +1,26 @@
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+using MonoGame.Framework;
+
+namespace ParticleSample.Android
+{
+    [Activity(
+        Label = "ParticleSample",
+        MainLauncher = true,
+        Icon = "@mipmap/icon",
+        Theme = "@style/MainTheme",
+        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize,
+        ScreenOrientation = ScreenOrientation.FullSensor
+    )]
+    public class MainActivity : AndroidGameActivity
+    {
+        protected override void OnCreate(Bundle savedInstanceState)
+        {
+            base.OnCreate(savedInstanceState);
+            var game = new ParticleSampleGame();
+            SetContentView((game.Services.GetService(typeof(Android.Views.View)) as Android.Views.View));
+            game.Run();
+        }
+    }
+}

+ 35 - 0
ParticleSample/Platforms/Android/ParticleSample.Android.csproj

@@ -0,0 +1,35 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0-android</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <RollForward>Major</RollForward>
+    <PublishReadyToRun>false</PublishReadyToRun>
+    <TieredCompilation>false</TieredCompilation>
+    <AssemblyName>ParticleSample</AssemblyName>
+    <RootNamespace>ParticleSample</RootNamespace>
+    <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
+    <AndroidUseAapt2>True</AndroidUseAapt2>
+    <AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
+    <AndroidManifest>AndroidManifest.xml</AndroidManifest>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <ApplicationId>com.monogame.particlesample</ApplicationId>
+    <ApplicationVersion>1</ApplicationVersion>
+    <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.Android" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+    <ProjectReference Include="..\..\Core\ParticleSample.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 26 - 0
ParticleSample/Platforms/Desktop/ParticleSample.DesktopGL.csproj

@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net8.0</TargetFramework>
+    <RollForward>Major</RollForward>
+    <PublishReadyToRun>false</PublishReadyToRun>
+    <TieredCompilation>false</TieredCompilation>
+    <AssemblyName>ParticleSample</AssemblyName>
+    <RootNamespace>ParticleSample</RootNamespace>
+    <ApplicationIcon>..\..\Core\Content\Game.ico</ApplicationIcon>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+    <ProjectReference Include="..\..\Core\ParticleSample.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 15 - 0
ParticleSample/Platforms/Desktop/Program.cs

@@ -0,0 +1,15 @@
+using System;
+using MonoGame.Framework;
+
+namespace ParticleSample.DesktopGL
+{
+    public static class Program
+    {
+        [STAThread]
+        static void Main()
+        {
+            using (var game = new ParticleSampleGame())
+                game.Run();
+        }
+    }
+}

+ 31 - 0
ParticleSample/Platforms/Windows/ParticleSample.Windows.csproj

@@ -0,0 +1,31 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>net8.0-windows</TargetFramework>
+    <RollForward>Major</RollForward>
+    <PublishReadyToRun>false</PublishReadyToRun>
+    <TieredCompilation>false</TieredCompilation>
+    <UseWindowsForms>true</UseWindowsForms>
+    <AssemblyName>ParticleSample</AssemblyName>
+    <RootNamespace>ParticleSample</RootNamespace>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+    <ApplicationIcon>..\..\Core\Content\Game.ico</ApplicationIcon>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.WindowsDX" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+    <ProjectReference Include="..\..\Core\ParticleSample.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 0 - 2
ParticleSample/Program.cs → ParticleSample/Platforms/Windows/Program.cs

@@ -10,7 +10,6 @@ using MonoTouch.UIKit;
 
 namespace ParticleSample
 {
-	#region Entry Point
 #if IPHONE
 	[Register("AppDelegate")]
 	class Program : UIApplicationDelegate
@@ -79,6 +78,5 @@ namespace ParticleSample
 		}
 	}	
 #endif
-	#endregion
 }
 

+ 34 - 0
ParticleSample/Platforms/Windows/app.manifest

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity version="1.0.0.0" name="ParticleSample"/>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+    <security>
+      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- Windows Vista -->
+      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
+      <!-- Windows 7 -->
+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+      <!-- Windows 8 -->
+      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+      <!-- Windows 8.1 -->
+      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+    </application>
+  </compatibility>
+
+  <application xmlns="urn:schemas-microsoft-com:asm.v3">
+    <windowsSettings>
+      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
+      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness>
+    </windowsSettings>
+  </application>
+
+</assembly>

+ 40 - 0
ParticleSample/Platforms/iOS/Info.plist

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleIdentifier</key>
+	<string>com.monogame.particlesample</string>
+	<key>CFBundleName</key>
+	<string>ParticleSample</string>
+	<key>CFBundleDisplayName</key>
+	<string>ParticleSample</string>
+	<key>CFBundleVersion</key>
+	<string>1.0</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>MinimumOSVersion</key>
+	<string>11.0</string>
+	<key>UIDeviceFamily</key>
+	<array>
+		<integer>1</integer>
+		<integer>2</integer>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>opengles-2</string>
+	</array>
+</dict>
+</plist>

+ 27 - 0
ParticleSample/Platforms/iOS/ParticleSample.iOS.csproj

@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0-ios</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <RollForward>Major</RollForward>
+    <PublishReadyToRun>false</PublishReadyToRun>
+    <TieredCompilation>false</TieredCompilation>
+    <AssemblyName>ParticleSample</AssemblyName>
+    <RootNamespace>ParticleSample</RootNamespace>
+    <SupportedOSPlatformVersion>11.0</SupportedOSPlatformVersion>
+    <ProvisioningType>manual</ProvisioningType>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.iOS" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+    <ProjectReference Include="..\..\Core\ParticleSample.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 14 - 0
ParticleSample/Platforms/iOS/Program.cs

@@ -0,0 +1,14 @@
+using System;
+using MonoGame.Framework;
+
+namespace ParticleSample.iOS
+{
+    public static class Program
+    {
+        static void Main(string[] args)
+        {
+            using (var game = new ParticleSampleGame())
+                game.Run();
+        }
+    }
+}

+ 190 - 0
ParticleSample/README.md

@@ -0,0 +1,190 @@
+# ParticleSample - MonoGame 3.8.* Sample
+
+This is a MonoGame sample project that demonstrates particle systems with explosions, smoke plumes, and various particle effects. The project has been modernized to use SDK-style project files and MonoGame 3.8.* NuGet packages.
+
+## Project Overview
+
+The ParticleSample demonstrates how to create and manage different types of particle systems in MonoGame:
+
+- **Explosion Particle System**: Creates dramatic explosion effects
+- **Explosion Smoke Particle System**: Adds smoke effects to explosions  
+- **Smoke Plume Particle System**: Creates rising smoke plume effects
+
+Key features:
+- Multiple particle system types with different behaviors
+- Touch/mouse input for triggering effects
+- Optimized particle rendering and management
+- Cross-platform compatible code
+
+## Project Structure
+
+The project contains the following main classes:
+
+- `ParticleSampleGame.cs` - Main game class that manages the particle systems
+- `ParticleSystem.cs` - Base class for all particle system implementations
+- `Particle.cs` - Individual particle data structure
+- `ExplosionParticleSystem.cs` - Explosion particle effects
+- `ExplosionSmokeParticleSystem.cs` - Smoke effects for explosions
+- `SmokePlumeParticleSystem.cs` - Rising smoke plume effects
+- `Program.cs` - Entry point with platform-specific initialization
+
+## Supported Platforms
+
+This project supports the following platforms with MonoGame 3.8.*:
+
+- **Windows** (.NET 8.0) - Uses DirectX backend
+- **DesktopGL** (.NET 8.0) - Cross-platform OpenGL backend
+- **Android** (.NET 8.0) - Mobile Android devices
+- **iOS** (.NET 8.0) - iOS devices (requires macOS for building)
+
+## Prerequisites
+
+- [.NET 8.0 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)
+- [Visual Studio 2022](https://visualstudio.microsoft.com/) or [Visual Studio Code](https://code.visualstudio.com/)
+- For Android: Android SDK and Java Development Kit
+- For iOS: Xcode and macOS (building iOS requires a Mac)
+
+## Building the Project
+
+### Using Visual Studio
+
+1. Open `ParticleSample.sln` in Visual Studio 2022
+2. Select your target platform (Windows, DesktopGL, Android, or iOS)
+3. Build the solution (Ctrl+Shift+B)
+4. Run the project (F5)
+
+### Using Visual Studio Code
+
+1. Open the project folder in VS Code
+2. Install the C# extension if not already installed
+3. Use the Command Palette (Ctrl+Shift+P) and run "Tasks: Run Task"
+4. Select one of the build tasks:
+   - `build-windows` - Build Windows version
+   - `build-desktopgl` - Build DesktopGL version
+   - `build-android` - Build Android version
+
+### Using Command Line
+
+#### Windows Platform
+```powershell
+dotnet build ParticleSample.Windows.csproj -c Debug
+dotnet run --project ParticleSample.Windows.csproj -c Debug
+```
+
+## Directory Structure
+```
+ParticleSample/
+├── Core/                # Shared game logic and particle system code
+│   └── ParticleSample.Core.csproj
+├── Platforms/
+│   ├── Windows/         # Windows-specific entry point and project
+│   │   └── ParticleSample.Windows.csproj
+│   ├── Desktop/         # DesktopGL (OpenGL) entry point and project
+│   │   └── ParticleSample.DesktopGL.csproj
+│   ├── Android/         # Android entry point and project
+│   │   └── ParticleSample.Android.csproj
+│   └── iOS/             # iOS entry point and project
+│       └── ParticleSample.iOS.csproj
+├── Content/             # Game assets (images, fonts, etc.)
+├── .vscode/             # VS Code tasks and launch configs
+└── ParticleSample.sln   # Solution file referencing all projects
+```
+#### DesktopGL Platform (Cross-platform)
+```powershell
+dotnet build ParticleSample.DesktopGL.csproj -c Debug
+dotnet run --project ParticleSample.DesktopGL.csproj -c Debug
+```
+
+#### Android Platform
+```powershell
+dotnet build ParticleSample.Android.csproj -c Debug
+# Deploy to connected Android device or emulator
+dotnet run --project ParticleSample.Android.csproj -c Debug
+```
+
+#### iOS Platform (requires macOS)
+```bash
+dotnet build ParticleSample.iOS.csproj -c Debug
+# Deploy to connected iOS device or simulator
+dotnet run --project ParticleSample.iOS.csproj -c Debug
+```
+
+## Running the Project
+
+### From Visual Studio Code
+
+Use the Debug panel (Ctrl+Shift+D) and select:
+- "Launch Windows" - Run the Windows DirectX version
+- "Launch DesktopGL" - Run the cross-platform OpenGL version
+
+### Controls
+
+- **Mouse/Touch**: Click or tap anywhere on the screen to trigger particle effects
+- **Multiple Effects**: Different click locations may trigger different particle systems
+- **Real-time Rendering**: Watch as particles are created, animated, and fade away
+
+## Content Pipeline
+
+This project uses pre-built .xnb content files located in the `Content/` directory:
+- `explosion.xnb` - Explosion particle texture
+- `smoke.xnb` - Smoke particle texture  
+- `font.xnb` - Font for text rendering
+
+The project does not include a Content.mgcb file as it directly uses the compiled .xnb files.
+
+## Platform-Specific Notes
+
+### Windows
+- Uses DirectX 11 backend
+- Requires Windows 7 SP1 or later
+- Best performance on Windows systems
+
+### DesktopGL  
+- Uses OpenGL backend
+- Runs on Windows, Linux, and macOS
+- Slightly lower performance than DirectX on Windows
+- Better compatibility across different systems
+
+### Android
+- Minimum API level 21 (Android 5.0)
+- Requires Android SDK for building
+- Touch input optimized for mobile devices
+
+### iOS
+- Minimum iOS version 11.0
+- Requires Xcode and macOS for building
+- Touch input optimized for mobile devices
+
+## Troubleshooting
+
+### Build Issues
+- Ensure .NET 8.0 SDK is installed
+- Run `dotnet restore` to restore NuGet packages
+- Clean and rebuild if encountering cache issues
+
+### Content Pipeline Warnings
+The projects may show warnings about "No Content References Found" - this is expected since we're using pre-built .xnb files directly instead of a Content.mgcb pipeline.
+
+### Platform-Specific Issues
+- **Android**: 
+  - Ensure Android SDK is properly configured
+  - May require Android SDK API level 34 for full compatibility
+  - The manifest is configured for basic functionality
+- **iOS**: Requires valid Apple Developer account for device deployment
+- **DesktopGL**: May require additional OpenGL drivers on some systems
+- **Windows**: May show DPI-related warnings which can be ignored for this sample
+
+## Project Tasks (VSCode)
+
+When using VSCode, you can use the following tasks:
+- **build-windows**: Build the Windows DirectX version
+- **build-desktopgl**: Build the cross-platform DesktopGL version  
+- **build-android**: Build the Android version
+- **run-windows**: Build and run the Windows version
+- **run-desktopgl**: Build and run the DesktopGL version
+
+Use Ctrl+Shift+P → "Tasks: Run Task" to access these tasks.
+
+## License
+
+This sample is based on Microsoft XNA Community Game Platform samples and is provided for educational purposes.