Browse Source

Ported Vector Rumble to MacOS, iOS and Android. It starts up on MacOS and iOS, but does not on Android, due to the way components are handled. MacOS port requires the DrawUserPrimitive changes for it to work, and the iOS port requires shaders for it to work. AudioEngine, SoundBanks and WaveBanks all need more work.

CartBlanche 13 years ago
parent
commit
02035d4547
90 changed files with 8894 additions and 0 deletions
  1. 22 0
      StarterKits/Android/VectorRumble/Activity1.cs
  2. 19 0
      StarterKits/Android/VectorRumble/Assets/AboutAssets.txt
  3. 6 0
      StarterKits/Android/VectorRumble/Properties/AndroidManifest.xml
  4. 44 0
      StarterKits/Android/VectorRumble/Resources/AboutResources.txt
  5. 59 0
      StarterKits/Android/VectorRumble/Resources/Resource.designer.cs
  6. BIN
      StarterKits/Android/VectorRumble/Resources/drawable/Icon.png
  7. 8 0
      StarterKits/Android/VectorRumble/Resources/layout/Main.axml
  8. 4 0
      StarterKits/Android/VectorRumble/Resources/values/Strings.xml
  9. 284 0
      StarterKits/Android/VectorRumble/VectorRumble.csproj
  10. 161 0
      StarterKits/MacOS/VectorRumble/AudioManager.cs
  11. 318 0
      StarterKits/MacOS/VectorRumble/BloomPostprocess/BloomComponent.cs
  12. 80 0
      StarterKits/MacOS/VectorRumble/BloomPostprocess/BloomSettings.cs
  13. 66 0
      StarterKits/MacOS/VectorRumble/CollectCollection.cs
  14. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/RacingGame Game Music 1.wav
  15. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/damage1.wav
  16. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/engine_3.wav
  17. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/explosion1.wav
  18. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/explosion2.wav
  19. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/explosion3.wav
  20. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/hax2_fire.wav
  21. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/hax2_fire2.wav
  22. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/menu_back.wav
  23. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/menu_scroll.wav
  24. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/menu_select3.wav
  25. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/phase_expire_alt.wav
  26. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/tx0_fire1.wav
  27. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/tx0_fire2.wav
  28. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/tx0_fire3.wav
  29. BIN
      StarterKits/MacOS/VectorRumble/Content/Audio/Waves/weapon_pickup_alt_retro.wav
  30. 51 0
      StarterKits/MacOS/VectorRumble/Content/Effects/BloomCombine.fx
  31. 25 0
      StarterKits/MacOS/VectorRumble/Content/Effects/BloomExtract.fx
  32. 33 0
      StarterKits/MacOS/VectorRumble/Content/Effects/GaussianBlur.fx
  33. 48 0
      StarterKits/MacOS/VectorRumble/Content/Fonts/retroMedium.spritefont
  34. BIN
      StarterKits/MacOS/VectorRumble/Content/Fonts/retroMedium.xnb
  35. 48 0
      StarterKits/MacOS/VectorRumble/Content/Fonts/retroSmall.spritefont
  36. BIN
      StarterKits/MacOS/VectorRumble/Content/Fonts/retroSmall.xnb
  37. BIN
      StarterKits/MacOS/VectorRumble/Content/Textures/blank.png
  38. BIN
      StarterKits/MacOS/VectorRumble/Content/Textures/title.png
  39. 108 0
      StarterKits/MacOS/VectorRumble/Game.cs
  40. 143 0
      StarterKits/MacOS/VectorRumble/Gameplay/Asteroid.cs
  41. 58 0
      StarterKits/MacOS/VectorRumble/Gameplay/DoubleLaserPowerUp.cs
  42. 66 0
      StarterKits/MacOS/VectorRumble/Gameplay/DoubleLaserWeapon.cs
  43. 99 0
      StarterKits/MacOS/VectorRumble/Gameplay/LaserProjectile.cs
  44. 50 0
      StarterKits/MacOS/VectorRumble/Gameplay/LaserWeapon.cs
  45. 96 0
      StarterKits/MacOS/VectorRumble/Gameplay/MineProjectile.cs
  46. 59 0
      StarterKits/MacOS/VectorRumble/Gameplay/MineWeapon.cs
  47. 55 0
      StarterKits/MacOS/VectorRumble/Gameplay/RocketPowerUp.cs
  48. 106 0
      StarterKits/MacOS/VectorRumble/Gameplay/RocketProjectile.cs
  49. 57 0
      StarterKits/MacOS/VectorRumble/Gameplay/RocketWeapon.cs
  50. 708 0
      StarterKits/MacOS/VectorRumble/Gameplay/Ship.cs
  51. 55 0
      StarterKits/MacOS/VectorRumble/Gameplay/TripleLaserPowerUp.cs
  52. 77 0
      StarterKits/MacOS/VectorRumble/Gameplay/TripleLaserWeapon.cs
  53. 16 0
      StarterKits/MacOS/VectorRumble/Info.plist
  54. 18 0
      StarterKits/MacOS/VectorRumble/Main.cs
  55. 33 0
      StarterKits/MacOS/VectorRumble/Program.cs
  56. 34 0
      StarterKits/MacOS/VectorRumble/Properties/AssemblyInfo.cs
  57. 271 0
      StarterKits/MacOS/VectorRumble/Rendering/LineBatch.cs
  58. 27 0
      StarterKits/MacOS/VectorRumble/Rendering/Particle.cs
  59. 235 0
      StarterKits/MacOS/VectorRumble/Rendering/ParticleSystem.cs
  60. 149 0
      StarterKits/MacOS/VectorRumble/Rendering/Starfield.cs
  61. 181 0
      StarterKits/MacOS/VectorRumble/Rendering/VectorPolygon.cs
  62. 310 0
      StarterKits/MacOS/VectorRumble/ScreenManager/GameScreen.cs
  63. 235 0
      StarterKits/MacOS/VectorRumble/ScreenManager/InputState.cs
  64. 314 0
      StarterKits/MacOS/VectorRumble/ScreenManager/ScreenManager.cs
  65. 163 0
      StarterKits/MacOS/VectorRumble/Screens/BackgroundScreen.cs
  66. 172 0
      StarterKits/MacOS/VectorRumble/Screens/GameOverScreen.cs
  67. 287 0
      StarterKits/MacOS/VectorRumble/Screens/GameplayScreen.cs
  68. 150 0
      StarterKits/MacOS/VectorRumble/Screens/LoadingScreen.cs
  69. 116 0
      StarterKits/MacOS/VectorRumble/Screens/MainMenuScreen.cs
  70. 157 0
      StarterKits/MacOS/VectorRumble/Screens/MenuEntry.cs
  71. 233 0
      StarterKits/MacOS/VectorRumble/Screens/MenuScreen.cs
  72. 159 0
      StarterKits/MacOS/VectorRumble/Screens/MessageBoxScreen.cs
  73. 168 0
      StarterKits/MacOS/VectorRumble/Screens/OptionsMenuScreen.cs
  74. 155 0
      StarterKits/MacOS/VectorRumble/Screens/PauseMenuScreen.cs
  75. 267 0
      StarterKits/MacOS/VectorRumble/Simulation/Actor.cs
  76. 144 0
      StarterKits/MacOS/VectorRumble/Simulation/Collision.cs
  77. 42 0
      StarterKits/MacOS/VectorRumble/Simulation/CollisionResult.cs
  78. 200 0
      StarterKits/MacOS/VectorRumble/Simulation/PowerUp.cs
  79. 226 0
      StarterKits/MacOS/VectorRumble/Simulation/Projectile.cs
  80. 107 0
      StarterKits/MacOS/VectorRumble/Simulation/Weapon.cs
  81. 736 0
      StarterKits/MacOS/VectorRumble/Simulation/World.cs
  82. 34 0
      StarterKits/MacOS/VectorRumble/Simulation/WorldActor.cs
  83. 43 0
      StarterKits/MacOS/VectorRumble/Simulation/WorldRules.cs
  84. 128 0
      StarterKits/MacOS/VectorRumble/VectorRumble.csproj
  85. BIN
      StarterKits/MacOS/VectorRumble/VectorRumble.png
  86. 8 0
      StarterKits/MonoGame.StarterKits.MacOS.sln
  87. 8 0
      StarterKits/iOS/MemoryMadness/Info.plist
  88. 15 0
      StarterKits/iOS/VectorRumble/Info.plist
  89. 44 0
      StarterKits/iOS/VectorRumble/Program.cs
  90. 296 0
      StarterKits/iOS/VectorRumble/VectorRumble.iOS.csproj

+ 22 - 0
StarterKits/Android/VectorRumble/Activity1.cs

@@ -0,0 +1,22 @@
+using System;
+
+using Android.App;
+using Android.OS;
+
+namespace VectorRumble
+{
+	[Activity (Label = "VectorRumble", MainLauncher = true, Icon = "@drawable/icon")]
+	public class Activity1 : Microsoft.Xna.Framework.AndroidGameActivity
+	{
+		protected override void OnCreate(Bundle bundle)
+        {
+            base.OnCreate(bundle);
+            VectorRumbleGame.Activity = this;
+            VectorRumbleGame g = new VectorRumbleGame();
+            SetContentView(g.Window);
+            g.Run();
+        }
+	}
+}
+
+

+ 19 - 0
StarterKits/Android/VectorRumble/Assets/AboutAssets.txt

@@ -0,0 +1,19 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories) and given a Build Action of "AndroidAsset".
+
+These files will be deployed with you package and will be accessible using Android's
+AssetManager, like this:
+
+public class ReadAsset : Activity
+{
+	protected override void OnCreate (Bundle bundle)
+	{
+		base.OnCreate (bundle);
+
+		InputStream input = Assets.Open ("my_asset.txt");
+	}
+}
+
+Additionally, some Android functions will automatically load asset files:
+
+Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

+ 6 - 0
StarterKits/Android/VectorRumble/Properties/AndroidManifest.xml

@@ -0,0 +1,6 @@
+<?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="vectorrumble.vectorrumble">
+	<application android:label="VectorRumble" android:debuggable="true">
+	</application>
+	<uses-sdk />
+</manifest>

+ 44 - 0
StarterKits/Android/VectorRumble/Resources/AboutResources.txt

@@ -0,0 +1,44 @@
+Images, layout descriptions, binary blobs and string dictionaries can be included 
+in your application as resource files.  Various Android APIs are designed to 
+operate on the resource IDs instead of dealing with images, strings or binary blobs 
+directly.
+
+For example, a sample Android app that contains a user interface layout (main.axml),
+an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) 
+would keep its resources in the "Resources" directory of the application:
+
+Resources/
+    drawable/
+        icon.png
+
+    layout/
+        main.axml
+
+    values/
+        strings.xml
+
+In order to get the build system to recognize Android resources, set the build action to
+"AndroidResource".  The native Android APIs do not operate directly with filenames, but 
+instead operate on resource IDs.  When you compile an Android application that uses resources, 
+the build system will package the resources for distribution and generate a class called "R" 
+(this is an Android convention) that contains the tokens for each one of the resources 
+included. For example, for the above Resources layout, this is what the R class would expose:
+
+public class R {
+    public class drawable {
+        public const int icon = 0x123;
+    }
+
+    public class layout {
+        public const int main = 0x456;
+    }
+
+    public class strings {
+        public const int first_string = 0xabc;
+        public const int second_string = 0xbcd;
+    }
+}
+
+You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main 
+to reference the layout/main.axml file, or R.strings.first_string to reference the first 
+string in the dictionary file values/strings.xml.

+ 59 - 0
StarterKits/Android/VectorRumble/Resources/Resource.designer.cs

@@ -0,0 +1,59 @@
+// ------------------------------------------------------------------------------
+//  <autogenerated>
+//      This code was generated by a tool.
+//      Mono Runtime Version: 4.0.30319.1
+// 
+//      Changes to this file may cause incorrect behavior and will be lost if 
+//      the code is regenerated.
+//  </autogenerated>
+// ------------------------------------------------------------------------------
+
+namespace VectorRumble
+{
+	
+	
+	public partial class Resource
+	{
+		
+		public partial class Attribute
+		{
+			
+			private Attribute()
+			{
+			}
+		}
+		
+		public partial class Drawable
+		{
+			
+			// aapt resource value: 0x7f020000
+			public const int Icon = 2130837504;
+			
+			private Drawable()
+			{
+			}
+		}
+		
+		public partial class Layout
+		{
+			
+			// aapt resource value: 0x7f030000
+			public const int Main = 2130903040;
+			
+			private Layout()
+			{
+			}
+		}
+		
+		public partial class String
+		{
+			
+			// aapt resource value: 0x7f040000
+			public const int app_name = 2130968576;
+			
+			private String()
+			{
+			}
+		}
+	}
+}

BIN
StarterKits/Android/VectorRumble/Resources/drawable/Icon.png


+ 8 - 0
StarterKits/Android/VectorRumble/Resources/layout/Main.axml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+</LinearLayout>
+

+ 4 - 0
StarterKits/Android/VectorRumble/Resources/values/Strings.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+	<string name="app_name">VectorRumble</string>
+</resources>

+ 284 - 0
StarterKits/Android/VectorRumble/VectorRumble.csproj

@@ -0,0 +1,284 @@
+<?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>{BF1FD50E-FB30-4E77-B8C0-77D933DA466C}</ProjectGuid>
+    <ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <RootNamespace>VectorRumble</RootNamespace>
+    <MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
+    <MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
+    <AndroidResgenClass>Resource</AndroidResgenClass>
+    <AndroidApplication>True</AndroidApplication>
+    <AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
+    <AssemblyName>VectorRumble</AssemblyName>
+    <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;ANDROID</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <AndroidLinkMode>None</AndroidLinkMode>
+  </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>
+    <AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
+    <AndroidLinkMode>SdkOnly</AndroidLinkMode>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Core" />
+    <Reference Include="Mono.Android" />
+    <Reference Include="OpenTK" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Activity1.cs" />
+    <Compile Include="Resources\Resource.designer.cs" />
+    <Compile Include="..\..\MacOS\VectorRumble\AudioManager.cs">
+      <Link>AudioManager.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\CollectCollection.cs">
+      <Link>CollectCollection.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Game.cs">
+      <Link>Game.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\BloomPostprocess\BloomComponent.cs">
+      <Link>BloomPostprocess\BloomComponent.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\BloomPostprocess\BloomSettings.cs">
+      <Link>BloomPostprocess\BloomSettings.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\Asteroid.cs">
+      <Link>Gameplay\Asteroid.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\DoubleLaserPowerUp.cs">
+      <Link>Gameplay\DoubleLaserPowerUp.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\DoubleLaserWeapon.cs">
+      <Link>Gameplay\DoubleLaserWeapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\LaserProjectile.cs">
+      <Link>Gameplay\LaserProjectile.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\LaserWeapon.cs">
+      <Link>Gameplay\LaserWeapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\MineProjectile.cs">
+      <Link>Gameplay\MineProjectile.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\MineWeapon.cs">
+      <Link>Gameplay\MineWeapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\RocketPowerUp.cs">
+      <Link>Gameplay\RocketPowerUp.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\RocketProjectile.cs">
+      <Link>Gameplay\RocketProjectile.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\RocketWeapon.cs">
+      <Link>Gameplay\RocketWeapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\Ship.cs">
+      <Link>Gameplay\Ship.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\TripleLaserPowerUp.cs">
+      <Link>Gameplay\TripleLaserPowerUp.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\TripleLaserWeapon.cs">
+      <Link>Gameplay\TripleLaserWeapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Rendering\LineBatch.cs">
+      <Link>Rendering\LineBatch.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Rendering\Particle.cs">
+      <Link>Rendering\Particle.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Rendering\ParticleSystem.cs">
+      <Link>Rendering\ParticleSystem.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Rendering\Starfield.cs">
+      <Link>Rendering\Starfield.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Rendering\VectorPolygon.cs">
+      <Link>Rendering\VectorPolygon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\ScreenManager\GameScreen.cs">
+      <Link>ScreenManager\GameScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\ScreenManager\InputState.cs">
+      <Link>ScreenManager\InputState.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\ScreenManager\ScreenManager.cs">
+      <Link>ScreenManager\ScreenManager.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\BackgroundScreen.cs">
+      <Link>Screens\BackgroundScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\GameOverScreen.cs">
+      <Link>Screens\GameOverScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\GameplayScreen.cs">
+      <Link>Screens\GameplayScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\LoadingScreen.cs">
+      <Link>Screens\LoadingScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\MainMenuScreen.cs">
+      <Link>Screens\MainMenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\MenuEntry.cs">
+      <Link>Screens\MenuEntry.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\MenuScreen.cs">
+      <Link>Screens\MenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\MessageBoxScreen.cs">
+      <Link>Screens\MessageBoxScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\OptionsMenuScreen.cs">
+      <Link>Screens\OptionsMenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\PauseMenuScreen.cs">
+      <Link>Screens\PauseMenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\Actor.cs">
+      <Link>Simulation\Actor.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\Collision.cs">
+      <Link>Simulation\Collision.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\CollisionResult.cs">
+      <Link>Simulation\CollisionResult.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\PowerUp.cs">
+      <Link>Simulation\PowerUp.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\Projectile.cs">
+      <Link>Simulation\Projectile.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\Weapon.cs">
+      <Link>Simulation\Weapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\World.cs">
+      <Link>Simulation\World.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\WorldActor.cs">
+      <Link>Simulation\WorldActor.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\WorldRules.cs">
+      <Link>Simulation\WorldRules.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Properties\AssemblyInfo.cs">
+      <Link>Properties\AssemblyInfo.cs</Link>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\AboutResources.txt" />
+    <None Include="Assets\AboutAssets.txt" />
+    <None Include="..\..\MacOS\VectorRumble\Content\Effects\BloomCombine.fx">
+      <Link>Assets\Content\Effects\BloomCombine.fx</Link>
+    </None>
+    <None Include="..\..\MacOS\VectorRumble\Content\Effects\BloomExtract.fx">
+      <Link>Assets\Content\Effects\BloomExtract.fx</Link>
+    </None>
+    <None Include="..\..\MacOS\VectorRumble\Content\Effects\GaussianBlur.fx">
+      <Link>Assets\Content\Effects\GaussianBlur.fx</Link>
+    </None>
+    <None Include="..\..\MacOS\VectorRumble\Content\Fonts\retroMedium.spritefont">
+      <Link>Assets\Content\Fonts\retroMedium.spritefont</Link>
+    </None>
+    <None Include="..\..\MacOS\VectorRumble\Content\Fonts\retroSmall.spritefont">
+      <Link>Assets\Content\Fonts\retroSmall.spritefont</Link>
+    </None>
+    <None Include="Properties\AndroidManifest.xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <AndroidResource Include="Resources\layout\Main.axml" />
+    <AndroidResource Include="Resources\values\Strings.xml" />
+    <AndroidResource Include="Resources\drawable\Icon.png" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\..\MonoGame\MonoGame.Framework\MonoGame.Framework.Android.csproj">
+      <Project>{BA9476CF-99BA-4D03-92F2-73D2C5E58883}</Project>
+      <Name>MonoGame.Framework.Android</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\damage1.wav">
+      <Link>Assets\Content\Audio\Waves\damage1.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\engine_3.wav">
+      <Link>Assets\Content\Audio\Waves\engine_3.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\explosion1.wav">
+      <Link>Assets\Content\Audio\Waves\explosion1.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\explosion2.wav">
+      <Link>Assets\Content\Audio\Waves\explosion2.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\explosion3.wav">
+      <Link>Assets\Content\Audio\Waves\explosion3.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\hax2_fire.wav">
+      <Link>Assets\Content\Audio\Waves\hax2_fire.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\hax2_fire2.wav">
+      <Link>Assets\Content\Audio\Waves\hax2_fire2.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\menu_back.wav">
+      <Link>Assets\Content\Audio\Waves\menu_back.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\menu_scroll.wav">
+      <Link>Assets\Content\Audio\Waves\menu_scroll.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\menu_select3.wav">
+      <Link>Assets\Content\Audio\Waves\menu_select3.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\phase_expire_alt.wav">
+      <Link>Assets\Content\Audio\Waves\phase_expire_alt.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\RacingGame Game Music 1.wav">
+      <Link>Assets\Content\Audio\Waves\RacingGame Game Music 1.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\tx0_fire1.wav">
+      <Link>Assets\Content\Audio\Waves\tx0_fire1.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\tx0_fire2.wav">
+      <Link>Assets\Content\Audio\Waves\tx0_fire2.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\tx0_fire3.wav">
+      <Link>Assets\Content\Audio\Waves\tx0_fire3.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\weapon_pickup_alt_retro.wav">
+      <Link>Assets\Content\Audio\Waves\weapon_pickup_alt_retro.wav</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Fonts\retroMedium.xnb">
+      <Link>Assets\Content\Fonts\retroMedium.xnb</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Fonts\retroSmall.xnb">
+      <Link>Assets\Content\Fonts\retroSmall.xnb</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Textures\blank.png">
+      <Link>Assets\Content\Textures\blank.png</Link>
+    </AndroidAsset>
+    <AndroidAsset Include="..\..\MacOS\VectorRumble\Content\Textures\title.png">
+      <Link>Assets\Content\Textures\title.png</Link>
+    </AndroidAsset>
+  </ItemGroup>
+</Project>

+ 161 - 0
StarterKits/MacOS/VectorRumble/AudioManager.cs

@@ -0,0 +1,161 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// AudioManager.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Component that manages audio playback for all cues.
+    /// </summary>
+    public class AudioManager : GameComponent
+    {
+        #region Fields
+        /// <summary>
+        /// The audio engine used to play all cues.
+        /// </summary>
+        private AudioEngine engine;
+        /// <summary>
+        /// The soundbank that contains all cues.
+        /// </summary>
+        private SoundBank sounds;
+        /// <summary>
+        /// The wavebank with all wave files for this game.
+        /// </summary>
+        private WaveBank waves;
+
+        /// <summary>
+        /// The music cue for the game
+        /// </summary>
+        private Cue musicCue;
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs the manager for audio playback of all cues.
+        /// </summary>
+        /// <param name="game">The game that this component will be attached to.</param>
+        /// <param name="settingsFile">The filename of the XACT settings file.</param>
+        /// <param name="waveBankFile">The filename of the XACT wavebank file.</param>
+        /// <param name="soundBankFile">The filename of the XACT soundbank file.</param>
+        public AudioManager(Game game, string settingsFile, string waveBankFile,
+            string soundBankFile)
+            : base(game)
+        {
+            engine = new AudioEngine(settingsFile);
+#if AUDIO
+            waves = new WaveBank(engine, waveBankFile);
+            sounds = new SoundBank(engine, soundBankFile);
+#endif
+        }
+        #endregion
+
+        #region Cues
+        /// <summary>
+        /// Retrieve a cue by name.
+        /// </summary>
+        /// <param name="cueName">The name of the cue requested.</param>
+        /// <returns>The cue corresponding to the name provided.</returns>
+        public Cue GetCue(string cueName)
+        {
+            return sounds.GetCue(cueName);
+        }
+
+        /// <summary>
+        /// Plays a cue by name.
+        /// </summary>
+        /// <param name="cueName">The name of the cue to play.</param>
+        public void PlayCue(string cueName)
+        {
+#if AUDIO
+            sounds.PlayCue(cueName);
+#endif
+        }
+        #endregion
+
+        #region Music
+        /// <summary>
+        /// Plays the music for this game, by name.
+        /// </summary>
+        /// <param name="cueName">The name of the music cue to play.</param>
+        public void PlayMusic(string cueName)
+        {
+            // if music is already playing, stop it
+            StopMusic();
+            // start the new music cue
+            musicCue = GetCue(cueName);
+            musicCue.Play();
+        }
+
+        /// <summary>
+        /// Stop music playback.
+        /// </summary>
+        public void StopMusic()
+        {
+            if (musicCue != null)
+            {
+                musicCue.Stop(AudioStopOptions.AsAuthored);
+                musicCue.Dispose();
+                musicCue = null;
+            }
+        }
+        #endregion
+
+        #region Update
+        /// <summary>
+        /// Update the audio system.
+        /// </summary>
+        /// <param name="gameTime">Provides a snapshot of timing values.</param>
+        public override void Update(GameTime gameTime)
+        {
+#if AUDIO
+            engine.Update();
+#endif
+        }
+        #endregion
+
+        #region Disposing
+        /// <summary>
+        /// Clean up the component when it is disposing.
+        /// </summary>
+        protected override void Dispose(bool disposing)
+        {
+            try
+            {
+                if (disposing)
+                {
+                    StopMusic();
+                    if (sounds != null)
+                    {
+                        sounds.Dispose();
+                        sounds = null;
+                    }
+                    if (waves != null)
+                    {
+                        waves.Dispose();
+                        waves = null;
+                    }
+                    if (engine != null)
+                    {
+                        engine.Dispose();
+                        engine = null;
+                    }
+                }
+            }
+            finally
+            {
+                base.Dispose(disposing);
+            }
+        }
+        #endregion
+    }
+}

+ 318 - 0
StarterKits/MacOS/VectorRumble/BloomPostprocess/BloomComponent.cs

@@ -0,0 +1,318 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// BloomComponent.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A DrawableGameComponent that will add bloom post-processing to what the previous
+    /// components have drawn.
+    /// </summary>
+    /// <remarks>
+    /// This class is similar to one of the same name in the Bloom sample.
+    /// </remarks>
+    public class BloomComponent : DrawableGameComponent
+    {
+        #region Fields
+
+        SpriteBatch spriteBatch;
+
+        Effect bloomExtractEffect;
+        Effect bloomCombineEffect;
+        Effect gaussianBlurEffect;
+
+        ResolveTexture2D resolveTarget;
+        RenderTarget2D renderTarget1;
+        RenderTarget2D renderTarget2;
+
+
+        // Choose what display settings the bloom should use.
+        public BloomSettings Settings
+        {
+            get { return settings; }
+            set { settings = value; }
+        }
+
+        BloomSettings settings = BloomSettings.PresetSettings[0];
+
+
+        // Optionally displays one of the intermediate buffers used
+        // by the bloom postprocess, so you can see exactly what is
+        // being drawn into each rendertarget.
+        public enum IntermediateBuffer
+        {
+            PreBloom,
+            BlurredHorizontally,
+            BlurredBothWays,
+            FinalResult,
+        }
+
+        public IntermediateBuffer ShowBuffer
+        {
+            get { return showBuffer; }
+            set { showBuffer = value; }
+        }
+
+        IntermediateBuffer showBuffer = IntermediateBuffer.FinalResult;
+
+
+        #endregion
+
+        #region Initialization
+
+
+        public BloomComponent(Game game)
+            : base(game)
+        {
+            if (game == null)
+                throw new ArgumentNullException("game");
+        }
+
+
+        /// <summary>
+        /// Load your graphics content.
+        /// </summary>
+        protected override void LoadContent()
+        {
+            spriteBatch = new SpriteBatch(GraphicsDevice);
+
+#if SHADER_EFFECTS
+            bloomExtractEffect = Game.Content.Load<Effect>("Effects/BloomExtract");
+            bloomCombineEffect = Game.Content.Load<Effect>("Effects/BloomCombine");
+            gaussianBlurEffect = Game.Content.Load<Effect>("Effects/GaussianBlur");
+#endif
+
+
+            // Look up the resolution and format of our main backbuffer.
+            PresentationParameters pp = GraphicsDevice.PresentationParameters;
+
+            int width = pp.BackBufferWidth;
+            int height = pp.BackBufferHeight;
+
+            SurfaceFormat format = pp.BackBufferFormat;
+
+            // Create a texture for reading back the backbuffer contents.
+            resolveTarget = new ResolveTexture2D(GraphicsDevice, width, height, 1, 
+                format);
+
+            // Create two rendertargets for the bloom processing. These are half the
+            // size of the backbuffer, in order to minimize fillrate costs. Reducing
+            // the resolution in this way doesn't hurt quality, because we are going
+            // to be blurring the bloom images in any case.
+            width /= 2;
+            height /= 2;
+
+            renderTarget1 = new RenderTarget2D(GraphicsDevice, width, height, true, format, DepthFormat.None);
+            renderTarget2 = new RenderTarget2D(GraphicsDevice, width, height, true, format, DepthFormat.None);
+        }
+
+
+        /// <summary>
+        /// Unload your graphics content.
+        /// </summary>
+        protected override void UnloadContent()
+        {
+            resolveTarget.Dispose();
+            renderTarget1.Dispose();
+            renderTarget2.Dispose();
+        }
+
+
+        #endregion
+
+        #region Draw
+
+
+        /// <summary>
+        /// This is where it all happens. Grabs a scene that has already been rendered,
+        /// and uses postprocess magic to add a glowing bloom effect over the top of it.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+#if SHADER_EFFECTS
+            // Resolve the scene into a texture, so we can
+            // use it as input data for the bloom processing.
+            GraphicsDevice.ResolveBackBuffer(resolveTarget);
+
+            // Pass 1: draw the scene into rendertarget 1, using a
+            // shader that extracts only the brightest parts of the image.
+            bloomExtractEffect.Parameters["BloomThreshold"].SetValue(Settings.BloomThreshold);
+
+            DrawFullscreenQuad(resolveTarget, renderTarget1, bloomExtractEffect, IntermediateBuffer.PreBloom);
+
+            // Pass 2: draw from rendertarget 1 into rendertarget 2,
+            // using a shader to apply a horizontal gaussian blur filter.
+            SetBlurEffectParameters(1.0f / (float)renderTarget1.Width, 0);
+
+            DrawFullscreenQuad(renderTarget1, renderTarget2, gaussianBlurEffect, IntermediateBuffer.BlurredHorizontally);
+
+            // Pass 3: draw from rendertarget 2 back into rendertarget 1,
+            // using a shader to apply a vertical gaussian blur filter.
+            SetBlurEffectParameters(0, 1.0f / (float)renderTarget1.Height);
+
+            DrawFullscreenQuad(renderTarget2, renderTarget1, gaussianBlurEffect, IntermediateBuffer.BlurredBothWays);
+
+            // Pass 4: draw both rendertarget 1 and the original scene
+            // image back into the main backbuffer, using a shader that
+            // combines them to produce the final bloomed result.
+            GraphicsDevice.SetRenderTarget(null);
+
+            EffectParameterCollection parameters = bloomCombineEffect.Parameters;
+
+            parameters["BloomIntensity"].SetValue(Settings.BloomIntensity);
+            parameters["BaseIntensity"].SetValue(Settings.BaseIntensity);
+            parameters["BloomSaturation"].SetValue(Settings.BloomSaturation);
+            parameters["BaseSaturation"].SetValue(Settings.BaseSaturation); 
+
+            GraphicsDevice.Textures[1] = resolveTarget;
+
+            Viewport viewport = GraphicsDevice.Viewport;
+
+            DrawFullscreenQuad(renderTarget1, viewport.Width, viewport.Height, bloomCombineEffect, IntermediateBuffer.FinalResult);
+#endif
+        }
+
+
+        /// <summary>
+        /// Helper for drawing a texture into a rendertarget, using
+        /// a custom shader to apply postprocessing effects.
+        /// </summary>
+        void DrawFullscreenQuad(Texture2D texture, RenderTarget2D renderTarget,
+                                Effect effect, IntermediateBuffer currentBuffer)
+        {
+            GraphicsDevice.SetRenderTarget(renderTarget);
+
+            DrawFullscreenQuad(texture,
+                               renderTarget.Width, renderTarget.Height,
+                               effect, currentBuffer);
+
+            GraphicsDevice.SetRenderTarget(null);
+        }
+
+
+        /// <summary>
+        /// Helper for drawing a texture into the current rendertarget,
+        /// using a custom shader to apply postprocessing effects.
+        /// </summary>
+        void DrawFullscreenQuad(Texture2D texture, int width, int height,
+                                Effect effect, IntermediateBuffer currentBuffer)
+        {
+            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque);
+
+            // Begin the custom effect, if it is currently enabled. If the user
+            // has selected one of the show intermediate buffer options, we still
+            // draw the quad to make sure the image will end up on the screen,
+            // but might need to skip applying the custom pixel shader.
+            if (showBuffer >= currentBuffer)
+            {
+                effect.CurrentTechnique.Passes[0].Apply();
+            }
+
+            // Draw the quad.
+            spriteBatch.Draw(texture, new Rectangle(0, 0, width, height), Color.White);
+            spriteBatch.End();
+
+            // End the custom effect.
+            /* NO LONGER NEEDED if (showBuffer >= currentBuffer)
+            {
+                effect.CurrentTechnique.Passes[0].End();
+                effect.End();
+            } */
+        }
+
+
+        /// <summary>
+        /// Computes sample weightings and texture coordinate offsets
+        /// for one pass of a separable gaussian blur filter.
+        /// </summary>
+        void SetBlurEffectParameters(float dx, float dy)
+        {
+            // Look up the sample weight and offset effect parameters.
+            EffectParameter weightsParameter, offsetsParameter;
+
+            weightsParameter = gaussianBlurEffect.Parameters["SampleWeights"];
+            offsetsParameter = gaussianBlurEffect.Parameters["SampleOffsets"];
+
+            // Look up how many samples our gaussian blur effect supports.
+            int sampleCount = weightsParameter.Elements.Count;
+
+            // Create temporary arrays for computing our filter settings.
+            float[] sampleWeights = new float[sampleCount];
+            Vector2[] sampleOffsets = new Vector2[sampleCount];
+
+            // The first sample always has a zero offset.
+            sampleWeights[0] = ComputeGaussian(0);
+            sampleOffsets[0] = new Vector2(0);
+
+            // Maintain a sum of all the weighting values.
+            float totalWeights = sampleWeights[0];
+
+            // Add pairs of additional sample taps, positioned
+            // along a line in both directions from the center.
+            for (int i = 0; i < sampleCount / 2; i++)
+            {
+                // Store weights for the positive and negative taps.
+                float weight = ComputeGaussian(i + 1);
+
+                sampleWeights[i * 2 + 1] = weight;
+                sampleWeights[i * 2 + 2] = weight;
+
+                totalWeights += weight * 2;
+
+                // To get the maximum amount of blurring from a limited number of
+                // pixel shader samples, we take advantage of the bilinear filtering
+                // hardware inside the texture fetch unit. If we position our texture
+                // coordinates exactly halfway between two texels, the filtering unit
+                // will average them for us, giving two samples for the price of one.
+                // This allows us to step in units of two texels per sample, rather
+                // than just one at a time. The 1.5 offset kicks things off by
+                // positioning us nicely in between two texels.
+                float sampleOffset = i * 2 + 1.5f;
+
+                Vector2 delta = new Vector2(dx, dy) * sampleOffset;
+
+                // Store texture coordinate offsets for the positive and negative taps.
+                sampleOffsets[i * 2 + 1] = delta;
+                sampleOffsets[i * 2 + 2] = -delta;
+            }
+
+            // Normalize the list of sample weightings, so they will always sum to one.
+            for (int i = 0; i < sampleWeights.Length; i++)
+            {
+                sampleWeights[i] /= totalWeights;
+            }
+
+            // Tell the effect about our new filter settings.
+            weightsParameter.SetValue(sampleWeights);
+            offsetsParameter.SetValue(sampleOffsets);
+        }
+
+
+        /// <summary>
+        /// Evaluates a single point on the gaussian falloff curve.
+        /// Used for setting up the blur filter weightings.
+        /// </summary>
+        float ComputeGaussian(float n)
+        {
+            float theta = Settings.BlurAmount;
+
+            return (float)((1.0 / Math.Sqrt(2 * Math.PI * theta)) *
+                           Math.Exp(-(n * n) / (2 * theta * theta)));
+        }
+
+
+        #endregion
+    }
+}

+ 80 - 0
StarterKits/MacOS/VectorRumble/BloomPostprocess/BloomSettings.cs

@@ -0,0 +1,80 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// BloomSettings.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Class holds all the settings used to tweak the bloom effect.
+    /// </summary>
+    /// <remarks>
+    /// This class is similar to one of the same name in the Bloom sample.
+    /// </remarks>
+    public class BloomSettings
+    {
+        #region Fields
+
+
+        // Name of a preset bloom setting, for display to the user.
+        public readonly string Name;
+
+
+        // Controls how bright a pixel needs to be before it will bloom.
+        // Zero makes everything bloom equally, while higher values select
+        // only brighter colors. Somewhere between 0.25 and 0.5 is good.
+        public readonly float BloomThreshold;
+
+
+        // Controls how much blurring is applied to the bloom image.
+        // The typical range is from 1 up to 10 or so.
+        public readonly float BlurAmount;
+
+
+        // Controls the amount of the bloom and base images that
+        // will be mixed into the final scene. Range 0 to 1.
+        public readonly float BloomIntensity;
+        public readonly float BaseIntensity;
+
+
+        // Independently control the color saturation of the bloom and
+        // base images. Zero is totally desaturated, 1.0 leaves saturation
+        // unchanged, while higher values increase the saturation level.
+        public readonly float BloomSaturation;
+        public readonly float BaseSaturation;
+
+
+        #endregion
+
+
+        /// <summary>
+        /// Constructs a new bloom settings descriptor.
+        /// </summary>
+        public BloomSettings(string name, float bloomThreshold, float blurAmount,
+                             float bloomIntensity, float baseIntensity,
+                             float bloomSaturation, float baseSaturation)
+        {
+            Name = name;
+            BloomThreshold = bloomThreshold;
+            BlurAmount = blurAmount;
+            BloomIntensity = bloomIntensity;
+            BaseIntensity = baseIntensity;
+            BloomSaturation = bloomSaturation;
+            BaseSaturation = baseSaturation;
+        }
+        
+
+        /// <summary>
+        /// Table of preset bloom settings, used by the sample program.
+        /// </summary>
+        public static BloomSettings[] PresetSettings =
+        {
+            //                Name           Thresh  Blur Bloom  Base  BloomSat BaseSat
+            new BloomSettings("VectorRumble",  0f,    2,   3f,    1,      2,       1),
+        };
+    }
+}

+ 66 - 0
StarterKits/MacOS/VectorRumble/CollectCollection.cs

@@ -0,0 +1,66 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// CollectCollection.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A collection with an additional feature to isolate the "collection"
+    /// of the objects.
+    /// </summary>
+    sealed class CollectCollection<T>
+        : System.Collections.ObjectModel.Collection<T>
+    {
+        #region Fields and Properties
+        private World world;
+        public World World
+        {
+            get { return world; }
+        }
+
+        private System.Collections.ObjectModel.Collection<T> garbage;
+        public System.Collections.ObjectModel.Collection<T> Garbage
+        {
+            get { return garbage; }
+            set { garbage = value; }
+        }
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new collection.
+        /// </summary>
+        /// <param name="world">The world this particle system resides in.</param>
+        public CollectCollection(World world)
+        {
+            this.world = world;
+            this.garbage =
+            new System.Collections.ObjectModel.Collection<T>();
+        }
+        #endregion
+
+        #region Collection
+        /// <summary>
+        /// Remove all of the "garbage" ParticleSystem objects from this collection.
+        /// </summary>
+        public void Collect()
+        {
+            for (int i = 0; i < garbage.Count; i++)
+            {
+                Remove(garbage[i]);
+            }
+            garbage.Clear();
+        }
+        #endregion
+    }
+}

BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/RacingGame Game Music 1.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/damage1.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/engine_3.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/explosion1.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/explosion2.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/explosion3.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/hax2_fire.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/hax2_fire2.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/menu_back.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/menu_scroll.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/menu_select3.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/phase_expire_alt.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/tx0_fire1.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/tx0_fire2.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/tx0_fire3.wav


BIN
StarterKits/MacOS/VectorRumble/Content/Audio/Waves/weapon_pickup_alt_retro.wav


+ 51 - 0
StarterKits/MacOS/VectorRumble/Content/Effects/BloomCombine.fx

@@ -0,0 +1,51 @@
+// Pixel shader combines the bloom image with the original
+// scene, using tweakable intensity levels and saturation.
+// This is the final step in applying a bloom postprocess.
+
+sampler BloomSampler : register(s0);
+sampler BaseSampler : register(s1);
+
+float BloomIntensity;
+float BaseIntensity;
+
+float BloomSaturation;
+float BaseSaturation;
+
+
+// Helper for modifying the saturation of a color.
+float4 AdjustSaturation(float4 color, float saturation)
+{
+    // The constants 0.3, 0.59, and 0.11 are chosen because the
+    // human eye is more sensitive to green light, and less to blue.
+    float grey = dot(color, float3(0.3, 0.59, 0.11));
+
+    return lerp(grey, color, saturation);
+}
+
+
+float4 PixelShader(float2 texCoord : TEXCOORD0) : COLOR0
+{
+    // Look up the bloom and original base image colors.
+    float4 bloom = tex2D(BloomSampler, texCoord);
+    float4 base = tex2D(BaseSampler, texCoord);
+    
+    // Adjust color saturation and intensity.
+    bloom = AdjustSaturation(bloom, BloomSaturation) * BloomIntensity;
+    base = AdjustSaturation(base, BaseSaturation) * BaseIntensity;
+    
+    // Darken down the base image in areas where there is a lot of bloom,
+    // to prevent things looking excessively burned-out.
+    base *= (1 - saturate(bloom));
+    
+    // Combine the two images.
+    return base + bloom;
+}
+
+
+technique BloomCombine
+{
+    pass Pass1
+    {
+        PixelShader = compile ps_2_0 PixelShader();
+    }
+}

+ 25 - 0
StarterKits/MacOS/VectorRumble/Content/Effects/BloomExtract.fx

@@ -0,0 +1,25 @@
+// Pixel shader extracts the brighter areas of an image.
+// This is the first step in applying a bloom postprocess.
+
+sampler TextureSampler : register(s0);
+
+float BloomThreshold;
+
+
+float4 PixelShader(float2 texCoord : TEXCOORD0) : COLOR0
+{
+    // Look up the original image color.
+    float4 c = tex2D(TextureSampler, texCoord);
+
+    // Adjust it to keep only values brighter than the specified threshold.
+    return saturate((c - BloomThreshold) / (1 - BloomThreshold));
+}
+
+
+technique BloomExtract
+{
+    pass Pass1
+    {
+        PixelShader = compile ps_2_0 PixelShader();
+    }
+}

+ 33 - 0
StarterKits/MacOS/VectorRumble/Content/Effects/GaussianBlur.fx

@@ -0,0 +1,33 @@
+// Pixel shader applies a one dimensional gaussian blur filter.
+// This is used twice by the bloom postprocess, first to
+// blur horizontally, and then again to blur vertically.
+
+sampler TextureSampler : register(s0);
+
+#define SAMPLE_COUNT 15
+
+float2 SampleOffsets[SAMPLE_COUNT];
+float SampleWeights[SAMPLE_COUNT];
+
+
+float4 PixelShader(float2 texCoord : TEXCOORD0) : COLOR0
+{
+    float4 c = 0;
+    
+    // Combine a number of weighted image filter taps.
+    for (int i = 0; i < SAMPLE_COUNT; i++)
+    {
+        c += tex2D(TextureSampler, texCoord + SampleOffsets[i]) * SampleWeights[i];
+    }
+    
+    return c;
+}
+
+
+technique GaussianBlur
+{
+    pass Pass1
+    {
+        PixelShader = compile ps_2_0 PixelShader();
+    }
+}

+ 48 - 0
StarterKits/MacOS/VectorRumble/Content/Fonts/retroMedium.spritefont

@@ -0,0 +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>Verdana</FontName>
+
+    <!--
+    Size is a float value, measured in points. Modify this value to change
+    the size of the font.
+    -->
+    <Size>18</Size>
+
+    <!--
+    Spacing is a float value, measured in pixels. Modify this value to change
+    the amount of spacing in between characters.
+    -->
+    <Spacing>1</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>

BIN
StarterKits/MacOS/VectorRumble/Content/Fonts/retroMedium.xnb


+ 48 - 0
StarterKits/MacOS/VectorRumble/Content/Fonts/retroSmall.spritefont

@@ -0,0 +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>Verdana</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>1</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>

BIN
StarterKits/MacOS/VectorRumble/Content/Fonts/retroSmall.xnb


BIN
StarterKits/MacOS/VectorRumble/Content/Textures/blank.png


BIN
StarterKits/MacOS/VectorRumble/Content/Textures/title.png


+ 108 - 0
StarterKits/MacOS/VectorRumble/Game.cs

@@ -0,0 +1,108 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Game.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+
+#if ANDROID
+using Android.App;
+#endif
+
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Storage;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// This is the main type for your game
+    /// </summary>
+    public class VectorRumbleGame : Microsoft.Xna.Framework.Game
+    {
+        GraphicsDeviceManager graphics;
+
+        ScreenManager screenManager;
+        AudioManager audioManager;
+
+
+        public VectorRumbleGame()
+        {
+            graphics = new GraphicsDeviceManager(this);
+            Content.RootDirectory = "Content";
+			
+#if IOS || ANDROID
+            graphics.PreferredBackBufferWidth = 640;
+            graphics.PreferredBackBufferHeight = 480;
+#else
+			graphics.PreferredBackBufferWidth = 1280;
+            graphics.PreferredBackBufferHeight = 720;
+#endif
+			graphics.SynchronizeWithVerticalRetrace = true;
+
+            // create the screen manager
+            screenManager = new ScreenManager(this);
+            Components.Add(screenManager);
+
+            // create the audio manager
+            audioManager = new AudioManager(this, 
+                "Content\\Audio\\VectorRumble.xgs", 
+                "Content\\Audio\\VectorRumble.xwb",
+                "Content\\Audio\\VectorRumble.xsb");
+            Services.AddService(typeof(AudioManager), audioManager);
+        }
+
+
+        /// <summary>
+        /// Overridden from Game.Initialize().  Sets up the ScreenManager.
+        /// </summary>
+        protected override void Initialize()
+        {
+            base.Initialize();
+
+            // add the background screen to the screen manager
+            screenManager.AddScreen(new BackgroundScreen());
+
+            // add the main menu screen to the screen manager
+            screenManager.AddScreen(new MainMenuScreen());
+        }
+
+
+        /// <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)
+        {
+            // update the audio manager
+            audioManager.Update(gameTime);
+
+            base.Update(gameTime);
+        }
+
+
+        /// <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);
+
+            // the screen manager owns the real drawing
+
+            base.Draw(gameTime);
+        }
+    }
+}

+ 143 - 0
StarterKits/MacOS/VectorRumble/Gameplay/Asteroid.cs

@@ -0,0 +1,143 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Asteroid.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Asteroids that fill the game simulation, blocking the player's 
+    /// shots and movements.
+    /// </summary>
+    class Asteroid : Actor
+    {
+        #region Constants
+        /// <summary>
+        /// The ratio between the mass and the radius of an asteroid.
+        /// </summary>
+        const float massRadiusRatio = 4f;
+
+        /// <summary>
+        /// The amount of drag applied to velocity per second, 
+        /// as a percentage of velocity.
+        /// </summary>
+        const float dragPerSecond = 0.20f;
+
+        /// <summary>
+        /// Scalar for calculated damage values that asteroids apply to players.
+        /// </summary>
+        const float damageScalar = 0.001f;
+
+        /// <summary>
+        /// Scalar to convert the velocity / mass ratio into a "nice" rotational value.
+        /// </summary>
+        const float velocityMassRatioToRotationScalar = 0.01f;
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Construct a new asteroid.
+        /// </summary>
+        /// <param name="world">The world that this asteroid belongs to.</param>
+        /// <param name="radius">The size of the asteroid.</param>
+        public Asteroid(World world, float radius)
+            : base(world)
+        {
+            // all asteroids are gray
+            this.color = Color.Gray;
+            // create the polygon
+            this.polygon = VectorPolygon.CreateAsteroid(radius);
+            // the asteroid polygon might not be as big as the original radius, 
+            // so find out how big it really is
+            for (int i = 0; i < this.polygon.Points.Length; i++)
+            {
+                float length = this.polygon.Points[i].Length();
+                if (length > this.radius)
+                {
+                    this.radius = length;
+                }
+            }
+            // calculate the mass
+            this.mass = radius * massRadiusRatio;
+        }
+        #endregion
+
+        #region Update
+        /// <summary>
+        /// Update the actor.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        public override void Update(float elapsedTime)
+        {
+            // spin the asteroid based on the size and velocity
+            this.rotation += (this.velocity.LengthSquared() / this.mass) * elapsedTime *
+                velocityMassRatioToRotationScalar;
+
+            // apply some drag so the asteroids settle down
+            velocity -= velocity * (elapsedTime * dragPerSecond);
+
+            base.Update(elapsedTime);
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Defines the interaction between the asteroid and a target actor
+        /// when they touch.
+        /// </summary>
+        /// <param name="target">The actor that is touching this object.</param>
+        /// <returns>True if the objects meaningfully interacted.</returns>
+        public override bool Touch(Actor target)
+        {
+            // if the asteroid has touched a player, then damage it
+            Ship player = target as Ship;
+            if (player != null)
+            {
+                // calculate damage as a function of how much the two actor's
+                // velocities were going towards one another
+                Vector2 playerAsteroidVector = 
+                    Vector2.Normalize(this.position - player.Position);
+                float rammingSpeed = 
+                    Vector2.Dot(playerAsteroidVector, player.Velocity) - 
+                    Vector2.Dot(playerAsteroidVector, this.velocity);
+                player.Damage(this, this.mass * rammingSpeed * damageScalar);
+
+            }
+            // if the asteroid didn't hit a projectile, play the asteroid-touch cue
+            if ((target is Projectile) == false)
+            {
+                this.world.AudioManager.PlayCue("asteroidTouch");
+            }
+            return base.Touch(target);
+        }
+
+        
+        /// <summary>
+        /// Damage this asteroid by the amount provided.
+        /// </summary>
+        /// <remarks>
+        /// This function is provided in lieu of a Life mutation property to allow 
+        /// classes of objects to restrict which kinds of objects may damage them,
+        /// and under what circumstances they may be damaged.
+        /// </remarks>
+        /// <param name="source">The actor responsible for the damage.</param>
+        /// <param name="damageAmount">The amount of damage.</param>
+        /// <returns>If true, this object was damaged.</returns>
+        public override bool Damage(Actor source, float damageAmount)
+        {
+            // nothing hurst asteroids, nothing!
+            return false;
+        }
+        #endregion
+    }
+}

+ 58 - 0
StarterKits/MacOS/VectorRumble/Gameplay/DoubleLaserPowerUp.cs

@@ -0,0 +1,58 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// DoubleLaserPowerUp.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A power-up that gives a player a triple-laser-shooting weapon.
+    /// </summary>
+    class DoubleLaserPowerUp : PowerUp
+    {
+        #region Fields
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new DoubleLaserPowerUp.
+        /// </summary>
+        /// <param name="world">The world that this power-up belongs to.</param>
+        public DoubleLaserPowerUp(World world) 
+            : base(world) 
+        { 
+            this.color = Color.Lime;
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Defines the interaction between this power-up and a target actor
+        /// when they touch.
+        /// </summary>
+        /// <param name="target">The actor that is touching this object.</param>
+        /// <returns>True if the objects meaningfully interacted.</returns>
+        public override bool Touch(Actor target)
+        {
+            // if we hit a ship, give it the weapon
+            Ship ship = target as Ship;
+            if (ship != null)
+            {
+                ship.SetWeapon(new DoubleLaserWeapon(ship));
+            }
+
+            return base.Touch(target);
+        }
+        #endregion
+    }
+}

+ 66 - 0
StarterKits/MacOS/VectorRumble/Gameplay/DoubleLaserWeapon.cs

@@ -0,0 +1,66 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// DoubleLaserWeapon.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A weapon that shoots a double stream of laser projectiles.
+    /// </summary>
+    class DoubleLaserWeapon : LaserWeapon
+    {
+        #region Constants
+        /// <summary>
+        /// The distance that the laser bolts are moved off of the owner's position.
+        /// </summary>
+        const float laserSpread = 8f;
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new double-laser weapon.
+        /// </summary>
+        /// <param name="owner">The ship that owns this weapon.</param>
+        public DoubleLaserWeapon(Ship owner)
+            : base(owner) { }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Create and spawn the projectile(s) from a firing from this weapon.
+        /// </summary>
+        /// <param name="direction">The direction that the projectile will move.</param>
+        protected override void CreateProjectiles(Vector2 direction)
+        {
+            // calculate the spread of the laser bolts
+            Vector2 cross = Vector2.Multiply(new Vector2(-direction.Y, direction.X),
+                laserSpread);
+
+            // create the new projectile
+            LaserProjectile projectile = new LaserProjectile(owner.World, owner,
+                direction);
+            // adjust the position for the laser spread
+            projectile.Position += cross;
+            // spawn the projectile
+            projectile.Spawn(false);
+
+            // create the second projectile
+            projectile = new LaserProjectile(owner.World, owner, direction);
+            // adjust the position for the laser spread
+            projectile.Position -= cross;
+            // spawn the projectile
+            projectile.Spawn(false);
+        }
+        #endregion
+    }
+}

+ 99 - 0
StarterKits/MacOS/VectorRumble/Gameplay/LaserProjectile.cs

@@ -0,0 +1,99 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// LaserProjectile.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A laser bolt projectile.
+    /// </summary>
+    class LaserProjectile : Projectile
+    {
+        #region Constants
+        /// <summary>
+        /// The length of the laser-bolt line, expressed as a percentage of velocity.
+        /// </summary>
+        const float lineLengthVelocityPercent = 0.01f;
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new laser projectile.
+        /// </summary>
+        /// <param name="world">The world that this projectile belongs to.</param>
+        /// <param name="owner">The ship that fired this projectile, if any.</param>
+        /// <param name="direction">The initial direction for this projectile.</param>
+        public LaserProjectile(World world, Ship owner, Vector2 direction)
+            : base(world, owner, direction)
+        {
+            this.radius = 0.5f;
+            this.speed = 640f;
+            this.duration = 5f;
+            this.damageAmount = 20f;
+            this.damageOwner = false;
+            this.mass = 0.5f;
+            this.explodes = false;
+            this.explosionColors = new Color[] 
+                { Color.White, Color.Gray, Color.Gray, Color.Silver, Color.Yellow };
+        }
+        #endregion
+
+        #region Drawing
+        /// <summary>
+        /// Render the actor.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        /// <param name="lineBatch">The LineBatch to render to.</param>
+        public override void Draw(float elapsedTime, LineBatch lineBatch)
+        {
+            if (lineBatch == null)
+            {
+                throw new ArgumentNullException("lineBatch");
+            }
+            // draw a simple line
+            lineBatch.DrawLine(position, 
+                position - velocity * lineLengthVelocityPercent, Color.Yellow);
+        }
+        #endregion 
+
+        #region Interaction
+        /// <summary>
+        /// Defines the interaction between this projectile and a target actor
+        /// when they touch.
+        /// </summary>
+        /// <param name="target">The actor that is touching this object.</param>
+        /// <returns>True if the objects meaningfully interacted.</returns>
+        public override bool Touch(Actor target)
+        {
+            // add a particle effect if we touched anything
+            if (base.Touch(target))
+            {
+                // make the particle effect slightly more significant if it was a ship
+                if (target is Ship)
+                {
+                    world.ParticleSystems.Add(new ParticleSystem(this.position,
+                        Vector2.Zero, 16, 32f, 64f, 1f, 0.1f, explosionColors));
+                }
+                else
+                {
+                    world.ParticleSystems.Add(new ParticleSystem(this.position,
+                        Vector2.Zero, 4, 32f, 64f, 1f, 0.05f, explosionColors));
+                }
+                return true;
+            }
+            return false;
+        }
+        #endregion
+    }
+}

+ 50 - 0
StarterKits/MacOS/VectorRumble/Gameplay/LaserWeapon.cs

@@ -0,0 +1,50 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// LaserWeapon.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A weapon that shoots a single stream of laser projectiles.
+    /// </summary>
+    class LaserWeapon : Weapon
+    {
+        #region Initialization
+        /// <summary>
+        /// Constructs a new laser weapon.
+        /// </summary>
+        /// <param name="owner">The ship that owns this weapon.</param>
+        public LaserWeapon(Ship owner)
+            : base(owner)
+        {
+            fireDelay = 0.15f;
+            fireCueName = "laserBlaster";            
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Create and spawn the projectile(s) from a firing from this weapon.
+        /// </summary>
+        /// <param name="direction">The direction that the projectile will move.</param>
+        protected override void CreateProjectiles(Vector2 direction)
+        {
+            // create the new projectile
+            LaserProjectile projectile = new LaserProjectile(owner.World, owner,
+                direction);
+            // spawn the projectile
+            projectile.Spawn(false);
+        }
+        #endregion
+    }
+}

+ 96 - 0
StarterKits/MacOS/VectorRumble/Gameplay/MineProjectile.cs

@@ -0,0 +1,96 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MineProjectile.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A mine projectile.
+    /// </summary>
+    class MineProjectile : Projectile
+    {
+        #region Constants
+        /// <summary>
+        /// The amount of drag applied to velocity per second, 
+        /// as a percentage of velocity.
+        /// </summary>
+        const float dragPerSecond = 0.9f;
+
+        /// <summary>
+        /// The radians-per-second that this object rotates at.
+        /// </summary>
+        const float rotationRadiansPerSecond = 1f;
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new mine projectile.
+        /// </summary>
+        /// <param name="world">The world that this projectile belongs to.</param>
+        /// <param name="owner">The ship that fired this projectile, if any.</param>
+        /// <param name="direction">The initial direction for this projectile.</param>
+        public MineProjectile(World world, Ship owner, Vector2 direction)
+            : base(world, owner, direction)
+        {
+            this.radius = 16f;
+            this.life = 15f;
+            this.speed = 64f;
+            this.duration = 15f;
+            this.mass = 5f;
+            this.damageAmount = 200f;
+            this.damageOwner = true;
+            this.damageRadius = 80f;
+            this.explodes = true;
+            this.explosionColors = new Color[] 
+                { Color.Red, Color.Maroon, Color.White, Color.Silver };
+            this.polygon = VectorPolygon.CreateMine();
+            this.color = Color.Red;
+        }
+        #endregion
+
+        #region Update
+        /// <summary>
+        /// Update the mine.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        public override void Update(float elapsedTime)
+        {
+            base.Update(elapsedTime);
+
+            this.velocity -= velocity * (elapsedTime * dragPerSecond);
+            this.rotation += elapsedTime * rotationRadiansPerSecond;
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Damages all actors in a radius around the mine.
+        /// </summary>
+        /// <param name="touchedActor">The actor that was originally hit.</param>
+        public override void Explode(Actor touchedActor)
+        {
+            // play the explosion cue
+            world.AudioManager.PlayCue("explosionLarge");
+
+            // add a double-particle system effect
+            world.ParticleSystems.Add(new ParticleSystem(this.position,
+                Vector2.Zero, 64, 32f, 64f, 3f, 0.05f, explosionColors));
+            world.ParticleSystems.Add(new ParticleSystem(this.position,
+                Vector2.Zero, 16, 128f, 256f, 4f, 0.1f, explosionColors));
+
+            base.Explode(touchedActor);
+        }
+        #endregion
+    }
+}

+ 59 - 0
StarterKits/MacOS/VectorRumble/Gameplay/MineWeapon.cs

@@ -0,0 +1,59 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MineWeapon.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A weapon that fires a single mine on a long timer.
+    /// </summary>
+    class MineWeapon : Weapon
+    {
+        #region Constants
+        /// <summary>
+        /// The distance that the mine spawns behind the ship.
+        /// </summary>
+        const float mineSpawnDistance = 8f;
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new mine-laying weapon.
+        /// </summary>
+        /// <param name="owner">The ship that owns this weapon.</param>
+        public MineWeapon(Ship owner)
+            : base(owner)
+        {
+            fireDelay = 2f;         
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Create and spawn the projectile(s) from a firing from this weapon.
+        /// </summary>
+        /// <param name="direction">The direction that the projectile will move.</param>
+        protected override void CreateProjectiles(Vector2 direction)
+        {
+            // create the new projectile
+            MineProjectile projectile = new MineProjectile(owner.World, owner,
+                direction);
+            // move the mine out from the ship
+            projectile.Position = owner.Position + 
+                direction * (owner.Radius + projectile.Radius + mineSpawnDistance);
+            // spawn the projectile
+            projectile.Spawn(false);
+        }
+        #endregion
+    }
+}

+ 55 - 0
StarterKits/MacOS/VectorRumble/Gameplay/RocketPowerUp.cs

@@ -0,0 +1,55 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// RocketPowerUp.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A power-up that gives a player a rocket-launching weapon.
+    /// </summary>
+    class RocketPowerUp : PowerUp
+    {
+        #region Initialization
+        /// <summary>
+        /// Constructs a new rocket-launcher power-up.
+        /// </summary>
+        /// <param name="world">The world that this power-up belongs to.</param>
+        public RocketPowerUp(World world)
+            : base(world) 
+        { 
+            this.color = Color.Orange;
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Defines the interaction between this power-up and a target actor
+        /// when they touch.
+        /// </summary>
+        /// <param name="target">The actor that is touching this object.</param>
+        /// <returns>True if the objects meaningfully interacted.</returns>
+        public override bool Touch(Actor target)
+        {
+            // if we hit a ship, give it the weapon
+            Ship ship = target as Ship;
+            if (ship != null)
+            {
+                ship.SetWeapon(new RocketWeapon(ship));
+            }
+
+            return base.Touch(target);
+        }
+        #endregion
+    }
+}

+ 106 - 0
StarterKits/MacOS/VectorRumble/Gameplay/RocketProjectile.cs

@@ -0,0 +1,106 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// RocketProjectile.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Audio;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A rocket projectile.
+    /// </summary>
+    class RocketProjectile : Projectile
+    {
+        #region Fields
+        /// <summary>
+        /// The sound effect of the rocket as it flies.
+        /// </summary>
+        protected Cue rocketCue = null;
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new rocket projectile.
+        /// </summary>
+        /// <param name="world">The world that this projectile belongs to.</param>
+        /// <param name="owner">The ship that fired this projectile, if any.</param>
+        /// <param name="direction">The initial direction for this projectile.</param>
+        public RocketProjectile(World world, Ship owner, Vector2 direction)
+            : base(world, owner, direction)
+        {
+            this.radius = 8f;
+            this.life = 80f;
+            this.mass = 3f;
+            this.speed = 520f;
+            this.duration = 4f;
+            this.damageAmount = 100f;
+            this.damageOwner = false;
+            this.damageRadius = 128f;
+            this.explodes = true;
+            this.explosionColors = new Color[] 
+                { Color.Orange, Color.Gray, Color.Gray, Color.Silver };
+            this.polygon = VectorPolygon.CreateRocket();
+            this.color = Color.Orange;
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Damages all actors in a radius around the rocket.
+        /// </summary>
+        /// <param name="touchedActor">The actor that was originally hit.</param>
+        public override void Explode(Actor touchedActor)
+        {
+            // stop the rocket-flying cue
+            if (rocketCue != null)
+            {
+                rocketCue.Stop(AudioStopOptions.Immediate);
+                rocketCue.Dispose();
+                rocketCue = null;
+            }
+
+            // play the explosion cue
+            world.AudioManager.PlayCue("explosionMedium");
+
+            // add a double-particle system effect
+            world.ParticleSystems.Add(new ParticleSystem(this.position,
+                Vector2.Zero, 64, 32f, 64f, 3f, 0.05f, explosionColors));
+            world.ParticleSystems.Add(new ParticleSystem(this.position,
+                Vector2.Zero, 16, 128f, 256f, 4f, 0.1f, explosionColors));
+
+            base.Explode(touchedActor);
+        }
+
+
+        /// <summary>
+        /// Place this rocket in the world.
+        /// </summary>
+        /// <param name="findSpawnPoint">
+        /// If true, the rocket's position is changed to a valid, non-colliding point.
+        /// </param>
+        public override void Spawn(bool findSpawnPoint)
+        {
+            base.Spawn(findSpawnPoint);
+
+            // get and play the rocket-flying cue
+            rocketCue = world.AudioManager.GetCue("rocket");
+            if (rocketCue != null)
+            {
+                rocketCue.Play();
+            }
+            // twitch the motors of the launching ship
+            owner.FireGamepadMotors(0f, 0.25f);
+        }
+        #endregion
+    }
+}

+ 57 - 0
StarterKits/MacOS/VectorRumble/Gameplay/RocketWeapon.cs

@@ -0,0 +1,57 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// RocketWeapon.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A weapon that shoots a rockets.
+    /// </summary>
+    class RocketWeapon : Weapon
+    {
+        #region Initialization
+        /// <summary>
+        /// Constructs a new rocket-launching weapon.
+        /// </summary>
+        /// <param name="owner">The ship that owns this weapon.</param>
+        public RocketWeapon(Ship owner)
+            : base(owner)
+        {
+            fireDelay = 0.75f;
+            fireCueName = "rocketFire";            
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Create and spawn the projectile(s) from a firing from this weapon.
+        /// </summary>
+        /// <param name="direction">The direction that the projectile will move.</param>
+        protected override void CreateProjectiles(Vector2 direction)
+        {
+            // calculate the rocket's rotation
+            float rotation = (float)Math.Acos(Vector2.Dot(new Vector2(0f, -1f),
+                direction));
+            rotation *= (Vector2.Dot(new Vector2(0f, -1f),
+                new Vector2(direction.Y, -direction.X)) > 0f) ? 1f : -1f;
+
+            // create the new projectile
+            RocketProjectile projectile = new RocketProjectile(owner.World, owner,
+                direction);
+            projectile.Rotation = rotation;
+            // spawn the projectile
+            projectile.Spawn(false);
+        }
+        #endregion
+    }
+}

+ 708 - 0
StarterKits/MacOS/VectorRumble/Gameplay/Ship.cs

@@ -0,0 +1,708 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Ship.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// The ship, which is the primary playing-piece in the game.
+    /// </summary>
+    class Ship : Actor
+    {
+        #region Constants
+        /// <summary>
+        /// The value of the spawn timer set when the ship dies.
+        /// </summary>
+        const float respawnTimerOnDeath = 5f;
+
+        /// <summary>
+        /// How long, in seconds, for the ship to fade in.
+        /// </summary>
+        const float fadeInTimerMaximum = 0.5f;
+
+        /// <summary>
+        /// The maximum value of the "safe" timer.
+        /// </summary>
+        const float safeTimerMaximum = 4f;
+
+        /// <summary>
+        /// The amount of drag applied to velocity per second, 
+        /// as a percentage of velocity.
+        /// </summary>
+        const float dragPerSecond = 0.9f;
+
+        /// <summary>
+        /// The amount that the right-stick must be pressed to fire, squared so that
+        /// we can use LengthSquared instead of Length, which has a square-root in it.
+        /// </summary>
+        const float fireThresholdSquared = 0.25f;
+
+        /// <summary>
+        /// The number of radians that the ship can turn in a second at full left-stick.
+        /// </summary>
+        const float rotationRadiansPerSecond = 6f;
+
+        /// <summary>
+        /// The maximum length of the velocity vector on a ship.
+        /// </summary>
+        const float velocityLengthMaximum = 320f;
+
+        /// <summary>
+        /// The maximum strength of the shield.
+        /// </summary>
+        const float shieldMaximum = 100f;
+
+        /// <summary>
+        /// How much the shield recharges per second.
+        /// </summary>
+        const float shieldRechargePerSecond = 50f;
+
+        /// <summary>
+        /// The duration of the shield-recharge timer when the ship is hit.
+        /// </summary>
+        const float shieldRechargeTimerOnDamage = 2.5f;
+
+        /// <summary>
+        /// The amount at which to vibrate the large motor if the timer is active.
+        /// </summary>
+        const float largeMotorSpeed = 0.5f;
+
+        /// <summary>
+        /// The amount at which to vibrate the small motor if the timer is active.
+        /// </summary>
+        const float smallMotorSpeed = 0.5f;
+
+        /// <summary>
+        /// The amount of time that the A button must be held to join the game.
+        /// </summary>
+        const float aButtonHeldToPlay = 2f;
+
+        /// <summary>
+        /// The amount of time that the B button must be held to leave the game.
+        /// </summary>
+        const float bButtonHeldToLeave = 2f;
+
+        /// <summary>
+        /// The number of radians that the shield rotates per second.
+        /// </summary>
+        const float shieldRotationPeriodPerSecond = 2f;
+
+        /// <summary>
+        /// The relationship between the shield rotation and it's scale.
+        /// </summary>
+        const float shieldRotationToScaleScalar = 0.025f;
+
+        /// <summary>
+        /// The relationship between the shield rotation and it's scale period.
+        /// </summary>
+        const float shieldRotationToScalePeriodScalar = 4f;
+
+        /// <summary>
+        /// The colors used for each ship, given it's player-index.
+        /// </summary>
+        static readonly Color[] shipColorsByPlayerIndex = 
+            {
+                Color.Lime, Color.CornflowerBlue, Color.Fuchsia, Color.Red
+            };
+
+        /// <summary>
+        /// Particle system colors for the ship-explosion effect.
+        /// </summary>
+        static readonly Color[] explosionColors = 
+            { 
+                Color.Red, Color.Red, Color.Silver, Color.Gray, Color.Orange, 
+                Color.Yellow 
+            };
+        #endregion
+
+        #region Fields
+        /// <summary>
+        /// If true, this ship is active in-game.
+        /// </summary>
+        private bool playing = false;
+
+        /// <summary>
+        /// The current score for this ship.
+        /// </summary>
+        private int score = 0;
+
+        /// <summary>
+        /// The speed at which the ship moves.
+        /// </summary>
+        private float speed = 480f;
+
+        /// <summary>
+        /// The strength of the shield.
+        /// </summary>
+        private float shield = 0f;
+
+        /// <summary>
+        /// The rotation of the shield effect.
+        /// </summary>
+        private float shieldRotation = 0f;
+
+        /// <summary>
+        /// The polygon used to render the shield effect
+        /// </summary>
+        private VectorPolygon shieldPolygon = null;
+
+        /// <summary>
+        /// The ship's current weapon.
+        /// </summary>
+        private Weapon weapon = null;
+
+        /// <summary>
+        /// The ship's additional mine-laying weapon.
+        /// </summary>
+        private MineWeapon mineWeapon = null;
+        
+        /// <summary>
+        /// The Gamepad player index that is controlling this ship.
+        /// </summary>
+        private PlayerIndex playerIndex;
+
+        /// <summary>
+        /// The current state of the Gamepad that is controlling this ship.
+        /// </summary>
+        private GamePadState currentGamePadState;
+
+        /// <summary>
+        /// The previous state of the Gamepad that is controlling this ship.
+        /// </summary>
+        private GamePadState lastGamePadState;
+
+        /// <summary>
+        /// Timer for how long the player has been holding the A button (to join).
+        /// </summary>
+        private float aButtonTimer = 0f;
+
+        /// <summary>
+        /// Timer for how long the player has been holding the B button (to leave).
+        /// </summary>
+        private float bButtonTimer = 0f;
+
+        /// <summary>
+        /// Timer for how much longer to vibrate the small motor.
+        /// </summary>
+        private float smallMotorTimer = 0f;
+
+        /// <summary>
+        /// Timer for how much longer to vibrate the large motor.
+        /// </summary>
+        private float largeMotorTimer = 0f;
+
+        /// <summary>
+        /// Timer for how much longer the player has to wait for the ship to respawn.
+        /// </summary>
+        private float respawnTimer = 0f;
+
+        /// <summary>
+        /// Timer for how long until the shield starts to recharge.
+        /// </summary>
+        private float shieldRechargeTimer = 0f;
+
+        /// <summary>
+        /// Timer for how long the player is safe after spawning.
+        /// </summary>
+        private float safeTimer = 0f;
+
+        /// <summary>
+        /// Timer for how long the player has been spawned for, to fade in
+        /// </summary>
+        private float fadeInTimer = 0f;
+        #endregion
+
+        #region Properties
+        public bool Playing
+        {
+            get { return playing; }
+        }
+
+        public bool Safe
+        {
+            get { return (safeTimer > 0f); }
+            set
+            {
+                if (value)
+                {
+                    safeTimer = safeTimerMaximum;
+                }
+                else
+                {
+                    safeTimer = 0f;
+                }
+            }
+        }
+
+        public int Score
+        {
+            get { return score; }
+            set { score = value; }
+        }
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Construct a new ship, for the given player.
+        /// </summary>
+        /// <param name="world">The world that this ship belongs to.</param>
+        /// <param name="playerIndex">
+        /// The Gamepad player index that controls this ship.
+        /// </param>
+        public Ship(World world, PlayerIndex playerIndex)
+            : base(world)
+        {
+            this.playerIndex = playerIndex;
+
+            this.radius = 20f;
+            this.mass = 32f;
+            this.color = shipColorsByPlayerIndex[(int)this.playerIndex];
+            this.polygon = VectorPolygon.CreatePlayer();
+            this.shieldPolygon = VectorPolygon.CreateCircle(Vector2.Zero, 20f, 16);
+        }
+        #endregion
+
+        #region Update
+        /// <summary>
+        /// Update the ship.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        public override void Update(float elapsedTime)
+        {
+            // process all input
+            ProcessInput(elapsedTime, false);
+
+            // if this player isn't in the game, then quit now
+            if (playing == false)
+            {
+                return;
+            }
+
+            if (dead == true)
+            {
+            // if we've died, then we're counting down to respawning
+                if (respawnTimer > 0f)
+                {
+                    respawnTimer = Math.Max(respawnTimer - elapsedTime, 0f);
+                }
+                if (respawnTimer <= 0f)
+                {
+                    Spawn(true);
+                }
+            }
+            else
+            {
+                // apply drag to the velocity
+                velocity -= velocity * (elapsedTime * dragPerSecond);
+                if (velocity.LengthSquared() <= 0f)
+                {
+                    velocity = Vector2.Zero;
+                }
+                // decrement the heal timer if necessary
+                if (shieldRechargeTimer > 0f)
+                {
+                    shieldRechargeTimer = Math.Max(shieldRechargeTimer - elapsedTime, 
+                        0f);
+                }
+                // recharge the shields if the timer has come up
+                if (shieldRechargeTimer <= 0f)
+                {
+                    if (shield < 100f)
+                    {
+                        shield = Math.Min(100f,
+                            shield + shieldRechargePerSecond * elapsedTime);
+                    }
+                }
+            }
+
+            // update the weapons
+            if (weapon != null)
+            {
+                weapon.Update(elapsedTime);
+            }
+            if (mineWeapon != null)
+            {
+                mineWeapon.Update(elapsedTime);
+            }
+
+            // decrement the safe timer
+            if (safeTimer > 0f)
+            {
+                safeTimer = Math.Max(safeTimer - elapsedTime, 0f);
+            }
+
+            // update the radius based on the shield
+            radius = (shield > 0f) ? 20f : 14f;
+
+            // update the spawn-in timer
+            if (fadeInTimer < fadeInTimerMaximum)
+            {
+                fadeInTimer = Math.Min(fadeInTimer + elapsedTime,
+                    fadeInTimerMaximum);
+            }
+
+            // update and apply the vibration
+            smallMotorTimer -= elapsedTime;
+            largeMotorTimer -= elapsedTime;
+            GamePad.SetVibration(playerIndex,
+                (largeMotorTimer > 0f) ? largeMotorSpeed : 0f,
+                (smallMotorTimer > 0f) ? smallMotorSpeed : 0f);
+            
+            base.Update(elapsedTime);
+        }
+        #endregion
+
+        #region Drawing
+        /// <summary>
+        /// Render the ship.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        /// <param name="lineBatch">The LineBatch to render to.</param>
+        public override void Draw(float elapsedTime, LineBatch lineBatch)
+        {
+            // if the ship isn't in the game, or it's dead, don't draw
+            if ((playing == false) || (dead == true))
+            {
+                return;
+            }
+            // update the shield rotation
+            shieldRotation += elapsedTime * shieldRotationPeriodPerSecond;
+            // calculate the current color
+            color = new Color(color.R, color.G, color.B, (byte)(255f * fadeInTimer / 
+                fadeInTimerMaximum));
+            // transform the shield polygon
+            Matrix translationMatrix = Matrix.CreateTranslation(position.X, 
+                position.Y, 0f);
+            shieldPolygon.Transform(Matrix.CreateScale(1f + shieldRotationToScaleScalar 
+                * (float)Math.Cos(shieldRotation * shieldRotationToScalePeriodScalar)) *
+                Matrix.CreateRotationZ(shieldRotation) * translationMatrix);
+            // draw the shield
+            if (Safe)
+            {
+                lineBatch.DrawPolygon(shieldPolygon, color);
+            }
+            else if (shield > 0f)
+            {
+                lineBatch.DrawPolygon(shieldPolygon, new Color(color.R, color.G, 
+                    color.B, (byte)(255f * shield / shieldMaximum)), true);
+            }
+            base.Draw(elapsedTime, lineBatch);
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Set the ship up to join the game, if it's not in it already.
+        /// </summary>
+        public void JoinGame()
+        {
+            if (playing == false)
+            {
+                playing = true;
+                score = 0;
+                Spawn(true);
+            }
+        }
+
+
+        /// <summary>
+        /// Remove the ship from the game, if it's in it.
+        /// </summary>
+        public void LeaveGame()
+        {
+            if (playing == true)
+            {
+                playing = false;
+                Die(null);
+            }
+        }
+
+
+        /// <summary>
+        /// Assigns the new weapon to the ship.
+        /// </summary>
+        /// <param name="weapon">The new weapon.</param>
+        public void SetWeapon(Weapon weapon)
+        {
+            if (weapon != null)
+            {
+                this.weapon = weapon;
+            }
+        }
+
+
+        /// <summary>
+        /// Damage this ship by the amount provided.
+        /// </summary>
+        /// <remarks>
+        /// This function is provided in lieu of a Life mutation property to allow 
+        /// classes of objects to restrict which kinds of objects may damage them,
+        /// and under what circumstances they may be damaged.
+        /// </remarks>
+        /// <param name="source">The actor responsible for the damage.</param>
+        /// <param name="damageAmount">The amount of damage.</param>
+        /// <returns>If true, this object was damaged.</returns>
+        public override bool Damage(Actor source, float damageAmount)
+        {
+            // if the safe timer hasn't yet gone off, then the ship can't be hurt
+            if (safeTimer > 0f)
+            {
+                return false;
+            }
+
+            // tickle the gamepad vibration motors
+            FireGamepadMotors(0f, 0.25f);
+
+            // once you're hit, the shield-recharge timer starts over
+            shieldRechargeTimer = 2.5f;
+
+            // damage the shield first, then life
+            if (shield <= 0f)
+            {
+                life -= damageAmount;
+            }
+            else
+            {
+                shield -= damageAmount;
+                if (shield < 0f)
+                {
+                    // shield has the overflow value as a negative value, just add it
+                    life += shield;
+                    shield = 0f;
+                }
+            }
+
+            // if the ship is out of life, it dies
+            if (life < 0f)
+            {
+                Die(source);
+            }
+
+            return true;
+        }
+
+
+        /// <summary>
+        /// Kills this ship, in response to the given actor.
+        /// </summary>
+        /// <param name="source">The actor responsible for the kill.</param>
+        public override void Die(Actor source)
+        {
+            if (dead == false)
+            {
+                // hit the gamepad vibration motors
+                FireGamepadMotors(0.75f, 0.25f);
+                // play several explosion cues
+                world.AudioManager.PlayCue("playerDeath");
+                // add several particle systems for effect
+                world.ParticleSystems.Add(new ParticleSystem(this.position,
+                    Vector2.Zero, 128, 64f, 256f, 3f, 0.05f, explosionColors));
+                world.ParticleSystems.Add(new ParticleSystem(this.position,
+                    Vector2.Zero, 64, 256f, 1024f, 3f, 0.05f, explosionColors));
+                // reset the respawning timer
+                respawnTimer = respawnTimerOnDeath;
+
+                // change the score
+                Ship ship = source as Ship;
+                if (ship == null)
+                {
+                    Projectile projectile = source as Projectile;
+                    if (projectile != null)
+                    {
+                        ship = projectile.Owner;
+                    }
+                }
+                if (ship != null)
+                {
+                    if (ship == this)
+                    {
+                        // reduce the score, since i blew myself up
+                        ship.Score--;
+                    }
+                    else
+                    {
+                        // add score to the ship who shot this object
+                        ship.Score++;
+                    }
+                }
+                else
+                {
+                    // if it wasn't a ship, then this object loses score
+                    this.Score--;
+                }
+
+                // ships should not be added to the garbage list, so just set dead
+                dead = true;
+            }
+        }
+
+
+        /// <summary>
+        /// Place this ship in the world.
+        /// </summary>
+        /// <param name="findSpawnPoint">
+        /// If true, the actor's position is changed to a valid, non-colliding point.
+        /// </param>
+        public override void Spawn(bool findSpawnPoint)
+        {
+            // do not call the base Spawn, as the actor never is added or removed when
+            // dying or respawning, because we always need to be processing input
+            // respawn this actor
+            if (dead == true)
+            {
+                // I LIVE
+                dead = false;
+                // find a new spawn point if requested
+                if (findSpawnPoint)
+                {
+                    position = world.FindSpawnPoint(this);
+                }
+                // reset the velocity
+                velocity = Vector2.Zero;
+                // reset the shield and life values
+                life = 25f;
+                shield = shieldMaximum;
+                // reset the safety timers
+                safeTimer = safeTimerMaximum;
+                // create the default weapons
+                weapon = new LaserWeapon(this);
+                mineWeapon = new MineWeapon(this);
+                // play the ship-spawn cue
+                world.AudioManager.PlayCue("playerSpawn");
+                // add a particle effect at the ship's new location
+                world.ParticleSystems.Add(new ParticleSystem(this.position, 
+                    Vector2.Zero, 32, 32f, 64f, 2f, 0.1f, new Color[] { this.color }));
+                // remind the player that we're spawning
+                FireGamepadMotors(0.25f, 0f);
+            }
+        }
+        #endregion
+
+        #region Input Methods
+        /// <summary>
+        /// Vibrate the gamepad motors for the given period of time.
+        /// </summary>
+        /// <param name="largeMotorTime">The time to run the large motor.</param>
+        /// <param name="smallMotorTime">The time to run the small motor.</param>
+        public void FireGamepadMotors(float largeMotorTime, float smallMotorTime)
+        {
+            // use the maximum timer value
+            this.largeMotorTimer = Math.Max(this.largeMotorTimer, largeMotorTime);
+            this.smallMotorTimer = Math.Max(this.smallMotorTimer, smallMotorTime);
+        }
+
+
+        /// <summary>
+        /// Process the input for this ship, from the gamepad assigned to it.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        /// <para
+        public virtual void ProcessInput(float elapsedTime, bool overlayPresent)
+        {
+            currentGamePadState = GamePad.GetState(playerIndex);
+            if (overlayPresent == false)
+            {
+                if (playing == false)
+                {
+                    // trying to join - update the a-button timer
+                    if (currentGamePadState.Buttons.A == ButtonState.Pressed)
+                    {
+                        aButtonTimer += elapsedTime;
+                    }
+                    else
+                    {
+                        aButtonTimer = 0f;
+                    }
+
+                    // if the timer has exceeded the expected value, join the game
+                    if (aButtonTimer > aButtonHeldToPlay)
+                    {
+                        JoinGame();
+                    }
+                }
+                else
+                {
+                    // check if we're trying to leave
+                    if (currentGamePadState.Buttons.B == ButtonState.Pressed)
+                    {
+                        bButtonTimer += elapsedTime;
+                    }
+                    else
+                    {
+                        bButtonTimer = 0f;
+                    }
+                    // if the timer has exceeded the expected value, leave the game
+                    if (bButtonTimer > bButtonHeldToLeave)
+                    {
+                        LeaveGame();
+                    }
+                    else if (dead == false)
+                    {
+                        //
+                        // the ship is alive, so process movement and firing
+                        //
+                        // calculate the current forward vector
+                        Vector2 forward = new Vector2((float)Math.Sin(Rotation),
+                            -(float)Math.Cos(Rotation));
+                        Vector2 right = new Vector2(-forward.Y, forward.X);
+                        // calculate the current left stick value
+                        Vector2 leftStick = currentGamePadState.ThumbSticks.Left;
+                        leftStick.Y *= -1f;
+                        if (leftStick.LengthSquared() > 0f)
+                        {
+                            Vector2 wantedForward = Vector2.Normalize(leftStick);
+                            float angleDiff = (float)Math.Acos(
+                                Vector2.Dot(wantedForward, forward));
+                            float facing = (Vector2.Dot(wantedForward, right) > 0f) ?
+                                1f : -1f;
+                            if (angleDiff > 0f)
+                            {
+                                Rotation += Math.Min(angleDiff, facing * elapsedTime *
+                                    rotationRadiansPerSecond);
+                            }
+                            // add velocity
+                            Velocity += leftStick * (elapsedTime * speed);
+                            if (Velocity.Length() > velocityLengthMaximum)
+                            {
+                                Velocity = Vector2.Normalize(Velocity) *
+                                    velocityLengthMaximum;
+                            }
+
+                        }
+                        // check for firing with the right stick
+                        Vector2 rightStick = currentGamePadState.ThumbSticks.Right;
+                        rightStick.Y *= -1f;
+                        if (rightStick.LengthSquared() > fireThresholdSquared)
+                        {
+                            weapon.Fire(Vector2.Normalize(rightStick));
+                        }
+                        // check for laying mines
+                        if ((currentGamePadState.Buttons.B == ButtonState.Pressed) &&
+                            (lastGamePadState.Buttons.B == ButtonState.Released))
+                        {
+                            // fire behind the ship
+                            mineWeapon.Fire(-forward);
+                        }
+                    }
+                }
+            }
+
+            // update the gamepad state
+            lastGamePadState = currentGamePadState;
+            return;
+        }
+        #endregion
+    }
+}

+ 55 - 0
StarterKits/MacOS/VectorRumble/Gameplay/TripleLaserPowerUp.cs

@@ -0,0 +1,55 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// TripleLaserPowerUp.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A power-up that gives a player a triple-laser-shooting weapon.
+    /// </summary>
+    class TripleLaserPowerUp : PowerUp
+    {
+        #region Initialization
+        /// <summary>
+        /// Constructs a new triple-laser power-up.
+        /// </summary>
+        /// <param name="world">The world that this power-up belongs to.</param>
+        public TripleLaserPowerUp(World world)
+            : base(world) 
+        {
+            this.color = Color.CornflowerBlue;
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Defines the interaction between this power-up and a target actor
+        /// when they touch.
+        /// </summary>
+        /// <param name="target">The actor that is touching this object.</param>
+        /// <returns>True if the objects meaningfully interacted.</returns>
+        public override bool Touch(Actor target)
+        {
+            // if we hit a ship, give it the weapon
+            Ship ship = target as Ship;
+            if (ship != null)
+            {
+                ship.SetWeapon(new TripleLaserWeapon(ship));
+            }
+
+            return base.Touch(target);
+        }
+        #endregion
+    }
+}

+ 77 - 0
StarterKits/MacOS/VectorRumble/Gameplay/TripleLaserWeapon.cs

@@ -0,0 +1,77 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// TripleLaserWeapon.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A weapon that shoots a triple stream of laser projectiles.
+    /// </summary>
+    class TripleLaserWeapon : LaserWeapon
+    {
+        #region Constants
+        /// <summary>
+        /// The spread of the second and third laser projectiles' directions, in radians
+        /// </summary>
+        static readonly float laserSpreadRadians = MathHelper.ToRadians(2.5f);
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new triple-laser weapon.
+        /// </summary>
+        /// <param name="owner">The ship that owns this weapon.</param>
+        public TripleLaserWeapon(Ship owner)
+            : base(owner) { }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Create and spawn the projectile(s) from a firing from this weapon.
+        /// </summary>
+        /// <param name="direction">The direction that the projectile will move.</param>
+        protected override void CreateProjectiles(Vector2 direction)
+        {
+            // calculate the direction vectors for the second and third projectiles
+            float rotation = (float)Math.Acos(Vector2.Dot(new Vector2(0f, -1f), 
+                direction));
+            rotation *= (Vector2.Dot(new Vector2(0f, -1f), 
+                new Vector2(direction.Y, -direction.X)) > 0f) ? 1f : -1f;
+            Vector2 direction2 = new Vector2(
+                 (float)Math.Sin(rotation - laserSpreadRadians), 
+                -(float)Math.Cos(rotation - laserSpreadRadians));
+            Vector2 direction3 = new Vector2(
+                 (float)Math.Sin(rotation + laserSpreadRadians), 
+                -(float)Math.Cos(rotation + laserSpreadRadians));
+
+            // create the first projectile
+            LaserProjectile projectile = new LaserProjectile(owner.World, owner,
+                direction);
+            // spawn the projectile
+            projectile.Spawn(false);
+
+            // create the second projectile
+            projectile = new LaserProjectile(owner.World, owner,
+                direction2);
+            // spawn the projectile
+            projectile.Spawn(false);
+
+            // create the third projectile
+            projectile = new LaserProjectile(owner.World, owner,
+                direction3);
+            // spawn the projectile
+            projectile.Spawn(false);
+        }
+        #endregion
+    }
+}

+ 16 - 0
StarterKits/MacOS/VectorRumble/Info.plist

@@ -0,0 +1,16 @@
+<?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.VectorRumble</string>
+	<key>CFBundleName</key>
+	<string>VectorRumble</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>10.6</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>

+ 18 - 0
StarterKits/MacOS/VectorRumble/Main.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Drawing;
+using MonoMac.Foundation;
+using MonoMac.AppKit;
+using MonoMac.ObjCRuntime;
+
+namespace VectorRumble
+{
+	class MainClass
+	{
+		static void Main (string [] args)
+		{
+			NSApplication.Init ();
+			NSApplication.Main (args);
+		}
+	}
+}	
+

+ 33 - 0
StarterKits/MacOS/VectorRumble/Program.cs

@@ -0,0 +1,33 @@
+using MonoMac.AppKit;
+using MonoMac.Foundation;
+
+namespace VectorRumble
+{
+	class Program
+	{
+		static void Main (string [] args)
+		{
+			NSApplication.Init ();
+			
+			using (var p = new NSAutoreleasePool ()) {
+				NSApplication.SharedApplication.Delegate = new AppDelegate();
+				NSApplication.Main(args);
+			}
+		}
+	}
+	
+	class AppDelegate : NSApplicationDelegate
+	{
+		public override void FinishedLaunching (MonoMac.Foundation.NSObject notification)
+		{			
+			using (VectorRumbleGame game = new VectorRumbleGame()) {
+				game.Run ();
+			}
+		}
+		
+		public override bool ApplicationShouldTerminateAfterLastWindowClosed (NSApplication sender)
+		{
+			return true;
+		}
+	}	
+}

+ 34 - 0
StarterKits/MacOS/VectorRumble/Properties/AssemblyInfo.cs

@@ -0,0 +1,34 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Vector Rumble")]
+[assembly: AssemblyProduct("Vector Rumble")]
+[assembly: AssemblyDescription(
+    "Players battle it out in an arena, collecting power-ups and avoiding asteroids.")]
+[assembly: AssemblyCompany("Microsoft")]
+
+[assembly: AssemblyCopyright("Copyright © Microsoft 2008")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("bced8acc-bdb8-4e82-92f2-cbfb0dc20742")]
+
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]

+ 271 - 0
StarterKits/MacOS/VectorRumble/Rendering/LineBatch.cs

@@ -0,0 +1,271 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// LineBatch.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Batches line "draw" calls from the game, and renders them at one time.
+    /// </summary>
+    class LineBatch
+    {
+        #region Constants
+        /// <summary>
+        /// The maximum number of vertices in the LineBatch vertex array.
+        /// </summary>
+        const int maxVertexCount = 512;
+        #endregion
+
+        #region Fields
+        /// <summary>
+        /// The graphics device that renders the lines.
+        /// </summary>
+        GraphicsDevice graphicsDevice;
+        
+        /// <summary>
+        /// The effect applied to the lines.
+        /// </summary>
+        BasicEffect effect;
+        
+        /// <summary>
+        /// The vertex declaration which is defines the line vertices.
+        /// </summary>
+        VertexDeclaration vertexDeclaration;
+        
+        /// <summary>
+        /// The line vertices.
+        /// </summary>
+        VertexPositionColor[] vertices;
+
+        /// <summary>
+        /// The current index being "drawn" into the array.
+        /// </summary>
+        int currentIndex;
+
+        /// <summary>
+        /// The current number of lines to be draw.
+        /// </summary>
+        int lineCount;
+		
+		///
+		public static BlendState LineBlendState = new BlendState()
+        {
+			AlphaBlendFunction = BlendFunction.Add,
+			ColorSourceBlend = Blend.SourceAlpha,
+			ColorDestinationBlend = Blend.InverseSourceAlpha,
+        };
+        #endregion
+
+        #region Initialization
+        public LineBatch(GraphicsDevice graphicsDevice)
+        {
+            // assign the graphics device parameter after safety-checking
+            if (graphicsDevice == null)
+            {
+                throw new ArgumentNullException("graphicsDevice");
+            }
+            this.graphicsDevice = graphicsDevice;
+			
+#if !IOS && !MONOMAC && !ANDROID
+            // create and configure the effect
+            this.effect = new BasicEffect(graphicsDevice, null);
+            this.effect.VertexColorEnabled = true;
+            this.effect.TextureEnabled = false;
+            this.effect.LightingEnabled = false;
+            // configure the effect
+            this.effect.World = Matrix.Identity;
+            this.effect.View = Matrix.CreateLookAt(Vector3.Zero, Vector3.Forward,
+                Vector3.Up);
+#endif
+            
+			var vd = VertexPositionColor.VertexDeclaration.GetVertexElements();
+            // create the vertex declaration
+            this.vertexDeclaration = new VertexDeclaration(vd);
+
+            // create the vertex array
+            this.vertices = new VertexPositionColor[maxVertexCount];
+        }
+
+        /// <summary>
+        /// Set the new projection for the line batch effect.
+        /// </summary>
+        /// <param name="projection"></param>
+        public void SetProjection(Matrix projection)
+        {
+            if (effect != null)
+            {
+                effect.Projection = projection;
+            }
+        }
+        #endregion
+
+        #region Drawing
+        /// <summary>
+        /// Configures the object and the device to begin drawing lines.
+        /// </summary>
+        public void Begin()
+        {
+            // reset the counters
+            currentIndex = 0;
+            lineCount = 0;
+        }
+
+
+        /// <summary>
+        /// Draw a line from one point to another with the same color.
+        /// </summary>
+        /// <param name="start">The starting point.</param>
+        /// <param name="end">The ending point.</param>
+        /// <param name="color">The color throughout the line.</param>
+        public void DrawLine(Vector2 start, Vector2 end, Color color)
+        {
+            DrawLine(
+                new VertexPositionColor(new Vector3(start, 0f), color),
+                new VertexPositionColor(new Vector3(end, 0f), color));
+        }
+
+
+        /// <summary>
+        /// Draw a line from one point to another with different colors at each end.
+        /// </summary>
+        /// <param name="start">The starting point.</param>
+        /// <param name="end">The ending point.</param>
+        /// <param name="startColor">The color at the starting point.</param>
+        /// <param name="endColor">The color at the ending point.</param>
+        public void DrawLine(Vector2 start, Vector2 end, 
+            Color startColor, Color endColor)
+        {
+            DrawLine(
+                new VertexPositionColor(new Vector3(start, 0f), startColor),
+                new VertexPositionColor(new Vector3(end, 0f), endColor));
+        }
+
+
+        /// <summary>
+        /// Draws a line from one vertex to another.
+        /// </summary>
+        /// <param name="start">The starting vertex.</param>
+        /// <param name="end">The ending vertex.</param>
+        public void DrawLine(VertexPositionColor start, VertexPositionColor end)
+        {
+            if (currentIndex >= (vertices.Length - 2))
+            {
+                End();
+                Begin();
+            }
+
+            vertices[currentIndex++] = start;
+            vertices[currentIndex++] = end;
+
+            lineCount++;
+        }
+
+
+        /// <summary>
+        /// Draws the given polygon.
+        /// </summary>
+        /// <param name="polygon">The polygon to render.</param>
+        /// <param name="color">The color to use when drawing the polygon.</param>
+        public void DrawPolygon(VectorPolygon polygon, Color color)
+        {
+            DrawPolygon(polygon, color, false);
+        }
+
+
+        /// <summary>
+        /// Draws the given polygon.
+        /// </summary>
+        /// <param name="polygon">The polygon to render.</param>
+        /// <param name="color">The color to use when drawing the polygon.</param>
+        /// <param name="dashed">If true, the polygon will be "dashed".</param>
+        public void DrawPolygon(VectorPolygon polygon, Color color, bool dashed)
+        {
+            if (polygon == null)
+            {
+                throw new ArgumentNullException("polygon");
+            }
+            int step = (dashed == true) ? 2 : 1;
+            for (int i = 0; i < polygon.TransformedPoints.Length; i += step)
+            {
+                if (currentIndex >= vertices.Length - 2)
+                {
+                    End();
+                    Begin();
+                }
+                vertices[currentIndex].Position.X = 
+                    polygon.TransformedPoints[i % polygon.TransformedPoints.Length].X;
+                vertices[currentIndex].Position.Y = 
+                    polygon.TransformedPoints[i % polygon.TransformedPoints.Length].Y;
+                vertices[currentIndex++].Color = color;
+                vertices[currentIndex].Position.X = 
+                    polygon.TransformedPoints[(i + 1) % 
+                        polygon.TransformedPoints.Length].X;
+                vertices[currentIndex].Position.Y =
+                    polygon.TransformedPoints[(i + 1) % 
+                        polygon.TransformedPoints.Length].Y;
+                vertices[currentIndex++].Color = color;
+                lineCount++;
+            }
+        }
+
+
+        /// <summary>
+        /// Ends the batch of lines, submitting them to the graphics device.
+        /// </summary>
+        public void End()
+        {
+            // if we don't have any vertices, then we can exit early
+            if (currentIndex == 0)
+            {
+                return;
+            }
+
+            // configure the graphics device and effect
+            graphicsDevice.VertexDeclaration = vertexDeclaration;
+
+            // configure the graphics device to render our lines			
+            graphicsDevice.BlendState = LineBlendState;
+
+            // run the effect
+#if !IOS && !MONOMAC && !ANDROID
+			foreach (EffectPass pass in effect.CurrentTechnique.Passes)
+			{
+				pass.Apply();
+				graphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, vertices, 0, lineCount);
+			}
+#endif
+        }
+        #endregion
+
+        #region Destruction
+        /// <summary>
+        /// Disposes of the object, cleanly releasing graphics resources.
+        /// </summary>
+        public void Dispose()
+        {
+            if (effect != null)
+            {
+                effect.Dispose();
+                effect = null;
+            }
+            if (vertexDeclaration != null)
+            {
+                vertexDeclaration.Dispose();
+                vertexDeclaration = null;
+            }
+        }
+        #endregion
+    }
+}

+ 27 - 0
StarterKits/MacOS/VectorRumble/Rendering/Particle.cs

@@ -0,0 +1,27 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Particle.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// The data for a single particle in this game's particle systems.
+    /// </summary>
+    struct Particle
+    {
+        public Vector2 Position;
+        public Vector2 Velocity;
+        public Color Color;
+    }
+}

+ 235 - 0
StarterKits/MacOS/VectorRumble/Rendering/ParticleSystem.cs

@@ -0,0 +1,235 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ParticleSystem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A system for maintaining and rendering particles in this game.
+    /// </summary>
+    class ParticleSystem
+    {
+        #region Constants
+        /// <summary>
+        /// The amount that the alpha on each particle diminishes per second.
+        /// </summary>
+        const float alphaReductionPerSecond = 45f;
+
+        /// <summary>
+        /// The percent that the velocity on each particle diminishes per second.
+        /// </summary>
+        const float velocityPercentReductionPerSecond = 0.98f;
+        #endregion
+
+        #region Fields
+        static Random random = new Random();
+
+        /// <summary>
+        /// The amount of time left before this particle system disappears.
+        /// </summary>
+        float lifeRemaining;
+
+        /// <summary>
+        /// The number of particles in this particle system.
+        /// </summary>
+        int count;
+
+        /// <summary>
+        /// The total lifetime of the particle system.
+        /// </summary>
+        float life;
+
+        /// <summary>
+        /// The list of particles in this system.
+        /// </summary>
+        Particle[] particles = null;
+
+        /// <summary>
+        /// The position of the particle system.
+        /// </summary>
+        Vector2 position;
+
+        /// <summary>
+        /// The direction that this particle system is moving in.
+        /// </summary>
+        Vector2 direction;
+
+        /// <summary>
+        /// The minimum velocity of particles when the system starts.
+        /// </summary>
+        float minVelocity;
+
+        /// <summary>
+        /// The maximum velocity of particles when the system starts.
+        /// </summary>
+        float maxVelocity;
+
+        /// <summary>
+        /// The length of the lines drawn for each particle.
+        /// </summary>
+        float tailLength;
+
+        /// <summary>
+        /// The colors used for the particles.
+        /// </summary>
+        Color[] colors;
+        #endregion
+
+        #region Properties
+        /// <summary>
+        /// If true, the particle system is still updating and rendering.
+        /// </summary>
+        public bool IsActive
+        {
+            get
+            {
+                return (lifeRemaining > 0f);
+            }
+        }
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new particle system object using the given parameters.
+        /// </summary>
+        /// <param name="position">The position of the system.</param>
+        /// <param name="direction">The direction that the system is moving in.</param>
+        /// <param name="count">The number of particles in the system.</param>
+        /// <param name="minVelocity">The minimum velocity of particles.</param>
+        /// <param name="maxVelocity">The maximum velocity of particles.</param>
+        /// <param name="life">The lifetime of the system.</param>
+        /// <param name="tailLength">The length of the tails of the particles.</param>
+        /// <param name="colors">The colors of the particles.</param>
+        public ParticleSystem(Vector2 position, Vector2 direction, int count, 
+            float minVelocity, float maxVelocity, float life, float tailLength, 
+            params Color[] colors)
+        {
+            Reset(position, direction, count, minVelocity, maxVelocity, life,
+                tailLength, colors);
+        }
+
+        /// <summary>
+        /// Resets the particle system object using the new values.
+        /// </summary>
+        /// <param name="position">The position of the system.</param>
+        /// <param name="direction">The direction that the system is moving in.</param>
+        /// <param name="count">The number of particles in the system.</param>
+        /// <param name="minVelocity">The minimum velocity of particles.</param>
+        /// <param name="maxVelocity">The maximum velocity of particles.</param>
+        /// <param name="life">The lifetime of the system.</param>
+        /// <param name="tailLength">The length of the tails of the particles.</param>
+        /// <param name="colors">The colors of the particles.</param>
+        public void Reset(Vector2 position, Vector2 direction, int count,
+            float minVelocity, float maxVelocity, float life, float tailLength,
+            params Color[] colors)
+        {
+            // assign the parameters
+            this.count = Math.Max(0, count);
+            this.life = life;
+            this.position = position;
+            this.direction = direction;
+            this.minVelocity = minVelocity;
+            this.maxVelocity = maxVelocity;
+            this.tailLength = tailLength;
+            this.colors = colors;
+            if ((this.colors == null) || (this.colors.Length < 1))
+            {
+                colors = new Color[1];
+                colors[0] = Color.White;
+            }
+
+            // recreate the particle array if necessary
+            if ((particles == null) || (particles.Length != this.count))
+            {
+                particles = new Particle[this.count];
+            }
+
+            Reset();
+        }
+
+        /// <summary>
+        /// Reset the particle system to it's initial state, using the last set of 
+        /// parameters.
+        /// </summary>
+        public void Reset()
+        {
+            // set each particle to it's default starting state
+            for (int i = 0; i < particles.Length; i++)
+            {
+                particles[i].Position = this.position;
+                particles[i].Velocity = new Vector2(
+                    1.0f - 2.0f * (float)random.NextDouble(), 
+                    1.0f - 2.0f * (float)random.NextDouble());
+                particles[i].Velocity.Normalize();
+                particles[i].Velocity *= this.minVelocity + 
+                    this.maxVelocity * (float)random.NextDouble();
+                particles[i].Velocity += this.direction;
+                particles[i].Color = this.colors[random.Next(this.colors.Length)];
+            }
+
+            // reset the remaining lifetime on the particle system
+            lifeRemaining = life;
+        }
+        #endregion
+
+        #region Update and Draw
+        /// <summary>
+        /// Update the particle system.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        public virtual void Update(float elapsedTime)
+        {
+            if (lifeRemaining > 0f)
+            {
+                // update each particle
+                for (int i = 0; i < particles.Length; i++)
+                {
+                    particles[i].Position += particles[i].Velocity * elapsedTime;
+                    particles[i].Color = new Color(
+                        particles[i].Color.R,
+                        particles[i].Color.G,
+                        particles[i].Color.B,
+                        (byte)((float)particles[i].Color.A - 
+                            alphaReductionPerSecond * elapsedTime)
+                        );
+                    particles[i].Velocity -= particles[i].Velocity * 
+                        (velocityPercentReductionPerSecond * elapsedTime);
+                }
+                this.lifeRemaining -= elapsedTime;
+            }
+        }
+
+        /// <summary>
+        /// Render the particle system.
+        /// </summary>
+        /// <param name="lineBatch">The line batch which draws all the particles</param>
+        public virtual void Draw(LineBatch lineBatch)
+        {
+            if (lifeRemaining > 0f)
+            {
+                if (lineBatch == null)
+                {
+                    throw new ArgumentNullException("lineBatch");
+                }
+                for (int i = 0; i < particles.Length; i++)
+                {
+                    lineBatch.DrawLine(particles[i].Position,
+                        particles[i].Position - particles[i].Velocity * tailLength,
+                        particles[i].Color);
+                }
+            }
+        }
+        #endregion
+    }
+}

+ 149 - 0
StarterKits/MacOS/VectorRumble/Rendering/Starfield.cs

@@ -0,0 +1,149 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Starfield.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A starfield represented by single-pixel "stars", with parallax-scrolling.
+    /// </summary>
+    class Starfield
+    {
+        #region Constants
+        /// <summary>
+        /// The distance that the stars move per second.
+        /// </summary>
+        const float starVelocity = 32f;
+        #endregion
+
+        #region Fields
+        Random random;
+
+        /// <summary>
+        /// The positions of each star.
+        /// </summary>
+        Vector2[] starPositions;
+
+        /// <summary>
+        /// The depth of each star, used for parallax.
+        /// </summary>
+        byte[] starDepths;
+
+        /// <summary>
+        /// The relative "target position" of the starfield, compared with the
+        /// currentPosition field to create motion.
+        /// </summary>
+        Vector2 targetPosition;
+
+        /// <summary>
+        /// The relative "current position" of the starfield, compared with the
+        /// targetPosition field to create motion.
+        /// </summary>
+        Vector2 currentPosition;
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Construct a new starfield.
+        /// </summary>
+        /// <param name="count">The number of stars in the starfield.</param>
+        /// <param name="bounds">The bounds of star generation.</param>
+        public Starfield(int count, Rectangle bounds)
+        {
+            if (count <= 0)
+            {
+                throw new ArgumentOutOfRangeException("count");
+            }
+
+            random = new Random();
+            currentPosition = targetPosition = new Vector2(
+                bounds.Left + (bounds.Right - bounds.Left) / 2,
+                bounds.Top + (bounds.Bottom - bounds.Top) / 2);
+            starPositions = new Vector2[count];
+            starDepths = new byte[count];
+            for (int i = 0; i < count; i++)
+            {
+                starPositions[i] = new Vector2(
+                    random.Next(bounds.Left, bounds.Right),
+                    random.Next(bounds.Top, bounds.Bottom));
+                starDepths[i] = (byte)random.Next(1, 255);
+            }
+        }
+        #endregion
+
+        #region Update and Draw
+        /// <summary>
+        /// Update the starfield.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        public void Update(float elapsedTime)
+        {
+            if (targetPosition != currentPosition)
+            {
+                Vector2 movement = targetPosition - currentPosition;
+                // if the movement is within 1 square in each direction, 
+                // then we're close enough
+                if (movement.LengthSquared() < 1.414f)  // approxmation of sqrt(2)
+                {
+                    currentPosition = targetPosition;
+                    return;
+                }
+                // move the current position
+                movement = Vector2.Normalize(movement) * (starVelocity * elapsedTime);
+                currentPosition += movement;
+                // move the stars, scaled by their depth
+                for (int i = 0; i < starPositions.Length; i++)
+                {
+                    starPositions[i] += movement * ((float)starDepths[i] / 255f);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Render the starfield.
+        /// </summary>
+        /// <param name="spriteBatch">The spritebatch that renders the stars.</param>
+        /// <param name="spriteTexture">The simple texture used when rendering.</param>
+        public void Draw(SpriteBatch spriteBatch, Texture2D spriteTexture)
+        {
+            if (spriteBatch == null)
+            {
+                throw new ArgumentNullException("spriteBatch");
+            }
+            if (spriteTexture == null)
+            {
+                throw new ArgumentNullException("spriteTexture");
+            }
+            for (int i = 0; i < starPositions.Length; i++)
+            {
+                Color starColor = new Color(starDepths[i], starDepths[i],
+                    starDepths[i]);
+                spriteBatch.Draw(spriteTexture, new Rectangle((int)starPositions[i].X,
+                    (int)starPositions[i].Y, 1, 1), starColor);
+            }
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Assign the target position for the starfield to scroll to.
+        /// </summary>
+        /// <param name="targetPosition">The target position.</param>
+        public void SetTargetPosition(Vector2 targetPosition)
+        {
+            this.targetPosition = targetPosition;
+        }
+        #endregion
+    }
+}

+ 181 - 0
StarterKits/MacOS/VectorRumble/Rendering/VectorPolygon.cs

@@ -0,0 +1,181 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// VectorPolygon.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A series of points that may be drawn together to form a line.
+    /// </summary>
+    class VectorPolygon
+    {
+        #region Fields and Properties
+        /// <summary>
+        /// The raw set of points, in "model space".
+        /// </summary>
+        private Vector2[] points;
+        public Vector2[] Points
+        {
+            get { return points; }
+        }
+
+        /// <summary>
+        /// The transformed points, typically in "world" space
+        /// </summary>
+        private Vector2[] transformedPoints;
+        public Vector2[] TransformedPoints
+        {
+            get { return transformedPoints; }
+        }
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new VectorPolygon object from the given points.
+        /// </summary>
+        /// <param name="points">The raw set of points.</param>
+        public VectorPolygon(Vector2[] points)
+        {
+            this.points = points;
+            this.transformedPoints = (Vector2[])points.Clone();
+        }
+        #endregion
+
+        #region Transformation
+        /// <summary>
+        /// Transform the raw points by the matrix given.
+        /// </summary>
+        /// <param name="matrix">The transformation matrix.</param>
+        public void Transform(Matrix matrix)
+        {
+            Vector2.Transform(points, 0, ref matrix, transformedPoints, 0,
+                points.Length);
+        }
+        #endregion
+
+        #region Static Constructors
+        private static Random random = new Random();
+        
+        
+        /// <summary>
+        /// Creates a polygon in the shape of a circle.
+        /// </summary>
+        /// <param name="center">The offset of the center of the circle.</param>
+        /// <param name="radius">The radius of the circle.</param>
+        /// <param name="segments">The number of segments used in the circle.</param>
+        /// <returns>A new VectorPolygon object in the shape of a circle.</returns>
+        public static VectorPolygon CreateCircle(Vector2 center, float radius, 
+            int segments)
+        {
+            Vector2[] points = new Vector2[segments];
+            float angle = MathHelper.TwoPi / points.Length;
+
+            for (int i = 0; i <= points.Length - 1; i++)
+            {
+                points[i] = new Vector2(
+                    center.X + radius * (float)Math.Round(Math.Sin(angle * i), 4), 
+                    center.Y + radius * (float)Math.Round(Math.Cos(angle * i), 4));
+            }
+
+            return new VectorPolygon(points);
+        }
+
+
+        /// <summary>
+        /// Create a polygon shaped like a player.
+        /// </summary>
+        /// <returns>A new VectorPolygon object in the shape of a player.</returns>
+        public static VectorPolygon CreatePlayer()
+        {
+            Vector2[] points = new Vector2[10];
+
+            points[0] = new Vector2(-15, -2);
+            points[1] = new Vector2(-11, -6);
+            points[2] = new Vector2(-7, -2);
+            points[3] = new Vector2(0, -9);
+            points[4] = new Vector2(7, -2);
+            points[5] = new Vector2(11, -6);
+            points[6] = new Vector2(15, -2);
+            points[7] = new Vector2(4, 9);
+            points[8] = new Vector2(0, 5);
+            points[9] = new Vector2(-4, 9);
+
+            return new VectorPolygon(points);
+        }
+        
+        
+        /// <summary>
+        /// Create a polygon shaped like a rocket.
+        /// </summary>
+        /// <returns>A new VectorPolygon object in the shape of a rocket.</returns>
+        public static VectorPolygon CreateRocket()
+        {
+            Vector2[] points = new Vector2[4];
+            points[0] = new Vector2(0, -6);
+            points[1] = new Vector2(6, 0);
+            points[2] = new Vector2(0, 10);
+            points[3] = new Vector2(-6, 0);
+
+            return new VectorPolygon(points);
+        }
+
+
+        /// <summary>
+        /// Create a polygon shaped like a mine.
+        /// </summary>
+        /// <returns>A new VectorPolygon object in the shape of a mine.</returns>
+        public static VectorPolygon CreateMine()
+        {
+            Vector2[] points = new Vector2[12];
+
+            points[0] = new Vector2(0, -32);
+            points[1] = new Vector2(16, -16);
+            points[2] = new Vector2(32, 0);
+            points[3] = new Vector2(16, 16);
+            points[4] = new Vector2(0, 32);
+            points[5] = new Vector2(-16, 16);
+            points[6] = new Vector2(-32, 0);
+            points[7] = new Vector2(-16, -16);
+            points[8] = new Vector2(16, -16);
+            points[9] = new Vector2(16, 16);
+            points[10] = new Vector2(-16, 16);
+            points[11] = new Vector2(-16, -16);
+
+            Matrix scale = Matrix.CreateScale(0.33f);
+
+            Vector2.Transform(points, ref scale, points);
+
+            return new VectorPolygon(points);
+        }
+
+
+        /// <summary>
+        /// Create a polygon shaped like an asteroid.
+        /// </summary>
+        /// <param name="radius">The radius of the asteroid.</param>
+        /// <returns>A new VectorPolygon object in the shape of an asteroid.</returns>
+        public static VectorPolygon CreateAsteroid(float radius)
+        {
+            VectorPolygon polygon = CreateCircle(Vector2.Zero, radius, 12);
+            for (int i = 0; i < polygon.Points.Length; ++i)
+            {
+                Vector2 normal = Vector2.Normalize(polygon.Points[i]);
+                polygon.Points[i] += normal * ((radius * 0.2f) * 
+                    (float)random.NextDouble() - (radius * 0.1f));
+            }
+
+            return polygon;
+        }
+        #endregion
+    }
+}

+ 310 - 0
StarterKits/MacOS/VectorRumble/ScreenManager/GameScreen.cs

@@ -0,0 +1,310 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// GameScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Enum describes the screen transition state.
+    /// </summary>
+    public enum ScreenState
+    {
+        TransitionOn,
+        Active,
+        TransitionOff,
+        Hidden,
+    }
+
+
+    /// <summary>
+    /// A screen is a single layer that has update and draw logic, and which
+    /// can be combined with other layers to build up a complex menu system.
+    /// For instance the main menu, the options menu, the "are you sure you
+    /// want to quit" message box, and the main game itself are all implemented
+    /// as screens.
+    /// </summary>
+    /// <remarks>Based on a class in the Game State Management sample.</remarks>
+    public abstract class GameScreen
+    {
+        #region Properties
+
+
+        /// <summary>
+        /// Normally when one screen is brought up over the top of another,
+        /// the first screen will transition off to make room for the new
+        /// one. This property indicates whether the screen is only a small
+        /// popup, in which case screens underneath it do not need to bother
+        /// transitioning off.
+        /// </summary>
+        public bool IsPopup
+        {
+            get { return isPopup; }
+            protected set { isPopup = value; }
+        }
+
+        bool isPopup = false;
+
+
+        /// <summary>
+        /// Indicates how long the screen takes to
+        /// transition on when it is activated.
+        /// </summary>
+        public TimeSpan TransitionOnTime
+        {
+            get { return transitionOnTime; }
+            protected set { transitionOnTime = value; }
+        }
+
+        TimeSpan transitionOnTime = TimeSpan.Zero;
+
+
+        /// <summary>
+        /// Indicates how long the screen takes to
+        /// transition off when it is deactivated.
+        /// </summary>
+        public TimeSpan TransitionOffTime
+        {
+            get { return transitionOffTime; }
+            protected set { transitionOffTime = value; }
+        }
+
+        TimeSpan transitionOffTime = TimeSpan.Zero;
+
+
+        /// <summary>
+        /// Gets the current position of the screen transition, ranging
+        /// from zero (fully active, no transition) to one (transitioned
+        /// fully off to nothing).
+        /// </summary>
+        public float TransitionPosition
+        {
+            get { return transitionPosition; }
+            protected set { transitionPosition = value; }
+        }
+
+        float transitionPosition = 1;
+
+
+        /// <summary>
+        /// Gets the current alpha of the screen transition, ranging
+        /// from 255 (fully active, no transition) to 0 (transitioned
+        /// fully off to nothing).
+        /// </summary>
+        public byte TransitionAlpha
+        {
+            get { return (byte)(255 - TransitionPosition * 255); }
+        }
+
+
+        /// <summary>
+        /// Gets the current screen transition state.
+        /// </summary>
+        public ScreenState ScreenState
+        {
+            get { return screenState; }
+            protected set { screenState = value; }
+        }
+
+        ScreenState screenState = ScreenState.TransitionOn;
+
+
+        /// <summary>
+        /// There are two possible reasons why a screen might be transitioning
+        /// off. It could be temporarily going away to make room for another
+        /// screen that is on top of it, or it could be going away for good.
+        /// This property indicates whether the screen is exiting for real:
+        /// if set, the screen will automatically remove itself as soon as the
+        /// transition finishes.
+        /// </summary>
+        public bool IsExiting
+        {
+            get { return isExiting; }
+            protected internal set { isExiting = value; }
+        }
+
+        bool isExiting = false;
+
+
+        /// <summary>
+        /// Checks whether this screen is active and can respond to user input.
+        /// </summary>
+        public bool IsActive
+        {
+            get
+            {
+                return !otherScreenHasFocus &&
+                       (screenState == ScreenState.TransitionOn ||
+                        screenState == ScreenState.Active);
+            }
+        }
+
+        bool otherScreenHasFocus;
+
+
+        /// <summary>
+        /// Gets the manager that this screen belongs to.
+        /// </summary>
+        public ScreenManager ScreenManager
+        {
+            get { return screenManager; }
+            internal set { screenManager = value; }
+        }
+
+        ScreenManager screenManager;
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Load graphics content for the screen.
+        /// </summary>
+        public virtual void LoadContent() { }
+
+
+        /// <summary>
+        /// Unload content for the screen.
+        /// </summary>
+        public virtual void UnloadContent() { }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Allows the screen to run logic, such as updating the transition position.
+        /// Unlike HandleInput, this method is called regardless of whether the screen
+        /// is active, hidden, or in the middle of a transition.
+        /// </summary>
+        public virtual void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                      bool coveredByOtherScreen)
+        {
+            this.otherScreenHasFocus = otherScreenHasFocus;
+
+            if (isExiting)
+            {
+                // If the screen is going away to die, it should transition off.
+                screenState = ScreenState.TransitionOff;
+
+                if (!UpdateTransition(gameTime, transitionOffTime, 1))
+                {
+                    // When the transition finishes, remove the screen.
+                    ScreenManager.RemoveScreen(this);
+                }
+            }
+            else if (coveredByOtherScreen)
+            {
+                // If the screen is covered by another, it should transition off.
+                if (UpdateTransition(gameTime, transitionOffTime, 1))
+                {
+                    // Still busy transitioning.
+                    screenState = ScreenState.TransitionOff;
+                }
+                else
+                {
+                    // Transition finished!
+                    screenState = ScreenState.Hidden;
+                }
+            }
+            else
+            {
+                // Otherwise the screen should transition on and become active.
+                if (UpdateTransition(gameTime, transitionOnTime, -1))
+                {
+                    // Still busy transitioning.
+                    screenState = ScreenState.TransitionOn;
+                }
+                else
+                {
+                    // Transition finished!
+                    screenState = ScreenState.Active;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Helper for updating the screen transition position.
+        /// </summary>
+        bool UpdateTransition(GameTime gameTime, TimeSpan time, int direction)
+        {
+            // How much should we move by?
+            float transitionDelta;
+
+            if (time == TimeSpan.Zero)
+                transitionDelta = 1;
+            else
+                transitionDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds /
+                                          time.TotalMilliseconds);
+
+            // Update the transition position.
+            transitionPosition += transitionDelta * direction;
+
+            // Did we reach the end of the transition?
+            if ((transitionPosition <= 0) || (transitionPosition >= 1))
+            {
+                transitionPosition = MathHelper.Clamp(transitionPosition, 0, 1);
+                return false;
+            }
+
+            // Otherwise we are still busy transitioning.
+            return true;
+        }
+
+
+        /// <summary>
+        /// Allows the screen to handle user input. Unlike Update, this method
+        /// is only called when the screen is active, and not when some other
+        /// screen has taken the focus.
+        /// </summary>
+        public virtual void HandleInput(InputState input) { }
+
+
+        /// <summary>
+        /// This is called when the screen should draw itself.
+        /// </summary>
+        public virtual void Draw(GameTime gameTime) { }
+
+
+        #endregion
+
+        #region Public Methods
+
+
+        /// <summary>
+        /// Tells the screen to go away. Unlike ScreenManager.RemoveScreen, which
+        /// instantly kills the screen, this method respects the transition timings
+        /// and will give the screen a chance to gradually transition off.
+        /// </summary>
+        public void ExitScreen()
+        {
+            if (TransitionOffTime == TimeSpan.Zero)
+            {
+                // If the screen has a zero transition time, remove it immediately.
+                ScreenManager.RemoveScreen(this);
+            }
+            else
+            {
+                // Otherwise flag that it should transition off and then exit.
+                isExiting = true;
+            }
+        }
+
+
+        #endregion
+    }
+}

+ 235 - 0
StarterKits/MacOS/VectorRumble/ScreenManager/InputState.cs

@@ -0,0 +1,235 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// InputState.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Helper for reading input from keyboard and gamepad. This class tracks both
+    /// the current and previous state of both input devices, and implements query
+    /// properties for high level input actions such as "move up through the menu"
+    /// or "pause the game".
+    /// </summary>
+    /// <remarks>Based on a class in the Game State Management sample.</remarks>
+    public class InputState
+    {
+        #region Fields
+
+        public const int MaxInputs = 4;
+
+        public readonly KeyboardState[] CurrentKeyboardStates;
+        public readonly GamePadState[] CurrentGamePadStates;
+
+        public readonly KeyboardState[] LastKeyboardStates;
+        public readonly GamePadState[] LastGamePadStates;
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs a new input state.
+        /// </summary>
+        public InputState()
+        {
+            CurrentKeyboardStates = new KeyboardState[MaxInputs];
+            CurrentGamePadStates = new GamePadState[MaxInputs];
+
+            LastKeyboardStates = new KeyboardState[MaxInputs];
+            LastGamePadStates = new GamePadState[MaxInputs];
+        }
+
+
+        #endregion
+
+        #region Properties
+
+
+        /// <summary>
+        /// Checks for a "menu up" input action, from any player,
+        /// on either keyboard or gamepad.
+        /// </summary>
+        public bool MenuUp
+        {
+            get
+            {
+                return IsNewKeyPress(Keys.Up) ||
+                       IsNewButtonPress(Buttons.DPadUp) ||
+                       IsNewButtonPress(Buttons.LeftThumbstickUp);
+            }
+        }
+
+
+        /// <summary>
+        /// Checks for a "menu down" input action, from any player,
+        /// on either keyboard or gamepad.
+        /// </summary>
+        public bool MenuDown
+        {
+            get
+            {
+                return IsNewKeyPress(Keys.Down) ||
+                       IsNewButtonPress(Buttons.DPadDown) ||
+                       IsNewButtonPress(Buttons.LeftThumbstickDown);
+            }
+        }
+
+
+        /// <summary>
+        /// Checks for a "menu select" input action, from any player,
+        /// on either keyboard or gamepad.
+        /// </summary>
+        public bool MenuSelect
+        {
+            get
+            {
+                return IsNewKeyPress(Keys.Space) ||
+                       IsNewKeyPress(Keys.Enter) ||
+                       IsNewButtonPress(Buttons.A) ||
+                       IsNewButtonPress(Buttons.Start);
+            }
+        }
+
+
+        /// <summary>
+        /// Checks for a "menu cancel" input action, from any player,
+        /// on either keyboard or gamepad.
+        /// </summary>
+        public bool MenuCancel
+        {
+            get
+            {
+                return IsNewKeyPress(Keys.Escape) ||
+                       IsNewButtonPress(Buttons.B) ||
+                       IsNewButtonPress(Buttons.Back);
+            }
+        }
+
+
+        /// <summary>
+        /// Checks for a "pause the game" input action, from any player,
+        /// on either keyboard or gamepad.
+        /// </summary>
+        public bool PauseGame
+        {
+            get
+            {
+                return IsNewKeyPress(Keys.Escape) ||
+                       IsNewButtonPress(Buttons.Back) ||
+                       IsNewButtonPress(Buttons.Start);
+            }
+        }
+
+
+        #endregion
+
+        #region Methods
+
+
+        /// <summary>
+        /// Reads the latest state of the keyboard and gamepad.
+        /// </summary>
+        public void Update()
+        {
+            for (int i = 0; i < MaxInputs; i++)
+            {
+                LastKeyboardStates[i] = CurrentKeyboardStates[i];
+                LastGamePadStates[i] = CurrentGamePadStates[i];
+
+                CurrentKeyboardStates[i] = Keyboard.GetState((PlayerIndex)i);
+                CurrentGamePadStates[i] = GamePad.GetState((PlayerIndex)i);
+            }
+        }
+
+
+        /// <summary>
+        /// Helper for checking if a key was newly pressed during this update,
+        /// by any player.
+        /// </summary>
+        public bool IsNewKeyPress(Keys key)
+        {
+            for (int i = 0; i < MaxInputs; i++)
+            {
+                if (IsNewKeyPress(key, (PlayerIndex)i))
+                    return true;
+            }
+
+            return false;
+        }
+
+
+        /// <summary>
+        /// Helper for checking if a key was newly pressed during this update,
+        /// by the specified player.
+        /// </summary>
+        public bool IsNewKeyPress(Keys key, PlayerIndex playerIndex)
+        {
+            return (CurrentKeyboardStates[(int)playerIndex].IsKeyDown(key) &&
+                    LastKeyboardStates[(int)playerIndex].IsKeyUp(key));
+        }
+
+
+        /// <summary>
+        /// Helper for checking if a button was newly pressed during this update,
+        /// by any player.
+        /// </summary>
+        public bool IsNewButtonPress(Buttons button)
+        {
+            for (int i = 0; i < MaxInputs; i++)
+            {
+                if (IsNewButtonPress(button, (PlayerIndex)i))
+                    return true;
+            }
+
+            return false;
+        }
+
+
+        /// <summary>
+        /// Helper for checking if a button was newly pressed during this update,
+        /// by the specified player.
+        /// </summary>
+        public bool IsNewButtonPress(Buttons button, PlayerIndex playerIndex)
+        {
+            return (CurrentGamePadStates[(int)playerIndex].IsButtonDown(button) &&
+                    LastGamePadStates[(int)playerIndex].IsButtonUp(button));
+        }
+
+
+        /// <summary>
+        /// Checks for a "menu select" input action from the specified player.
+        /// </summary>
+        public bool IsMenuSelect(PlayerIndex playerIndex)
+        {
+            return IsNewKeyPress(Keys.Space, playerIndex) ||
+                   IsNewKeyPress(Keys.Enter, playerIndex) ||
+                   IsNewButtonPress(Buttons.A, playerIndex) ||
+                   IsNewButtonPress(Buttons.Start, playerIndex);
+        }
+
+
+        /// <summary>
+        /// Checks for a "menu cancel" input action from the specified player.
+        /// </summary>
+        public bool IsMenuCancel(PlayerIndex playerIndex)
+        {
+            return IsNewKeyPress(Keys.Escape, playerIndex) ||
+                   IsNewButtonPress(Buttons.B, playerIndex) ||
+                   IsNewButtonPress(Buttons.Back, playerIndex);
+        }
+
+
+        #endregion
+    }
+}

+ 314 - 0
StarterKits/MacOS/VectorRumble/ScreenManager/ScreenManager.cs

@@ -0,0 +1,314 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ScreenManager.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// The screen manager is a component which manages one or more GameScreen
+    /// instances. It maintains a stack of screens, calls their Update and Draw
+    /// methods at the appropriate times, and automatically routes input to the
+    /// topmost active screen.
+    /// </summary>
+    /// <remarks>Based on a class in the Game State Management sample.</remarks>
+    public class ScreenManager : DrawableGameComponent
+    {
+        #region Fields
+
+        List<GameScreen> screens = new List<GameScreen>();
+        List<GameScreen> screensToUpdate = new List<GameScreen>();
+
+        InputState input = new InputState();
+
+        SpriteBatch spriteBatch;
+        SpriteFont font;
+        Texture2D blankTexture;
+
+        bool isInitialized;
+
+        bool traceEnabled;
+
+        #endregion
+
+        #region Properties
+
+
+        /// <summary>
+        /// A default SpriteBatch shared by all the screens. This saves
+        /// each screen having to bother creating their own local instance.
+        /// </summary>
+        public SpriteBatch SpriteBatch
+        {
+            get { return spriteBatch; }
+        }
+
+
+        /// <summary>
+        /// A default font shared by all the screens. This saves
+        /// each screen having to bother loading their own local copy.
+        /// </summary>
+        public SpriteFont Font
+        {
+            get { return font; }
+        }
+
+
+        /// <summary>
+        /// If true, the manager prints out a list of all the screens
+        /// each time it is updated. This can be useful for making sure
+        /// everything is being added and removed at the right times.
+        /// </summary>
+        public bool TraceEnabled
+        {
+            get { return traceEnabled; }
+            set { traceEnabled = value; }
+        }
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs a new screen manager component.
+        /// </summary>
+        public ScreenManager(Game game)
+            : base(game)
+        {
+        }
+
+
+        /// <summary>
+        /// Initializes the screen manager component.
+        /// </summary>
+        public override void Initialize()
+        {
+            base.Initialize();
+
+            isInitialized = true;
+        }
+
+
+        /// <summary>
+        /// Load your graphics content.
+        /// </summary>
+        protected override void LoadContent()
+        {
+            // Load content belonging to the screen manager.
+            ContentManager content = Game.Content;
+
+            spriteBatch = new SpriteBatch(GraphicsDevice);
+            font = content.Load<SpriteFont>("Fonts/retroMedium");
+            blankTexture = content.Load<Texture2D>("Textures/blank");
+
+            // Tell each of the screens to load their content.
+            foreach (GameScreen screen in screens)
+            {
+                screen.LoadContent();
+            }
+        }
+
+
+        /// <summary>
+        /// Unload your graphics content.
+        /// </summary>
+        protected override void UnloadContent()
+        {
+            // Tell each of the screens to unload their content.
+            foreach (GameScreen screen in screens)
+            {
+                screen.UnloadContent();
+            }
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Allows each screen to run logic.
+        /// </summary>
+        public override void Update(GameTime gameTime)
+        {
+            // Read the keyboard and gamepad.
+            input.Update();
+
+            // Make a copy of the master screen list, to avoid confusion if
+            // the process of updating one screen adds or removes others.
+            screensToUpdate.Clear();
+
+            foreach (GameScreen screen in screens)
+                screensToUpdate.Add(screen);
+
+            bool otherScreenHasFocus = !Game.IsActive;
+            bool coveredByOtherScreen = false;
+
+            // Loop as long as there are screens waiting to be updated.
+            while (screensToUpdate.Count > 0)
+            {
+                // Pop the topmost screen off the waiting list.
+                GameScreen screen = screensToUpdate[screensToUpdate.Count - 1];
+
+                screensToUpdate.RemoveAt(screensToUpdate.Count - 1);
+
+                // Update the screen.
+                screen.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+                if (screen.ScreenState == ScreenState.TransitionOn ||
+                    screen.ScreenState == ScreenState.Active)
+                {
+                    // If this is the first active screen we came across,
+                    // give it a chance to handle input.
+                    if (!otherScreenHasFocus)
+                    {
+                        screen.HandleInput(input);
+
+                        otherScreenHasFocus = true;
+                    }
+
+                    // If this is an active non-popup, inform any subsequent
+                    // screens that they are covered by it.
+                    if (!screen.IsPopup)
+                        coveredByOtherScreen = true;
+                }
+            }
+
+            // Print debug trace?
+            if (traceEnabled)
+                TraceScreens();
+        }
+
+
+        /// <summary>
+        /// Prints a list of all the screens, for debugging.
+        /// </summary>
+        void TraceScreens()
+        {
+            List<string> screenNames = new List<string>();
+
+            foreach (GameScreen screen in screens)
+                screenNames.Add(screen.GetType().Name);
+
+            // TODO Trace.WriteLine(string.Join(", ", screenNames.ToArray()));
+        }
+
+
+        /// <summary>
+        /// Tells each screen to draw itself.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            foreach (GameScreen screen in screens)
+            {
+                if (screen.ScreenState == ScreenState.Hidden)
+                    continue;
+
+                screen.Draw(gameTime);
+            }
+        }
+
+
+        /// <summary>
+        /// Draw an empty rectangle of the given size and color.
+        /// </summary>
+        /// <param name="rectangle">The destination rectangle.</param>
+        /// <param name="color">The color of the rectangle.</param>
+        public void DrawRectangle(Rectangle rectangle, Color color)
+        {
+            SpriteBatch.Begin();
+            SpriteBatch.Draw(blankTexture, rectangle, color);
+            SpriteBatch.End();
+        }
+
+
+        #endregion
+
+        #region Public Methods
+
+
+        /// <summary>
+        /// Adds a new screen to the screen manager.
+        /// </summary>
+        public void AddScreen(GameScreen screen)
+        {
+            screen.ScreenManager = this;
+            screen.IsExiting = false;
+
+            // If we have a graphics device, tell the screen to load content.
+            if (isInitialized)
+            {
+                screen.LoadContent();
+            }
+
+            screens.Add(screen);
+        }
+
+
+        /// <summary>
+        /// Removes a screen from the screen manager. You should normally
+        /// use GameScreen.ExitScreen instead of calling this directly, so
+        /// the screen can gradually transition off rather than just being
+        /// instantly removed.
+        /// </summary>
+        public void RemoveScreen(GameScreen screen)
+        {
+            // If we have a graphics device, tell the screen to unload content.
+            if (isInitialized)
+            {
+                screen.UnloadContent();
+            }
+
+            screens.Remove(screen);
+            screensToUpdate.Remove(screen);
+        }
+
+
+        /// <summary>
+        /// Expose an array holding all the screens. We return a copy rather
+        /// than the real master list, because screens should only ever be added
+        /// or removed using the AddScreen and RemoveScreen methods.
+        /// </summary>
+        public GameScreen[] GetScreens()
+        {
+            return screens.ToArray();
+        }
+
+
+        /// <summary>
+        /// Helper draws a translucent black fullscreen sprite, used for fading
+        /// screens in and out, and for darkening the background behind popups.
+        /// </summary>
+        public void FadeBackBufferToBlack(int alpha)
+        {
+            Viewport viewport = GraphicsDevice.Viewport;
+
+            spriteBatch.Begin();
+
+            spriteBatch.Draw(blankTexture,
+                             new Rectangle(0, 0, viewport.Width, viewport.Height),
+                             new Color(0, 0, 0, (byte)alpha));
+
+            spriteBatch.End();
+        }
+
+
+        #endregion
+    }
+}

+ 163 - 0
StarterKits/MacOS/VectorRumble/Screens/BackgroundScreen.cs

@@ -0,0 +1,163 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// BackgroundScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// The background screen sits behind all the other menu screens.
+    /// It draws a background image that remains fixed in place regardless
+    /// of whatever transitions the screens on top of it may be doing.
+    /// </summary>
+    /// <remarks>
+    /// This class is somewhat similar to one of the same name in the 
+    /// GameStateManagement sample.
+    /// </remarks>
+    class BackgroundScreen : GameScreen
+    {
+        #region Fields
+        Random random;
+        CollectCollection<ParticleSystem> particleSystems;
+        float addTimer;
+        Color[] explosionColors = new Color[] 
+            {
+                Color.Orange, Color.Red, Color.Gray, Color.Silver, Color.Yellow
+            };
+        LineBatch lineBatch;
+        Texture2D titleTexture;
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public BackgroundScreen()
+        {
+            TransitionOnTime = TimeSpan.FromSeconds(1.0);
+            TransitionOffTime = TimeSpan.FromSeconds(1.0);
+
+            random = new Random();
+            particleSystems = new CollectCollection<ParticleSystem>(null);
+        }
+
+
+        /// <summary>
+        /// Loads graphics content for this screen. The background texture is quite
+        /// big, so we use our own local ContentManager to load it. This allows us
+        /// to unload before going from the menus into the game itself, wheras if we
+        /// used the shared ContentManager provided by the ScreenManager, the content
+        /// would remain loaded forever.
+        /// </summary>
+        public override void LoadContent()
+        {
+            lineBatch = new LineBatch(ScreenManager.GraphicsDevice);
+            titleTexture = ScreenManager.Game.Content.Load<Texture2D>("Textures/title");
+
+            int viewportWidth = ScreenManager.GraphicsDevice.Viewport.Width;
+            int viewportHeight = ScreenManager.GraphicsDevice.Viewport.Height;
+
+            // update the projection in the line-batch
+            lineBatch.SetProjection(Matrix.CreateOrthographicOffCenter(0.0f,
+                viewportWidth, viewportHeight, 0.0f, 0.0f, 1.0f));
+
+            // recreate the particle systems
+            particleSystems.Clear();
+            for (int i = 0; i < 4; i++)
+            {
+                particleSystems.Add(new ParticleSystem(
+                   new Vector2(random.Next(viewportWidth), random.Next(viewportHeight)),
+                   Vector2.Zero, 64, 256, 128,
+                   1f + 2f * (float)random.NextDouble(), 0.05f, explosionColors));
+            }
+
+            base.LoadContent();
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Updates the background screen. Unlike most screens, this should not
+        /// transition off even if it has been covered by another screen: it is
+        /// supposed to be covered, after all! This overload forces the
+        /// coveredByOtherScreen parameter to false in order to stop the base
+        /// Update method wanting to transition off.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            addTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
+            if (addTimer > 0.25f)
+            {
+                addTimer -= 0.15f;
+                particleSystems.Add(new ParticleSystem(
+                   new Vector2(random.Next(ScreenManager.GraphicsDevice.Viewport.Width),
+                       random.Next(ScreenManager.GraphicsDevice.Viewport.Height)),
+                   Vector2.Zero, 64, 256, 128,
+                   1f + 2f * (float)random.NextDouble(), 0.05f, explosionColors));
+            }
+
+            for (int i = 0; i < particleSystems.Count; i++)
+            {
+                particleSystems[i].Update((float)gameTime.ElapsedGameTime.TotalSeconds);
+                if (particleSystems[i].IsActive == false)
+                {
+                    particleSystems.Garbage.Add(particleSystems[i]);
+                }
+            }
+            particleSystems.Collect();
+
+            base.Update(gameTime, otherScreenHasFocus, false);
+        }
+
+
+        /// <summary>
+        /// Draws the background screen.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+            Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
+
+            lineBatch.Begin();
+            for (int i = 0; i < particleSystems.Count; i++)
+            {
+                particleSystems[i].Draw(lineBatch);
+            }
+            lineBatch.End();
+
+            // title
+            Vector2 titlePosition = new Vector2(
+                (viewportSize.X - titleTexture.Width) / 2f,
+                viewportSize.Y * 0.18f);
+            titlePosition.Y -= (float)Math.Pow(TransitionPosition, 2) * titlePosition.Y;
+            Color titleColor = Color.White;
+
+            ScreenManager.SpriteBatch.Begin();
+            ScreenManager.SpriteBatch.Draw(titleTexture, titlePosition,
+                new Color(titleColor.R, titleColor.G, titleColor.B, TransitionAlpha));
+            ScreenManager.SpriteBatch.End();
+        }
+
+
+        #endregion
+    }
+}

+ 172 - 0
StarterKits/MacOS/VectorRumble/Screens/GameOverScreen.cs

@@ -0,0 +1,172 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// GameOverScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// The GameOverScreen screen object is displayed when the game is over.
+    /// </summary>
+    class GameOverScreen : MenuScreen
+    {
+        #region Fields
+
+
+        Texture2D titleTexture;
+        string message;
+
+
+        #endregion
+
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor fills in the menu contents.
+        /// </summary>
+        public GameOverScreen(string message)
+        {
+            this.message = message;
+
+            MenuEntry playAgainMenuEntry = new MenuEntry("Play Again");
+            MenuEntry returnToMenuMenuEntry = new MenuEntry("Return to Menu");
+
+            playAgainMenuEntry.Selected += PlayAgainMenuEntrySelected;
+            returnToMenuMenuEntry.Selected += ReturnToMenuMenuEntrySelected;
+
+            MenuEntries.Add(playAgainMenuEntry);
+            MenuEntries.Add(returnToMenuMenuEntry);
+
+            TransitionOnTime = TimeSpan.FromSeconds(1.0);
+            TransitionOffTime = TimeSpan.FromSeconds(1.0);
+            IsPopup = true;
+        }
+
+
+        /// <summary>
+        /// Loads graphics content for this screen.
+        /// </summary>
+        public override void LoadContent()
+        {
+            titleTexture = ScreenManager.Game.Content.Load<Texture2D>("Textures/title");
+            base.LoadContent();
+        }
+
+
+        #endregion
+
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Event handler for when the Play Again menu entry is selected.
+        /// </summary>
+        void PlayAgainMenuEntrySelected(object sender, EventArgs e)
+        {
+            LoadingScreen.Load(ScreenManager, LoadGameplayScreen, true);
+        }
+
+
+        /// <summary>
+        /// Loading screen callback for activating the gameplay screen.
+        /// </summary>
+        void LoadGameplayScreen(object sender, EventArgs e)
+        {
+            GameplayScreen gameplayScreen = new GameplayScreen();
+            gameplayScreen.ScreenManager = this.ScreenManager;
+            gameplayScreen.Initialize();
+            ScreenManager.AddScreen(gameplayScreen);
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Return to Menu menu entry is selected.
+        /// </summary>
+        void ReturnToMenuMenuEntrySelected(object sender, EventArgs e)
+        {
+            LoadingScreen.Load(ScreenManager, LoadMainMenuScreen, false);
+        }
+
+
+        /// <summary>
+        /// Event handler for when the user selects ok on the "are you sure
+        /// you want to exit" message box.
+        /// </summary>
+        void LoadMainMenuScreen(object sender, EventArgs e)
+        {
+            ScreenManager.AddScreen(new BackgroundScreen());
+            ScreenManager.AddScreen(new MainMenuScreen());
+        }
+
+
+        protected override void OnCancel() 
+        {
+            LoadingScreen.Load(ScreenManager, LoadMainMenuScreen, false);
+        }
+
+
+        #endregion
+
+
+        #region Drawing
+
+
+        /// <summary>
+        /// Draws the game-over screen.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            // Darken down any other screens that were drawn beneath the popup.
+            ScreenManager.FadeBackBufferToBlack(TransitionAlpha * 2 / 3);
+
+            Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+            Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
+
+            // calculate the texture position
+            Vector2 titlePosition = new Vector2(
+                (viewportSize.X - titleTexture.Width) / 2f, 
+                viewportSize.Y * 0.18f);
+            titlePosition.Y -= (float)Math.Pow(TransitionPosition, 2) * titlePosition.Y;
+            Color titleColor = Color.White;
+
+            // calculate the message position and size
+            float textScale = 1.5f + 0.05f * 
+                (float)Math.Cos(gameTime.TotalRealTime.TotalSeconds * 8.0f);
+            Vector2 textSize = ScreenManager.Font.MeasureString(message);
+            Vector2 textPosition = new Vector2(
+                viewportSize.X / 2f - textSize.X / 2f * textScale,
+                viewportSize.Y * 0.417f);
+            titlePosition.Y -= (float)Math.Pow(TransitionPosition, 2) * titlePosition.Y;
+            textSize.X = 0f;
+            textSize.Y /= 2f;
+
+            // draw the texture and text
+            ScreenManager.SpriteBatch.Begin();
+            ScreenManager.SpriteBatch.Draw(titleTexture, titlePosition, 
+                new Color(titleColor.R, titleColor.G, titleColor.B, TransitionAlpha));
+            ScreenManager.SpriteBatch.DrawString(ScreenManager.Font, message,
+                textPosition, new Color(0, 255, 0, TransitionAlpha), 0.0f, textSize,
+                textScale, SpriteEffects.None, 1.0f);
+            ScreenManager.SpriteBatch.End();
+
+            base.Draw(gameTime);
+        }
+
+
+        #endregion
+    }
+}

+ 287 - 0
StarterKits/MacOS/VectorRumble/Screens/GameplayScreen.cs

@@ -0,0 +1,287 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// GameplayScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Threading;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// This screen implements the actual game logic. It is just a
+    /// placeholder to get the idea across: you'll probably want to
+    /// put some more interesting gameplay in here!
+    /// </summary>
+    /// <remarks>
+    /// This class is somewhat similar to one of the same name in the 
+    /// GameStateManagement sample.
+    /// </remarks>
+    class GameplayScreen : GameScreen
+    {
+        #region Fields
+        BloomComponent bloomComponent;
+        ContentManager content;
+        LineBatch lineBatch;
+        SpriteBatch spriteBatch;
+        SpriteFont spriteFont;
+        Texture2D starTexture;
+        World world;
+        AudioManager audio;
+        bool gameOver;
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public GameplayScreen()
+        {
+            TransitionOnTime = TimeSpan.FromSeconds(1.0);
+            TransitionOffTime = TimeSpan.FromSeconds(1.0);
+        }
+
+
+        /// <summary>
+        /// Initialize the game, after the ScreenManager is set, but not every time
+        /// the graphics are reloaded.
+        /// </summary>
+        public void Initialize()
+        {
+            // create and add the bloom effect
+            bloomComponent = new BloomComponent(ScreenManager.Game);
+            ScreenManager.Game.Components.Add(bloomComponent);
+            // do not automatically draw the bloom component
+            bloomComponent.Visible = false;
+
+            // create the world
+            world = new World(new Vector2(ScreenManager.GraphicsDevice.Viewport.Width,
+                ScreenManager.GraphicsDevice.Viewport.Height));
+
+            // retrieve the audio manager
+            audio = (AudioManager)ScreenManager.Game.Services.GetService(
+                typeof(AudioManager));
+            world.AudioManager = audio;
+            // start up the music
+#if AUDIO
+            audio.PlayMusic("gameMusic");
+#endif
+            // start up the game
+            world.StartNewGame();
+            gameOver = false;
+        }
+
+
+        /// <summary>
+        /// Load graphics content for the game.
+        /// </summary>
+        public override void LoadContent()
+        {
+            if (content == null)
+            {
+                content = new ContentManager(ScreenManager.Game.Services, "Content");
+            }
+            spriteBatch = new SpriteBatch(ScreenManager.GraphicsDevice);
+            lineBatch = new LineBatch(ScreenManager.GraphicsDevice);
+            spriteFont = content.Load<SpriteFont>("Fonts/retroSmall");
+            starTexture = content.Load<Texture2D>("Textures/blank");
+
+            // update the projection in the line-batch
+            lineBatch.SetProjection(Matrix.CreateOrthographicOffCenter(0.0f,
+                ScreenManager.GraphicsDevice.Viewport.Width, 
+                ScreenManager.GraphicsDevice.Viewport.Height, 0.0f, 0.0f, 1.0f));
+        }
+
+
+        /// <summary>
+        /// Unload graphics content used by the game.
+        /// </summary>
+        public override void UnloadContent()
+        {
+            if (spriteBatch != null)
+            {
+                spriteBatch.Dispose();
+                spriteBatch = null;
+            }
+            if (lineBatch != null)
+            {
+                lineBatch.Dispose();
+                lineBatch = null;
+            }
+            content.Unload();
+        }
+        #endregion
+
+        #region Update and Draw
+        /// <summary>
+        /// Updates the state of the game. This method checks the GameScreen.IsActive
+        /// property, so the game will stop updating when the pause menu is active,
+        /// or if you tab away to a different application.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+            // if this screen is leaving, then stop the music
+            if (IsExiting)
+            {
+                audio.StopMusic();
+            }
+            else if ((otherScreenHasFocus == true) || (coveredByOtherScreen == true))
+            {
+                // make sure nobody's controller is vibrating
+                for (int i = 0; i < 4; i++)
+                {
+                    GamePad.SetVibration((PlayerIndex)i, 0f, 0f);
+                }
+                if (gameOver == false)
+                {
+                    for (int i = 0; i < world.Ships.Length; i++)
+                    {
+                        world.Ships[i].ProcessInput(gameTime.TotalGameTime.Seconds, 
+                            true);
+                    }
+                }
+            }
+            else
+            {
+                // check for a winner
+                if (gameOver == false)
+                {
+                    for (int i = 0; i < world.Ships.Length; i++)
+                    {
+                        if (world.Ships[i].Score >= WorldRules.ScoreLimit)
+                        {
+                            ScreenManager.AddScreen(new GameOverScreen("Player " +
+                                (i + 1).ToString() + " wins the game!"));
+                            gameOver = true;
+                            break;
+                        }
+                    }
+                }
+                // update the world
+                if (gameOver == false)
+                {
+                    world.Update((float)gameTime.ElapsedGameTime.TotalSeconds);
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Lets the game respond to player input. Unlike the Update method,
+        /// this will only be called when the gameplay screen is active.
+        /// </summary>
+        public override void HandleInput(InputState input)
+        {
+            if (input == null)
+                throw new ArgumentNullException("input");
+
+            if (input.PauseGame)
+            {
+                // If they pressed pause, bring up the pause menu screen.
+                ScreenManager.AddScreen(new PauseMenuScreen());
+            }
+        }
+
+
+        /// <summary>
+        /// Draws the gameplay screen.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+            lineBatch.Begin();
+
+            // draw all actors
+            foreach (Actor actor in world.Actors)
+            {
+                if (actor.Dead == false)
+                {
+                    actor.Draw(elapsedTime, lineBatch);
+                }
+            }
+
+            // draw all particle systems
+            foreach (ParticleSystem particleSystem in world.ParticleSystems)
+            {
+                if (particleSystem.IsActive)
+                {
+                    particleSystem.Draw(lineBatch);
+                }
+            }
+
+            // draw the walls
+            world.DrawWalls(lineBatch);
+
+            lineBatch.End();
+
+            // draw the stars
+            spriteBatch.Begin();
+            world.Starfield.Draw(spriteBatch, starTexture);
+            spriteBatch.End();
+
+            if (WorldRules.NeonEffect)
+            {
+                bloomComponent.Draw(gameTime);
+            }
+
+            DrawHud(elapsedTime);
+
+            // If the game is transitioning on or off, fade it out to black.
+            if (TransitionPosition > 0)
+                ScreenManager.FadeBackBufferToBlack(255 - TransitionAlpha);
+        }
+
+
+        /// <summary>
+        /// Draw the user interface elements of the game (scores, etc.).
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        private void DrawHud(float elapsedTime)
+        {
+            spriteBatch.Begin();
+
+            Vector2 position = new Vector2(128, 64);
+            int offset = (1280) / 5;
+
+            for (int i = 0; i < world.Ships.Length; ++i)
+            {
+                string message;
+
+                if (world.Ships[i].Playing)
+                {
+                    message = "Player " + (i + 1).ToString() + ": " +
+                        world.Ships[i].Score.ToString();
+                }
+                else
+                {
+                    message = "Hold A to Join";
+                }
+
+                float scale = 1.0f;
+
+                Vector2 size = spriteFont.MeasureString(message) * scale;
+                position.X = (i + 1) * offset - size.X / 2;
+                spriteBatch.DrawString(spriteFont, message, position, 
+                    world.Ships[i].Color, 0.0f, Vector2.Zero, scale, 
+                    SpriteEffects.None, 1.0f);
+            }
+
+            spriteBatch.End();
+        }
+        #endregion
+    }
+}

+ 150 - 0
StarterKits/MacOS/VectorRumble/Screens/LoadingScreen.cs

@@ -0,0 +1,150 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// LoadingScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// The loading screen coordinates transitions between the menu system and the
+    /// game itself. Normally one screen will transition off at the same time as
+    /// the next screen is transitioning on, but for larger transitions that can
+    /// take a longer time to load their data, we want the menu system to be entirely
+    /// gone before we start loading the game. This is done as follows:
+    /// 
+    /// - Tell all the existing screens to transition off.
+    /// - Activate a loading screen, which will transition on at the same time.
+    /// - The loading screen watches the state of the previous screens.
+    /// - When it sees they have finished transitioning off, it activates the real
+    ///   next screen, which may take a long time to load its data. The loading
+    ///   screen will be the only thing displayed while this load is taking place.
+    /// </summary>
+    /// <remarks>
+    /// This class is similar to one of the same name in the GameStateManagement sample.
+    /// </remarks>
+    class LoadingScreen : GameScreen
+    {
+        #region Fields
+
+        bool loadingIsSlow;
+        bool otherScreensAreGone;
+        EventHandler<EventArgs> loadNextScreen;
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// The constructor is private: loading screens should
+        /// be activated via the static Load method instead.
+        /// </summary>
+        private LoadingScreen()
+        {
+            TransitionOnTime = TimeSpan.FromSeconds(0.5);
+        }
+
+
+        /// <summary>
+        /// Activates the loading screen.
+        /// </summary>
+        public static void Load(ScreenManager screenManager,
+                                EventHandler<EventArgs> loadNextScreen,
+                                bool loadingIsSlow)
+        {
+            // Tell all the current screens to transition off.
+            foreach (GameScreen screen in screenManager.GetScreens())
+                screen.ExitScreen();
+
+            // Create and activate the loading screen.
+            LoadingScreen loadingScreen = new LoadingScreen();
+
+            loadingScreen.loadingIsSlow = loadingIsSlow;
+            loadingScreen.loadNextScreen = loadNextScreen;
+
+            screenManager.AddScreen(loadingScreen);
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Updates the loading screen.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+            // If all the previous screens have finished transitioning
+            // off, it is time to actually perform the load.
+            if (otherScreensAreGone)
+            {
+                ScreenManager.RemoveScreen(this);
+
+                loadNextScreen(this, EventArgs.Empty);
+            }
+        }
+
+
+        /// <summary>
+        /// Draws the loading screen.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            // If we are the only active screen, that means all the previous screens
+            // must have finished transitioning off. We check for this in the Draw
+            // method, rather than in Update, because it isn't enough just for the
+            // screens to be gone: in order for the transition to look good we must
+            // have actually drawn a frame without them before we perform the load.
+            if ((ScreenState == ScreenState.Active) &&
+                (ScreenManager.GetScreens().Length == 1))
+            {
+                otherScreensAreGone = true;
+            }
+
+            // The gameplay screen takes a while to load, so we display a loading
+            // message while that is going on, but the menus load very quickly, and
+            // it would look silly if we flashed this up for just a fraction of a
+            // second while returning from the game to the menus. This parameter
+            // tells us how long the loading is going to take, so we know whether
+            // to bother drawing the message.
+            if (loadingIsSlow)
+            {
+                const string message = "Loading...";
+
+                // Center the text in the viewport.
+                Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+                Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
+                Vector2 textSize = ScreenManager.Font.MeasureString(message);
+                Vector2 textPosition = (viewportSize - textSize) / 2;
+
+                Color color = new Color(255, 255, 255, TransitionAlpha);
+
+                // Draw the text.
+                ScreenManager.SpriteBatch.Begin();
+                
+                ScreenManager.SpriteBatch.DrawString(ScreenManager.Font, message,
+                                                     textPosition, color);
+                
+                ScreenManager.SpriteBatch.End();
+            }
+        }
+
+
+        #endregion
+    }
+}

+ 116 - 0
StarterKits/MacOS/VectorRumble/Screens/MainMenuScreen.cs

@@ -0,0 +1,116 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MainMenuScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// The main menu screen is the first thing displayed when the game starts up.
+    /// </summary>
+    /// <remarks>
+    /// This class is somewhat similar to one of the same name in the 
+    /// GameStateManagement sample.
+    /// </remarks>
+    class MainMenuScreen : MenuScreen
+    {
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor fills in the menu contents.
+        /// </summary>
+        public MainMenuScreen()
+        {
+            MenuEntry playGameMenuEntry = new MenuEntry("Play Game");
+            MenuEntry optionsMenuEntry = new MenuEntry("Options");
+            MenuEntry exitMenuEntry = new MenuEntry("Exit");
+
+            playGameMenuEntry.Selected += PlayGameMenuEntrySelected;
+            optionsMenuEntry.Selected += OptionsMenuEntrySelected;
+            exitMenuEntry.Selected += ExitMenuEntrySelected;
+
+            MenuEntries.Add(playGameMenuEntry);
+            MenuEntries.Add(optionsMenuEntry);
+            MenuEntries.Add(exitMenuEntry);
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Event handler for when the Play Game menu entry is selected.
+        /// </summary>
+        void PlayGameMenuEntrySelected(object sender, EventArgs e)
+        {
+            LoadingScreen.Load(ScreenManager, LoadGameplayScreen, true);
+        }
+
+
+        /// <summary>
+        /// Loading screen callback for activating the gameplay screen.
+        /// </summary>
+        void LoadGameplayScreen(object sender, EventArgs e)
+        {
+            GameplayScreen gameplayScreen = new GameplayScreen();
+            gameplayScreen.ScreenManager = this.ScreenManager;
+            gameplayScreen.Initialize();
+            ScreenManager.AddScreen(gameplayScreen);
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Options menu entry is selected.
+        /// </summary>
+        void OptionsMenuEntrySelected(object sender, EventArgs e)
+        {
+            ScreenManager.AddScreen(new OptionsMenuScreen());
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Exit menu entry is selected.
+        /// </summary>
+        void ExitMenuEntrySelected(object sender, EventArgs e)
+        {
+            OnCancel();
+        }
+
+
+        /// <summary>
+        /// When the user cancels the main menu, ask if they want to exit the sample.
+        /// </summary>
+        protected override void OnCancel()
+        {
+            const string message = "Exit Vector Rumble?";
+
+            MessageBoxScreen messageBox = new MessageBoxScreen(message);
+            messageBox.Accepted += ExitMessageBoxAccepted;
+            ScreenManager.AddScreen(messageBox);
+        }
+
+
+        /// <summary>
+        /// Event handler for when the user selects ok on the "are you sure
+        /// you want to exit" message box.
+        /// </summary>
+        void ExitMessageBoxAccepted(object sender, EventArgs e)
+        {
+            ScreenManager.Game.Exit();
+        }
+
+
+        #endregion
+    }
+}

+ 157 - 0
StarterKits/MacOS/VectorRumble/Screens/MenuEntry.cs

@@ -0,0 +1,157 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MenuEntry.cs
+//
+// XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Helper class represents a single entry in a MenuScreen. By default this
+    /// just draws the entry text string, but it can be customized to display menu
+    /// entries in different ways. This also provides an event that will be raised
+    /// when the menu entry is selected.
+    /// </summary>
+    /// <remarks>Based on a class in the Game State Management sample.</remarks>
+    class MenuEntry
+    {
+        #region Fields
+
+        /// <summary>
+        /// The text rendered for this entry.
+        /// </summary>
+        string text;
+
+        /// <summary>
+        /// Tracks a fading selection effect on the entry.
+        /// </summary>
+        /// <remarks>
+        /// The entries transition out of the selection effect when they are deselected.
+        /// </remarks>
+        float selectionFade;
+
+        #endregion
+
+        #region Properties
+
+
+        /// <summary>
+        /// Gets or sets the text of this menu entry.
+        /// </summary>
+        public string Text
+        {
+            get { return text; }
+            set { text = value; }
+        }
+
+
+        #endregion
+
+        #region Events
+
+
+        /// <summary>
+        /// Event raised when the menu entry is selected.
+        /// </summary>
+        public event EventHandler<EventArgs> Selected;
+
+
+        /// <summary>
+        /// Method for raising the Selected event.
+        /// </summary>
+        protected internal virtual void OnSelectEntry()
+        {
+            if (Selected != null)
+                Selected(this, EventArgs.Empty);
+        }
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs a new menu entry with the specified text.
+        /// </summary>
+        public MenuEntry(string text)
+        {
+            this.text = text;
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Updates the menu entry.
+        /// </summary>
+        public virtual void Update(MenuScreen screen, bool isSelected,
+                                                      GameTime gameTime)
+        {
+            // When the menu selection changes, entries gradually fade between
+            // their selected and deselected appearance, rather than instantly
+            // popping to the new state.
+            float fadeSpeed = (float)gameTime.ElapsedGameTime.TotalSeconds * 4;
+
+            if (isSelected)
+                selectionFade = Math.Min(selectionFade + fadeSpeed, 1);
+            else
+                selectionFade = Math.Max(selectionFade - fadeSpeed, 0);
+        }
+
+
+        /// <summary>
+        /// Draws the menu entry. This can be overridden to customize the appearance.
+        /// </summary>
+        public virtual void Draw(MenuScreen screen, Vector2 position,
+                                 bool isSelected, GameTime gameTime)
+        {
+            // Draw the selected entry in yellow, otherwise white.
+            Color color = isSelected ? Color.Yellow : Color.White;
+            
+            // Pulsate the size of the selected menu entry.
+            double time = gameTime.TotalGameTime.TotalSeconds;
+            
+            float pulsate = (float)Math.Sin(time * 6) + 1;
+            
+            float scale = 1 + pulsate * 0.05f * selectionFade;
+
+            // Modify the alpha to fade text out during transitions.
+            color = new Color(color.R, color.G, color.B, screen.TransitionAlpha);
+
+            // Draw text, centered on the middle of each line.
+            ScreenManager screenManager = screen.ScreenManager;
+            SpriteBatch spriteBatch = screenManager.SpriteBatch;
+            SpriteFont font = screenManager.Font;
+
+            Vector2 origin = new Vector2(0, font.LineSpacing / 2);
+
+            spriteBatch.DrawString(font, text, position, color, 0,
+                                   origin, scale, SpriteEffects.None, 0);
+        }
+
+
+        /// <summary>
+        /// Queries how much space this menu entry requires.
+        /// </summary>
+        public virtual int GetHeight(MenuScreen screen)
+        {
+            return screen.ScreenManager.Font.LineSpacing;
+        }
+
+
+        #endregion
+    }
+}

+ 233 - 0
StarterKits/MacOS/VectorRumble/Screens/MenuScreen.cs

@@ -0,0 +1,233 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MenuScreen.cs
+//
+// XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Base class for screens that contain a menu of options. The user can
+    /// move up and down to select an entry, or cancel to back out of the screen.
+    /// </summary>
+    /// <remarks>Based on a class in the Game State Management sample.</remarks>
+    abstract class MenuScreen : GameScreen
+    {
+        #region Fields
+
+        List<MenuEntry> menuEntries = new List<MenuEntry>();
+        int selectedEntry = 0;
+        AudioManager audioManager;
+
+        #endregion
+
+        #region Properties
+
+
+        /// <summary>
+        /// Gets the list of menu entries, so derived classes can add
+        /// or change the menu contents.
+        /// </summary>
+        protected IList<MenuEntry> MenuEntries
+        {
+            get { return menuEntries; }
+        }
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public MenuScreen()
+        {
+            TransitionOnTime = TimeSpan.FromSeconds(1.0);
+            TransitionOffTime = TimeSpan.FromSeconds(1.0);
+
+        }
+
+
+        /// <summary>
+        /// Load all content.
+        /// </summary>
+        public override void LoadContent()
+        {
+            // retrieve the audio manager, done here in lieu of Initialize
+            audioManager = (AudioManager)ScreenManager.Game.Services.GetService(
+                                                                typeof(AudioManager));
+            base.LoadContent();
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Responds to user input, changing the selected entry and accepting
+        /// or cancelling the menu.
+        /// </summary>
+        public override void HandleInput(InputState input)
+        {
+            // Move to the previous menu entry?
+            if (input.MenuUp)
+            {
+                selectedEntry--;
+
+                if (selectedEntry < 0)
+                    selectedEntry = menuEntries.Count - 1;
+
+                audioManager.PlayCue("menuMove");
+            }
+
+            // Move to the next menu entry?
+            if (input.MenuDown)
+            {
+                selectedEntry++;
+
+                if (selectedEntry >= menuEntries.Count)
+                    selectedEntry = 0;
+
+                audioManager.PlayCue("menuMove");
+            }
+			
+			OnSelectEntry(selectedEntry);
+			
+            // Accept or cancel the menu?
+            if (input.MenuSelect)
+            {
+                OnSelectEntry(selectedEntry);
+
+                audioManager.PlayCue("menuSelect");
+            }
+            else if (input.MenuCancel)
+            {
+                OnCancel();
+            }
+        }
+
+
+        /// <summary>
+        /// Handler for when the user has chosen a menu entry.
+        /// </summary>
+        protected virtual void OnSelectEntry(int entryIndex)
+        {
+            menuEntries[selectedEntry].OnSelectEntry();
+        }
+
+
+        /// <summary>
+        /// Handler for when the user has cancelled the menu.
+        /// </summary>
+        protected virtual void OnCancel()
+        {
+            ExitScreen();
+        }
+
+
+        /// <summary>
+        /// Helper overload makes it easy to use OnCancel as a MenuEntry event handler.
+        /// </summary>
+        protected void OnCancel(object sender, EventArgs e)
+        {
+            OnCancel();
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Updates the menu.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+            // Update each nested MenuEntry object.
+            for (int i = 0; i < menuEntries.Count; i++)
+            {
+                bool isSelected = IsActive && (i == selectedEntry);
+
+                menuEntries[i].Update(this, isSelected, gameTime);
+            }
+        }
+
+
+        /// <summary>
+        /// Draws the menu.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+            Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+            Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
+
+            Vector2 position = new Vector2(0f, viewportSize.Y * 0.55f);
+
+            // Make the menu slide into place during transitions, using a
+            // power curve to make things look more interesting (this makes
+            // the movement slow down as it nears the end).
+            float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
+
+            if (ScreenState == ScreenState.TransitionOn)
+                position.Y += transitionOffset * 256;
+            else
+                position.Y += transitionOffset * 512;
+
+            // Draw each menu entry in turn.
+            spriteBatch.Begin();
+
+            for (int i = 0; i < menuEntries.Count; i++)
+            {
+                Color color = Color.White;
+                float scale = 1.0f;
+
+                if (IsActive && (i == selectedEntry))
+                {
+                    // The selected entry is yellow, and has an animating size.
+                    double time = gameTime.TotalGameTime.TotalSeconds;
+                    float pulsate = (float)Math.Sin(time * 6f) + 1f;
+
+                    color = Color.Orange;
+                    scale += pulsate * 0.05f;
+                }
+
+                // Modify the alpha to fade text out during transitions.
+                color = new Color(color.R, color.G, color.B, TransitionAlpha);
+
+                // Draw text, centered on the middle of each line.
+                Vector2 origin = new Vector2(0, ScreenManager.Font.LineSpacing / 2);
+                Vector2 size = ScreenManager.Font.MeasureString(menuEntries[i].Text);
+                position.X = viewportSize.X / 2f - size.X / 2f * scale;
+                spriteBatch.DrawString(ScreenManager.Font, menuEntries[i].Text,
+                                                     position, color, 0, origin, scale,
+                                                     SpriteEffects.None, 0);
+
+                position.Y += ScreenManager.Font.LineSpacing;
+            }
+
+            spriteBatch.End();
+        }
+
+
+        #endregion
+    }
+}

+ 159 - 0
StarterKits/MacOS/VectorRumble/Screens/MessageBoxScreen.cs

@@ -0,0 +1,159 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MessageBoxScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A popup message box screen, used to display "are you sure?"
+    /// confirmation messages.
+    /// </summary>
+    /// <remarks>
+    /// This class is somewhat similar to one of the same name in the 
+    /// GameStateManagement sample.
+    /// </remarks>
+    class MessageBoxScreen : GameScreen
+    {
+        #region Constants
+        const string usageText = "A button, Space, Enter = ok\n" +
+                                 "B button, Esc = cancel";
+        #endregion
+
+        #region Fields
+
+        string message;
+        SpriteFont smallFont;
+
+        #endregion
+
+        #region Events
+
+        public event EventHandler<EventArgs> Accepted;
+        public event EventHandler<EventArgs> Cancelled;
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public MessageBoxScreen(string message)
+        {
+            this.message = message;
+
+            IsPopup = true;
+
+            TransitionOnTime = TimeSpan.FromSeconds(0.25);
+            TransitionOffTime = TimeSpan.FromSeconds(0.25);
+        }
+
+
+        /// <summary>
+        /// Loads graphics content for this screen.
+        /// </summary>
+        public override void LoadContent()
+        {
+            smallFont = ScreenManager.Game.Content.Load<SpriteFont>("Fonts/retroSmall");
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Responds to user input, accepting or cancelling the message box.
+        /// </summary>
+        public override void HandleInput(InputState input)
+        {
+            if (input.MenuSelect)
+            {
+                // Raise the accepted event, then exit the message box.
+                if (Accepted != null)
+                    Accepted(this, EventArgs.Empty);
+            
+                ExitScreen();
+            }
+            else if (input.MenuCancel)
+            {
+                // Raise the cancelled event, then exit the message box.
+                if (Cancelled != null)
+                    Cancelled(this, EventArgs.Empty);
+
+                ExitScreen();
+            }
+        }
+
+
+        #endregion
+
+        #region Draw
+
+
+        /// <summary>
+        /// Draws the message box.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            // Darken down any other screens that were drawn beneath the popup.
+            ScreenManager.FadeBackBufferToBlack(TransitionAlpha * 2 / 3);
+
+            // Center the message text in the viewport.
+            Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+            Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
+            Vector2 textSize = ScreenManager.Font.MeasureString(message);
+            Vector2 textPosition = (viewportSize - textSize) / 2;
+            Vector2 usageTextSize = smallFont.MeasureString(usageText);
+            Vector2 usageTextPosition = (viewportSize - usageTextSize) / 2;
+            usageTextPosition.Y = textPosition.Y + 
+                ScreenManager.Font.LineSpacing * 1.1f;
+
+            // Fade the popup alpha during transitions.
+            Color color = new Color(255, 255, 255, TransitionAlpha);
+
+            // Draw the background rectangles
+            Rectangle rect = new Rectangle(
+                (int)(Math.Min(usageTextPosition.X, textPosition.X)),
+                (int)(textPosition.Y),
+                (int)(Math.Max(usageTextSize.X, textSize.X)),
+                (int)(ScreenManager.Font.LineSpacing * 1.1f+ usageTextSize.Y)
+                );
+            rect.X -= (int)(0.1f * rect.Width);
+            rect.Y -= (int)(0.1f * rect.Height);
+            rect.Width += (int)(0.2f * rect.Width);
+            rect.Height += (int)(0.2f * rect.Height);
+
+            Rectangle rect2 = new Rectangle(rect.X - 1, rect.Y - 1, 
+                rect.Width + 2, rect.Height + 2);
+            ScreenManager.DrawRectangle(rect2, new Color(128, 128, 128, 
+                (byte)(192.0f * (float)TransitionAlpha / 255.0f)));
+            ScreenManager.DrawRectangle(rect, new Color(0, 0, 0, 
+                (byte)(232.0f * (float)TransitionAlpha / 255.0f)));
+
+            // Draw the message box text.
+            ScreenManager.SpriteBatch.Begin();
+            ScreenManager.SpriteBatch.DrawString(ScreenManager.Font, message,
+                                                 textPosition, color);
+            ScreenManager.SpriteBatch.DrawString(smallFont, usageText,
+                                                 usageTextPosition, color);
+            ScreenManager.SpriteBatch.End();
+        }
+
+
+        #endregion
+    }
+}

+ 168 - 0
StarterKits/MacOS/VectorRumble/Screens/OptionsMenuScreen.cs

@@ -0,0 +1,168 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// OptionsMenuScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// The options screen is brought up over the top of the main menu
+    /// screen, and gives the user a chance to configure the game
+    /// in various hopefully useful ways.
+    /// </summary>
+    /// <remarks>
+    /// This class is similar to one of the same name in the GameStateManagement sample.
+    /// </remarks>
+    class OptionsMenuScreen : MenuScreen
+    {
+        #region Fields
+
+
+        static string[] asteroidDensity = { "None", "Low", "Medium", "High" };
+        static int currentAsteroidDensity = 2;
+
+        static string[] wallStyle = { "None", "One", "Two", "Three" };
+        static int currentWallStyle = 0;
+
+        static int scoreLimit = 10;
+        static bool motionBlur = true;
+        static bool neonEffect = true;
+
+        MenuEntry scoreLimitMenuEntry = new MenuEntry(String.Empty);
+        MenuEntry asteroidDensityMenuEntry = new MenuEntry(String.Empty);
+        MenuEntry wallStyleMenuEntry = new MenuEntry(String.Empty);
+        MenuEntry motionBlurMenuEntry = new MenuEntry(String.Empty);
+        MenuEntry neonEffectMenuEntry = new MenuEntry(String.Empty);
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor populates the menu with empty strings: the real values
+        /// are filled in by the Update method to reflect the changing settings.
+        /// </summary>
+        public OptionsMenuScreen()
+        {
+            currentAsteroidDensity = (int)WorldRules.AsteroidDensity;
+            currentWallStyle = (int)WorldRules.WallStyle;
+            scoreLimit = WorldRules.ScoreLimit;
+            motionBlur = WorldRules.MotionBlur;
+            neonEffect = WorldRules.NeonEffect;
+
+            scoreLimitMenuEntry.Selected += ScoreLimitMenuEntrySelected;
+            asteroidDensityMenuEntry.Selected += AsteroidDensityMenuEntrySelected;
+            wallStyleMenuEntry.Selected += WallStyleMenuEntrySelected;
+            motionBlurMenuEntry.Selected += MotionBlurMenuEntrySelected;
+            neonEffectMenuEntry.Selected += NeonEffectMenuEntrySelected;
+
+            MenuEntries.Add(scoreLimitMenuEntry);
+            MenuEntries.Add(asteroidDensityMenuEntry);
+            MenuEntries.Add(wallStyleMenuEntry);
+            MenuEntries.Add(motionBlurMenuEntry);
+            MenuEntries.Add(neonEffectMenuEntry);
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Updates the options screen, filling in the latest values for the menu text.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+            scoreLimitMenuEntry.Text = "Score Limit : " + scoreLimit.ToString();
+            asteroidDensityMenuEntry.Text = "Asteroid Density : " + 
+                asteroidDensity[currentAsteroidDensity];
+            wallStyleMenuEntry.Text = "Wall Style : " + wallStyle[currentWallStyle];
+            motionBlurMenuEntry.Text = "Motion Blur : " + motionBlur.ToString();
+            neonEffectMenuEntry.Text = "Neon Effect : " + neonEffect.ToString();
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Score Limit menu entry is selected.
+        /// </summary>
+        void ScoreLimitMenuEntrySelected(object sender, EventArgs e)
+        {
+            scoreLimit += 5;
+            if (scoreLimit > 25)
+                scoreLimit = 5;
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Asteroid Density menu entry is selected.
+        /// </summary>
+        void AsteroidDensityMenuEntrySelected(object sender, EventArgs e)
+        {
+            currentAsteroidDensity = (currentAsteroidDensity + 1) %
+                asteroidDensity.Length;
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Wall Style menu entry is selected.
+        /// </summary>
+        void WallStyleMenuEntrySelected(object sender, EventArgs e)
+        {
+            currentWallStyle = (currentWallStyle + 1) % wallStyle.Length;
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Motion Blur menu entry is selected.
+        /// </summary>
+        void MotionBlurMenuEntrySelected(object sender, EventArgs e)
+        {
+            motionBlur = !motionBlur;
+        }
+
+
+        /// <summary>
+        /// Event handler for when the NeonEffect menu entry is selected.
+        /// </summary>
+        void NeonEffectMenuEntrySelected(object sender, EventArgs e)
+        {
+            neonEffect = !neonEffect;
+        }
+
+
+        /// <summary>
+        /// When the user cancels the options screen, go back to the main menu.
+        /// </summary>
+        protected override void OnCancel()
+        {
+            WorldRules.AsteroidDensity = 
+                (AsteroidDensity)Enum.Parse(typeof(AsteroidDensity), 
+                                         asteroidDensity[currentAsteroidDensity], true);
+            WorldRules.WallStyle = (WallStyle)Enum.Parse(typeof(WallStyle), 
+                wallStyle[currentWallStyle], true);
+            WorldRules.ScoreLimit = scoreLimit;
+            WorldRules.MotionBlur = motionBlur;
+            WorldRules.NeonEffect = neonEffect;
+
+            ExitScreen();
+        }
+
+
+        #endregion
+    }
+}

+ 155 - 0
StarterKits/MacOS/VectorRumble/Screens/PauseMenuScreen.cs

@@ -0,0 +1,155 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// PauseMenuScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// The pause menu comes up over the top of the game,
+    /// giving the player options to resume or quit.
+    /// </summary>
+    /// <remarks>
+    /// This class is somewhat similar to one of the same name in the 
+    /// GameStateManagement sample.
+    /// </remarks>
+    class PauseMenuScreen : MenuScreen
+    {
+        #region Fields
+        Texture2D titleTexture;
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public PauseMenuScreen()
+        {
+            MenuEntry resumeGameMenuEntry = new MenuEntry("Resume Game");
+            MenuEntry quitGameEntry = new MenuEntry("Quit Game");
+
+            resumeGameMenuEntry.Selected += ResumeGameMenuEntrySelected;
+            quitGameEntry.Selected += QuitGameEntrySelected;
+
+            MenuEntries.Add(resumeGameMenuEntry);
+            MenuEntries.Add(quitGameEntry);
+
+            // Flag that there is no need for the game to transition
+            // off when the pause menu is on top of it.
+            IsPopup = true;
+        }
+
+        public override void LoadContent()
+        {
+            titleTexture = ScreenManager.Game.Content.Load<Texture2D>("Textures/title");
+            base.LoadContent();
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// When the user cancels the pause menu, resume the game.
+        /// </summary>
+        protected override void OnCancel()
+        {
+            ExitScreen();
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Resume Game menu entry is selected.
+        /// </summary>
+        void ResumeGameMenuEntrySelected(object sender, EventArgs e)
+        {
+            ExitScreen();
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Quit Game menu entry is selected.
+        /// </summary>
+        void QuitGameEntrySelected(object sender, EventArgs e)
+        {
+            // Quit the game, after a confirmation message box.
+            const string message = "Are you sure?";
+
+            MessageBoxScreen messageBox = new MessageBoxScreen(message);
+            messageBox.Accepted += QuitMessageBoxAccepted;
+            ScreenManager.AddScreen(messageBox);
+        }
+
+
+        /// <summary>
+        /// Event handler for when the user selects ok on the "are you sure
+        /// you want to quit" message box. This uses the loading screen to
+        /// transition from the game back to the main menu screen.
+        /// </summary>
+        void QuitMessageBoxAccepted(object sender, EventArgs e)
+        {
+            LoadingScreen.Load(ScreenManager, LoadMainMenuScreen, false);
+        }
+
+
+        /// <summary>
+        /// Loading screen callback for activating the main menu screen,
+        /// used when quitting from the game.
+        /// </summary>
+        void LoadMainMenuScreen(object sender, EventArgs e)
+        {
+            ScreenManager.AddScreen(new BackgroundScreen());
+            ScreenManager.AddScreen(new MainMenuScreen());
+        }
+
+
+        #endregion
+
+        #region Draw
+
+
+        /// <summary>
+        /// Draws the pause menu screen. This darkens down the gameplay screen
+        /// that is underneath us, and then chains to the base MenuScreen.Draw.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            ScreenManager.FadeBackBufferToBlack(TransitionAlpha * 2 / 3);
+
+            Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+            Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
+
+            // title
+            Vector2 titlePosition = new Vector2(
+                (viewportSize.X - titleTexture.Width) / 2f,
+                viewportSize.Y * 0.18f);
+            titlePosition.Y -= (float)Math.Pow(TransitionPosition, 2) * titlePosition.Y;
+            Color titleColor = Color.White;
+
+            ScreenManager.SpriteBatch.Begin();
+            ScreenManager.SpriteBatch.Draw(titleTexture, titlePosition,
+                new Color(titleColor.R, titleColor.G, titleColor.B, TransitionAlpha));
+            ScreenManager.SpriteBatch.End();
+
+            base.Draw(gameTime);
+        }
+
+
+        #endregion
+    }
+}

+ 267 - 0
StarterKits/MacOS/VectorRumble/Simulation/Actor.cs

@@ -0,0 +1,267 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Actor.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A base class for all active objects in the game.
+    /// </summary>
+    abstract class Actor
+    {
+        #region Fields
+        /// <summary>
+        /// The world which owns this actor.
+        /// </summary>
+        protected World world;
+
+        protected bool dead = true;
+        protected float life = 0f;
+
+        protected Vector2 position = Vector2.Zero;
+        protected Vector2 lastPosition = Vector2.Zero;
+        protected Vector2 velocity = Vector2.Zero;
+        protected float rotation = 0f;
+
+        // collision data
+        protected bool collidable = true;
+        protected float mass = 1f;
+        protected float radius = 16f;
+        protected bool collidedThisFrame = false;
+
+        // visual data
+        protected VectorPolygon polygon = null;
+        protected Color color = Color.White;
+        protected bool useMotionBlur = WorldRules.MotionBlur;
+        #endregion
+
+        #region Properties
+        public World World
+        {
+            get { return world; }
+        }
+
+        public bool Dead
+        {
+            get { return dead; }
+        }
+
+        public Vector2 Position
+        {
+            get { return position; }
+            set
+            {
+                lastPosition = position;
+                position = value;
+            }
+        }
+
+        public Vector2 Velocity
+        {
+            get { return velocity; }
+            set { velocity = value; }
+        }
+
+        public float Rotation
+        {
+            get { return rotation; }
+            set { rotation = value; }
+        }
+
+        public bool Collidable
+        {
+            get { return collidable; }
+        }
+
+        public float Mass
+        {
+            get { return mass; }
+        }
+
+        public bool CollidedThisFrame
+        {
+            get { return collidedThisFrame; }
+            set { collidedThisFrame = value; }
+        }
+
+        public float Radius
+        {
+            get { return radius; }
+        }
+
+        public Color Color
+        {
+            get { return color; }
+        }
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new actor.
+        /// </summary>
+        /// <param name="world">The world that this actor belongs to.</param>
+        public Actor(World world)
+        {
+            if (world == null)
+            {
+                throw new ArgumentNullException("world");
+            }
+            this.world = world;
+        }
+        #endregion
+
+        #region Update
+        /// <summary>
+        /// Update the actor.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        public virtual void Update(float elapsedTime) 
+        {
+            collidedThisFrame = false;
+        }
+        #endregion
+
+        #region Drawing
+        /// <summary>
+        /// Render the actor.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        /// <param name="lineBatch">The LineBatch to render to.</param>
+        public virtual void Draw(float elapsedTime, LineBatch lineBatch)
+        {
+            if (polygon != null)
+            {
+                if (lineBatch == null)
+                {
+                    throw new ArgumentNullException("lineBatch");
+                }
+                // create the transformation
+                Matrix rotationMatrix = Matrix.CreateRotationZ(rotation);
+                Matrix world =  rotationMatrix *
+                    Matrix.CreateTranslation(position.X, position.Y, 0f);
+                // transform the polygon
+                polygon.Transform(world);
+                // draw the polygon
+                lineBatch.DrawPolygon(polygon, color);
+
+                // draw the motion blur
+                if (useMotionBlur && velocity.LengthSquared() > 1024f)
+                {
+                    // draw several "blur" polygons behind the real polygon
+                    Vector2 backwards = Vector2.Normalize(position - lastPosition);
+                    float speed = velocity.Length();
+                    for (int i = 1; i < speed / 16; ++i)
+                    {
+                        // calculate the "blur" polygon's position
+                        Vector2 blurPosition = this.position - backwards * (i * 4);
+                        // calculate the transformation for the "blur" polygon
+                        Matrix blurWorld = rotationMatrix *
+                            Matrix.CreateTranslation(blurPosition.X, blurPosition.Y, 0);
+                        // transform the polygon to the "blur" location
+                        polygon.Transform(blurWorld);
+                        // calculate the alpha of the "blur" location
+                        byte alpha = (byte)(160 / (i + 1));
+                        if (alpha < 1)
+                            break;
+                        // draw the "blur" polygon
+                        lineBatch.DrawPolygon(polygon,
+                            new Color(color.R, color.G, color.B, alpha));
+                    }
+                }
+            }
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Defines the interaction between this actor and a target actor
+        /// when they touch.
+        /// </summary>
+        /// <param name="target">The actor that is touching this object.</param>
+        /// <returns>True if the objects meaningfully interacted.</returns>
+        public virtual bool Touch(Actor target) 
+        { 
+            return true; 
+        }
+
+
+        /// <summary>
+        /// Damage this actor by the amount provided.
+        /// </summary>
+        /// <remarks>
+        /// This function is provided in lieu of a Life mutation property to allow 
+        /// classes of objects to restrict which kinds of objects may damage them,
+        /// and under what circumstances they may be damaged.
+        /// </remarks>
+        /// <param name="source">The actor responsible for the damage.</param>
+        /// <param name="damageAmount">The amount of damage.</param>
+        /// <returns>If true, this object was damaged.</returns>
+        public virtual bool Damage(Actor source, float damageAmount)
+        {
+            // reduce life by the given amound
+            life -= damageAmount;
+            // if life had gone below 0, then we're dead
+            // -- 0 health actors are destroyed by any damage
+            if (life < 0f)
+            {
+                Die(source);
+            }
+            return true;
+        }
+
+        
+        /// <summary>
+        /// Kills this actor, in response to the given actor.
+        /// </summary>
+        /// <param name="source">The actor responsible for the kill.</param>
+        public virtual void Die(Actor source) 
+        {
+            if (dead == false)
+            {
+                // arrrggghhhh
+                dead = true;
+                // remove this actor from the world
+                world.Actors.Garbage.Add(this);
+            }
+        }
+
+        
+        /// <summary>
+        /// Place this actor in the world.
+        /// </summary>
+        /// <param name="findSpawnPoint">
+        /// If true, the actor's position is changed to a valid, non-colliding point.
+        /// </param>
+        public virtual void Spawn(bool findSpawnPoint)
+        {
+            // find a new spawn point if requested
+            if (findSpawnPoint)
+            {
+                position = world.FindSpawnPoint(this);
+            }
+            // reset the velocity
+            velocity = Vector2.Zero;
+
+            // respawn this actor
+            if (dead == true)
+            {
+                // I LIVE
+                dead = false;
+                // add this object to the world's actors list
+                world.Actors.Add(this);
+            }
+        }
+        #endregion
+    }
+}

+ 144 - 0
StarterKits/MacOS/VectorRumble/Simulation/Collision.cs

@@ -0,0 +1,144 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Collision.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// A code container for collision-related mathematical functions.
+    /// </summary>
+    static class Collision
+    {
+        /// <summary>
+        /// Determines the point of intersection between two line segments, 
+        /// as defined by four points.
+        /// </summary>
+        /// <param name="a">The first point on the first line segment.</param>
+        /// <param name="b">The second point on the first line segment.</param>
+        /// <param name="c">The first point on the second line segment.</param>
+        /// <param name="d">The second point on the second line segment.</param>
+        /// <param name="point">The output value with the interesection, if any.</param>
+        /// <remarks>The output parameter "point" is only valid
+        /// when the return value is true.</remarks>
+        /// <returns>True if intersecting, false otherwise.</returns>
+        public static bool LineLineIntersect(Vector2 a, Vector2 b, Vector2 c, 
+            Vector2 d, out Vector2 point)
+        {
+            point = Vector2.Zero;
+
+            double r, s;
+            double denominator = (b.X - a.X) * (d.Y - c.Y) - (b.Y - a.Y) * (d.X - c.X);
+
+            // If the denominator in above is zero, AB & CD are colinear
+            if (denominator == 0)
+            {
+                return false;
+            }
+
+            double numeratorR = (a.Y - c.Y) * (d.X - c.X) - (a.X - c.X) * (d.Y - c.Y);
+            r = numeratorR / denominator;
+
+            double numeratorS = (a.Y - c.Y) * (b.X - a.X) - (a.X - c.X) * (b.Y - a.Y);
+            s = numeratorS / denominator;
+
+            // non-intersecting
+            if (r < 0 || r > 1 || s < 0 || s > 1)
+            {
+                return false;
+            }
+
+            // find intersection point
+            point.X = (float)(a.X + (r * (b.X - a.X)));
+            point.Y = (float)(a.Y + (r * (b.Y - a.Y)));
+
+            return true;
+        }
+
+
+        /// <summary>
+        /// Determine if two circles intersect or contain each other.
+        /// </summary>
+        /// <param name="center1">The center of the first circle.</param>
+        /// <param name="radius1">The radius of the first circle.</param>
+        /// <param name="center2">The center of the second circle.</param>
+        /// <param name="radius2">The radius of the second circle.</param>
+        /// <returns>True if the circles intersect or contain one another.</returns>
+        public static bool CircleCircleIntersect(Vector2 center1, float radius1, 
+            Vector2 center2, float radius2)
+        {
+            Vector2 line = center2 - center1;
+            // we use LengthSquared to avoid a costly square-root call
+            return (line.LengthSquared() <= (radius1 + radius2) * (radius1 + radius2));
+        }
+
+
+        /// <summary>
+        /// Data defining a circle/line collision result.
+        /// </summary>
+        public struct CircleLineCollisionResult
+        {
+            public bool Collision;
+            public Vector2 Point;
+            public Vector2 Normal;
+            public float Distance;
+        }
+
+
+        /// <summary>
+        /// Determines if a circle and line segment intersect, and if so, how they do.
+        /// </summary>
+        /// <param name="position">The center of the circle.</param>
+        /// <param name="radius">The radius of the circle.</param>
+        /// <param name="lineStart">The first point on the line segment.</param>
+        /// <param name="lineEnd">The second point on the line segment.</param>
+        /// <param name="result">The result data for the collision.</param>
+        /// <returns>True if a collision occurs, provided for convenience.</returns>
+        public static bool CircleLineCollide(Vector2 center, float radius, 
+            Vector2 lineStart, Vector2 lineEnd, ref CircleLineCollisionResult result)
+        {
+            Vector2 AC = center - lineStart;
+            Vector2 AB = lineEnd - lineStart;
+            float ab2 = AB.LengthSquared();
+            if (ab2 <= 0f)
+            {
+                return false;
+            }
+            float acab = Vector2.Dot(AC, AB);
+            float t = acab / ab2;
+
+            if (t < 0.0f)
+                t = 0.0f;
+            else if (t > 1.0f)
+                t = 1.0f;
+
+            result.Point = lineStart + t * AB;
+            result.Normal = center - result.Point;
+
+            float h2 = result.Normal.LengthSquared();
+            float r2 = radius * radius;
+
+            if (h2 > r2)
+            {
+                result.Collision = false;
+            }
+            else
+            {
+                result.Normal.Normalize();
+                result.Distance = (radius - (center - result.Point).Length());
+                result.Collision = true;
+            }
+
+            return result.Collision;
+        }
+    }
+}

+ 42 - 0
StarterKits/MacOS/VectorRumble/Simulation/CollisionResult.cs

@@ -0,0 +1,42 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// CollisionResult.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// The result of a collision query
+    /// </summary>
+    struct CollisionResult
+    {
+        /// <summary>
+        /// How far away did the collision occur down the ray
+        /// </summary>
+        public float Distance;
+
+        /// <summary>
+        /// The collision "direction"
+        /// </summary>
+        public Vector2 Normal;
+
+        /// <summary>
+        /// What caused the collison (what the source ran into)
+        /// </summary>
+        public Actor Actor;
+
+        public static int Compare(CollisionResult a, CollisionResult b)
+        {
+            return a.Distance.CompareTo(b.Distance);
+        }
+    } 
+}

+ 200 - 0
StarterKits/MacOS/VectorRumble/Simulation/PowerUp.cs

@@ -0,0 +1,200 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// PowerUp.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Base class for all power-ups that exist in the game.
+    /// </summary>
+    abstract class PowerUp : Actor
+    {
+        #region Constants
+        /// <summary>
+        /// The scalar between time elapsed and the scale period.
+        /// </summary>
+        protected float timeToScalePeriod = 8f;
+        #endregion
+
+        #region Fields
+        /// <summary>
+        /// A second polygon for rendering the power-up.
+        /// </summary>
+        protected VectorPolygon innerPolygon;
+
+        /// <summary>
+        /// Keeps track of the period of the scale "pulse" of the power-up.
+        /// </summary>
+        protected float scalePeriodElapsed;
+
+        /// <summary>
+        /// Colors for the particle systems created when spawned and touched.
+        /// </summary>
+        protected Color[] particleColors = 
+            {
+                Color.White, Color.Lime, Color.Red, Color.CornflowerBlue, Color.Yellow
+            };
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new power-up.
+        /// </summary>
+        /// <param name="world">The world that this power-up belongs to.</param>
+        public PowerUp(World world) : base(world) 
+        {
+            this.mass = 500f;
+            this.polygon = VectorPolygon.CreateCircle(Vector2.Zero, 16f, 16);
+            this.innerPolygon = VectorPolygon.CreateCircle(Vector2.Zero, 10f, 16);
+        }
+        #endregion
+
+        #region Drawing
+        /// <summary>
+        /// Render the power-up.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        /// <param name="lineBatch">The LineBatch to render to.</param>
+        public override void Draw(float elapsedTime, LineBatch lineBatch)
+        {
+            // update the scale period
+            scalePeriodElapsed += elapsedTime * timeToScalePeriod;
+            while (scalePeriodElapsed < 0)
+            {
+                scalePeriodElapsed += MathHelper.TwoPi;
+            }
+            while (scalePeriodElapsed > MathHelper.TwoPi)
+            {
+                scalePeriodElapsed -= MathHelper.TwoPi;
+            }
+            // draw the polygons 
+            if (polygon != null)
+            {
+                if (lineBatch == null)
+                {
+                    throw new ArgumentNullException("lineBatch");
+                }
+                // calculate the transformation
+                Matrix scaleMatrix = Matrix.CreateScale(0.8f + 
+                    0.1f * (float)Math.Cos(scalePeriodElapsed));
+                Matrix world = scaleMatrix * 
+                    Matrix.CreateTranslation(position.X, position.Y, 0f);
+                // transform the polygons
+                polygon.Transform(world);
+                innerPolygon.Transform(world);
+                // draw the polygons
+                lineBatch.DrawPolygon(polygon, Color.White);
+                if (innerPolygon != null)
+                {
+                    lineBatch.DrawPolygon(innerPolygon, color, true);
+                }
+                // draw the motion blur
+                if (useMotionBlur && velocity.LengthSquared() > 1024f)
+                {
+                    // draw several "blur" polygons behind the real polygons
+                    Vector2 backwards = Vector2.Normalize(position - lastPosition);
+                    float speed = velocity.Length();
+                    for (int i = 1; i < speed / 16; ++i)
+                    {
+                        // calculate the "blur" position
+                        Vector2 blurPosition = this.position - backwards * (i * 4);
+                        // calculate the transformation
+                        Matrix blurWorld = scaleMatrix *
+                            Matrix.CreateTranslation(blurPosition.X, blurPosition.Y, 0);
+                        // transform the polygons
+                        polygon.Transform(blurWorld);
+                        if (innerPolygon != null)
+                        {
+                            innerPolygon.Transform(blurWorld);
+                        }
+                        // calculate the alpha of the "blur" polygons polygons
+                        byte alpha = (byte)(160 / (i + 1));
+                        if (alpha < 1)
+                            break;
+                        // draw the "blur" polygons
+                        lineBatch.DrawPolygon(polygon, new Color(255, 255, 255, alpha));
+                        if (innerPolygon != null)
+                        {
+                            lineBatch.DrawPolygon(innerPolygon,
+                                new Color(color.R, color.G, color.B, alpha), true);
+                        }
+                    }
+                }
+            }
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Defines the interaction between this power-up and a target actor
+        /// when they touch.
+        /// </summary>
+        /// <param name="target">The actor that is touching this object.</param>
+        /// <returns>True if the objects meaningfully interacted.</returns>
+        public override bool Touch(Actor target)
+        {
+            // if it touched a ship, then create a particle system and play a sound
+            Ship ship = target as Ship;
+            if (ship != null)
+            {
+                // tickle the ship's vibration motors
+                ship.FireGamepadMotors(0f, 0.25f);
+                // add a particle system for effect
+                world.ParticleSystems.Add(new ParticleSystem(this.Position, 
+                    Vector2.Zero, 24, 32f, 64f, 2f, 0.1f, particleColors));
+                // play the "power-up picked up" cue
+                world.AudioManager.PlayCue("powerUpTouch");
+                // kill the power-up
+                Die(target);
+                return false;
+            }
+
+            return base.Touch(target);
+        }
+
+        /// <summary>
+        /// Damage this power-up by the amount provided.
+        /// </summary>
+        /// <remarks>
+        /// Power-ups cannot be damaged.
+        /// </remarks>
+        /// <param name="source">The actor responsible for the damage.</param>
+        /// <param name="damageAmount">The amount of damage.</param>
+        /// <returns>If true, this object was damaged.</returns>
+        public override bool Damage(Actor source, float damageAmount)
+        {
+            return false;
+        }
+
+
+        /// <summary>
+        /// Place this power-up in the world.
+        /// </summary>
+        /// <param name="findSpawnPoint">
+        /// If true, the power-up's position is changed to a valid, non-colliding point.
+        /// </param>
+        public override void Spawn(bool findSpawnPoint)
+        {
+            // spawn the power-up
+            base.Spawn(findSpawnPoint);
+            // recreate the particle array using the current color
+            this.particleColors = new Color[] { this.color, Color.White };
+            // add a particle effect and play a sound effect
+            world.ParticleSystems.Add(new ParticleSystem(this.position, 
+                Vector2.Zero, 24, 32f, 64f, 2f, 0.1f, particleColors));
+            world.AudioManager.PlayCue("powerUpSpawn");
+        }
+        #endregion
+    }
+}

+ 226 - 0
StarterKits/MacOS/VectorRumble/Simulation/Projectile.cs

@@ -0,0 +1,226 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Projectile.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Base class for all projectiles that exist in the game.
+    /// </summary>
+    abstract class Projectile : Actor
+    {
+        #region Fields
+        /// <summary>
+        /// The player who fired this projectile.
+        /// </summary>
+        protected Ship owner;
+
+        /// <summary>
+        /// The speed that the projectile will move at.
+        /// </summary>
+        protected float speed = 0f;
+
+        /// <summary>
+        /// The amount that this projectile hurts it's target and those around it.
+        /// </summary>
+        protected float damageAmount = 0f;
+
+        /// <summary>
+        /// The radius at which this projectile hurts other actors when it explodes.
+        /// </summary>
+        protected float damageRadius = 0f;
+
+        /// <summary>
+        /// The amount of time before this projectile dies on it's own.
+        /// </summary>
+        protected float duration = 0f;
+
+        /// <summary>
+        /// If true, this object will damage it's owner if it hits it
+        /// </summary>
+        protected bool damageOwner = true;
+
+        /// <summary>
+        /// If true, this object explodes - calling Explode() - when it dies.
+        /// </summary>
+        protected bool explodes = false;
+
+        /// <summary>
+        /// The colors used in the particle system shown when this projectile hits.
+        /// </summary>
+        protected Color[] explosionColors;
+        #endregion
+
+        #region Properties
+        public Ship Owner
+        {
+            get { return owner; }
+        }
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new projectile.
+        /// </summary>
+        /// <param name="world">The world that this projectile belongs to.</param>
+        /// <param name="owner">The ship that fired this projectile, if any.</param>
+        /// <param name="direction">The initial direction for this projectile.</param>
+        public Projectile(World world, Ship owner, Vector2 direction)
+            : base(world) 
+        {
+            this.owner = owner;
+            this.position = owner.Position;
+            this.velocity = direction;
+        }
+        #endregion
+
+        #region Update
+        /// <summary>
+        /// Update the projectile.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        public override void Update(float elapsedTime)
+        {
+            // projectiles can "time out"
+            if (duration > 0f)
+            {
+                duration -= elapsedTime;
+                if (duration < 0f)
+                {
+                    Die(null);
+                }
+            }
+
+            base.Update(elapsedTime);
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Damages all actors in a radius around the projectile.
+        /// </summary>
+        /// <param name="touchedActor">The actor that was originally hit.</param>
+        public virtual void Explode(Actor touchedActor)
+        {
+            // if there is no radius, then don't bother
+            if (damageRadius <= 0f)
+            {
+                return;
+            }
+            // check each actor for damage
+            foreach (Actor actor in world.Actors)
+            {
+                // don't bother if it's already dead
+                if (actor.Dead == true)
+                {
+                    continue;
+                }
+                // don't hurt the actor that the projectile hit, it's already hurt
+                if (actor == touchedActor)
+                {
+                    continue;
+                }
+                // don't hit the owner if the damageOwner flag is off
+                if ((actor == owner) && (damageOwner == false))
+                {
+                    continue;
+                }
+                // measure the distance to the actor and see if it's in range
+                float distance = (actor.Position - this.Position).Length();
+                if (distance <= damageRadius)
+                {
+                    // adjust the amount of damage based on the distance
+                    // -- note that damageRadius <= 0 is accounted for earlier
+                    float adjustedDamage = damageAmount *
+                        (damageRadius - distance) / damageRadius;
+                    // if we're still damaging the actor, then go ahead and apply it
+                    if (adjustedDamage > 0f)
+                    {
+                        actor.Damage(this, adjustedDamage);
+                    }
+                }
+            }
+        }
+
+        
+        /// <summary>
+        /// Defines the interaction between this projectile and a target actor
+        /// when they touch.
+        /// </summary>
+        /// <param name="target">The actor that is touching this object.</param>
+        /// <returns>True if the objects meaningfully interacted.</returns>
+        public override bool Touch(Actor target)
+        {
+            // check the target, if we have one
+            if (target != null)
+            {
+                // don't bother hitting any power-ups
+                if (target is PowerUp)
+                {
+                    return false;
+                }
+                // don't hit the owner if the damageOwner flag isn't set
+                if ((target == owner) && (this.damageOwner == false))
+                {
+                    return false;
+                }
+                // don't hit other projectiles from the same ship
+                Projectile projectile = target as Projectile;
+                if ((projectile != null) && (projectile.Owner == this.Owner))
+                {
+                    return false;
+                }
+                // damage the target
+                target.Damage(this, this.damageAmount);
+            }
+
+            // either we hit something or the target is null - in either case, die
+            Die(target);
+            
+            return base.Touch(target);
+        }
+        
+
+        /// <summary>
+        /// Kills this projectile, in response to the given actor.
+        /// </summary>
+        /// <param name="source">The actor responsible for the kill.</param>
+        public override void Die(Actor source)
+        {
+            if (dead == false)
+            {
+                base.Die(source);
+                if (dead && explodes)
+                {
+                    Explode(source);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Place this projectile in the world.
+        /// </summary>
+        /// <param name="findSpawnPoint">
+        /// If true, the actor's position is changed to a valid, non-colliding point.
+        /// </param>
+        public override void Spawn(bool findSpawnPoint)
+        {
+            Vector2 newVelocity = speed * Vector2.Normalize(velocity);
+            base.Spawn(findSpawnPoint);
+            // reset the velocity to the speed times the current direction;
+            velocity = newVelocity;
+        }
+        #endregion
+    }
+}

+ 107 - 0
StarterKits/MacOS/VectorRumble/Simulation/Weapon.cs

@@ -0,0 +1,107 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Weapon.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Base class for all weapons that exist in the game.
+    /// </summary>
+    abstract class Weapon
+    {
+        #region Fields
+        /// <summary>
+        /// The ship that owns this weapon.
+        /// </summary>
+        protected Ship owner = null;
+
+        /// <summary>
+        /// The amount of time remaining before this weapon can fire again.
+        /// </summary>
+        protected float timeToNextFire = 0f;
+
+        /// <summary>
+        /// The minimum amount of time between each firing of this weapon.
+        /// </summary>
+        protected float fireDelay = 0f;
+
+        /// <summary>
+        /// The name of the audio cue played when this weapon fires.
+        /// </summary>
+        protected string fireCueName = String.Empty;
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Constructs a new weapon.
+        /// </summary>
+        /// <param name="owner">The ship that owns this weapon.</param>
+        public Weapon(Ship owner)
+        {
+            if (owner == null)
+            {
+                throw new ArgumentNullException("owner");
+            }
+            this.owner = owner;
+        }
+        #endregion
+
+        #region Update
+        public virtual void Update(float elapsedTime)
+        {
+            // count down to when the weapon can fire again
+            if (timeToNextFire > 0f)
+            {
+                timeToNextFire = MathHelper.Max(timeToNextFire - elapsedTime, 0f);
+            }
+        }
+        #endregion
+
+        #region Interaction
+        /// <summary>
+        /// Fire the weapon in the direction given.
+        /// </summary>
+        /// <param name="direction">The direction that the weapon is firing in.</param>
+        public virtual void Fire(Vector2 direction)
+        {
+            // if we can't fire yet, then we're done
+            if (timeToNextFire > 0f)
+            {
+                return;
+            }
+
+            // the owner is no longer safe from damage
+            owner.Safe = false;
+
+            // set the timer
+            timeToNextFire = fireDelay;
+
+            // create and spawn the projectile
+            CreateProjectiles(direction);
+
+            // play the audio cue for firing
+            if (String.IsNullOrEmpty(fireCueName) == false)
+            {
+                this.owner.World.AudioManager.PlayCue(fireCueName);
+            }
+        }
+
+
+        /// <summary>
+        /// Create and spawn the projectile(s) from a firing from this weapon.
+        /// </summary>
+        /// <param name="direction">The direction that the projectile will move.</param>
+        protected abstract void CreateProjectiles(Vector2 direction);
+        #endregion
+    }
+}

+ 736 - 0
StarterKits/MacOS/VectorRumble/Simulation/World.cs

@@ -0,0 +1,736 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// World.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// Owns all game state and executes all game-wide logic.
+    /// </summary>
+    class World
+    {
+        #region Constants
+        /// <summary>
+        /// The number of seconds before the first power-up appears in a game.
+        /// </summary>
+        const float initialPowerUpDelay = 10f;
+
+        /// <summary>
+        /// The time between each power-up spawn.
+        /// </summary>
+        const float powerUpDelay = 20f;
+
+        /// <summary>
+        /// The number of stars to generate in the starfield.
+        /// </summary>
+        const int starCount = 2048;
+
+        /// <summary>
+        /// How far starfield should generate outside the dimensions of the game field.
+        /// </summary>
+        const int starfieldBuffer = 512;
+        #endregion
+
+        #region Fields
+        Random random = new Random();
+
+        /// <summary>
+        /// The dimensions of the game board.
+        /// </summary>
+        Vector2 dimensions;
+
+        /// <summary>
+        /// The safe dimensions of the game board.
+        /// </summary>
+        Rectangle safeDimensions;
+
+        /// <summary>
+        /// The timer to see if another power-up can arrive.
+        /// </summary>
+        float powerUpTimer;
+
+        /// <summary>
+        /// The audio manager that all objects in the world will use.
+        /// </summary>
+        private AudioManager audioManager;
+
+        /// <summary>
+        /// All ships that might enter the game.
+        /// </summary>
+        Ship[] ships;
+
+        /// <summary>
+        /// The walls in the game.
+        /// </summary>
+        Vector2[] walls;
+
+        /// <summary>
+        /// The starfield effect behind the game-board.
+        /// </summary>
+        Starfield starfield;
+
+        /// <summary>
+        /// All actors in the game.
+        /// </summary>
+        CollectCollection<Actor> actors;
+
+        /// <summary>
+        /// All particle-systems in the game.
+        /// </summary>
+        CollectCollection<ParticleSystem> particleSystems;
+
+        /// <summary>
+        /// Cached list of collision results, for more optimal collision detection.
+        /// </summary>
+        List<CollisionResult> collisionResults = new List<CollisionResult>();
+        #endregion
+
+        #region Properties
+        public AudioManager AudioManager
+        {
+            get { return audioManager; }
+            set { audioManager = value; }
+        }
+
+        public Starfield Starfield
+        {
+            get { return starfield; }
+        }
+
+        public Ship[] Ships
+        {
+            get { return ships; }
+        }
+
+        public CollectCollection<Actor> Actors
+        {
+            get { return actors; }
+        }
+
+        public CollectCollection<ParticleSystem> ParticleSystems
+        {
+            get { return particleSystems; }
+        }
+        #endregion
+
+        #region Initialization
+        /// <summary>
+        /// Construct a new World object, holding the game simulation.
+        /// </summary>
+        public World(Vector2 dimensions)
+        {
+            this.dimensions = dimensions;
+            safeDimensions = new Rectangle(
+                (int)(dimensions.X * 0.05f), (int)(dimensions.Y * 0.05f), 
+                (int)(dimensions.X * 0.90f), (int)(dimensions.Y * 0.90f));
+            // create the players
+            ships = new Ship[4];
+            ships[0] = new Ship(this, PlayerIndex.One);
+            ships[1] = new Ship(this, PlayerIndex.Two);
+            ships[2] = new Ship(this, PlayerIndex.Three);
+            ships[3] = new Ship(this, PlayerIndex.Four);
+            // create the starfield
+            starfield = new Starfield(starCount, new Rectangle(
+                starfieldBuffer * -1,
+                starfieldBuffer  * -1,
+                (int)this.dimensions.X + starfieldBuffer * 2,
+                (int)this.dimensions.Y + starfieldBuffer * 2));
+            // create a new list of actors
+            actors = new CollectCollection<Actor>(this);
+            // create a new list of particle systems
+            particleSystems = new CollectCollection<ParticleSystem>(this);
+        }
+        #endregion
+
+        #region New Game
+        public void StartNewGame()
+        {
+            // create the walls
+            CreateWalls();
+
+            // clear out the actors list
+            actors.Clear();
+            // add the world actor
+            WorldActor worldActor = new WorldActor(this);
+            actors.Add(worldActor);
+            // add the players to the actor list - they won't be removed
+            for (int i = 0; i < ships.Length; i++)
+            {
+                actors.Add(ships[i]);
+            }
+
+            // spawn asteroids
+            switch (WorldRules.AsteroidDensity)
+            {
+                case AsteroidDensity.None:
+                    break;
+                case AsteroidDensity.Low:
+                    SpawnAsteroids(4, 3, 0);
+                    break;
+                case AsteroidDensity.Medium:
+                    SpawnAsteroids(6, 4, 1);
+                    break;
+                case AsteroidDensity.High:
+                    SpawnAsteroids(8, 6, 2);
+                    break;
+            }
+
+            // set up the power-up timer for the initial delay
+            powerUpTimer = initialPowerUpDelay;
+
+            // set up the starfield
+            starfield.SetTargetPosition(dimensions * 0.5f);
+        }
+
+
+        /// <summary>
+        /// Initialize the walls based on the current world rules.
+        /// </summary>
+        private void CreateWalls()
+        {
+            switch (WorldRules.WallStyle)
+            {
+                case WallStyle.None:
+                    walls = new Vector2[8];
+                    break;
+                case WallStyle.One:
+                    walls = new Vector2[10];
+                    break;
+                case WallStyle.Two:
+                    walls = new Vector2[12];
+                    break;
+                case WallStyle.Three:
+                    walls = new Vector2[14];
+                    break;
+            }
+
+            // The outer boundaries
+            walls[0] = new Vector2(safeDimensions.X, safeDimensions.Y);
+            walls[1] = new Vector2(safeDimensions.X, 
+                safeDimensions.Y + safeDimensions.Height);
+            walls[2] = new Vector2(safeDimensions.X + safeDimensions.Width,
+                safeDimensions.Y);
+            walls[3] = new Vector2(safeDimensions.X + safeDimensions.Width,
+                safeDimensions.Y + safeDimensions.Height);
+            walls[4] = new Vector2(safeDimensions.X, safeDimensions.Y);
+            walls[5] = new Vector2(safeDimensions.X + safeDimensions.Width,
+                safeDimensions.Y);
+            walls[6] = new Vector2(safeDimensions.X,
+                safeDimensions.Y + safeDimensions.Height);
+            walls[7] = new Vector2(safeDimensions.X + safeDimensions.Width,
+                safeDimensions.Y + safeDimensions.Height);
+
+            int quarterX = safeDimensions.Width / 4;
+            int quarterY = safeDimensions.Height / 4;
+            int halfY = safeDimensions.Height / 2;
+
+            switch (WorldRules.WallStyle)
+            {
+                case WallStyle.One:
+                    // Cross line
+                    walls[8] = new Vector2(safeDimensions.X + quarterX, 
+                        safeDimensions.Y + halfY);
+                    walls[9] = new Vector2(safeDimensions.X + 3 * quarterX,
+                        safeDimensions.Y + halfY);
+                    break;
+                case WallStyle.Two:
+                    walls[8] = new Vector2(safeDimensions.X + quarterX, 
+                        safeDimensions.Y + quarterY);
+                    walls[9] = new Vector2(safeDimensions.X + quarterX, 
+                        safeDimensions.Y + 3 * quarterY);
+                    walls[10] = new Vector2(safeDimensions.X + 3 * quarterX, 
+                        safeDimensions.Y + quarterY);
+                    walls[11] = new Vector2(safeDimensions.X + 3 * quarterX, 
+                        safeDimensions.Y + 3 * quarterY);
+                    break;
+                case WallStyle.Three:
+                    // Cross line
+                    walls[8] = new Vector2(safeDimensions.X + quarterX,
+                        safeDimensions.Y + halfY);
+                    walls[9] = new Vector2(safeDimensions.X + 3 * quarterX,
+                        safeDimensions.Y + halfY);
+                    walls[10] = new Vector2(safeDimensions.X + quarterX, 
+                        safeDimensions.Y + quarterY);
+                    walls[11] = new Vector2(safeDimensions.X + quarterX, 
+                        safeDimensions.Y + 3 * quarterY);
+                    walls[12] = new Vector2(safeDimensions.X + 3 * quarterX, 
+                        safeDimensions.Y + quarterY);
+                    walls[13] = new Vector2(safeDimensions.X + 3 * quarterX, 
+                        safeDimensions.Y + 3 * quarterY);
+                    break;
+            }
+        }
+
+
+        /// <summary>
+        /// Create many asteroids and add them to the game world.
+        /// </summary>
+        /// <param name="smallCount">The number of "small" asteroids to create.</param>
+        /// <param name="mediumCount">The number of "medium" asteroids to create.
+        /// </param>
+        /// <param name="largeCount">The number of "large" asteroids to create.</param>
+        private void SpawnAsteroids(int smallCount, int mediumCount, int largeCount)
+        {
+            // create small asteroids
+            for (int i = 0; i < smallCount; ++i)
+            {
+                float radius = 8.0f + 16.0f * (float)random.NextDouble();
+
+                Asteroid asteroid = new Asteroid(this, radius);
+                asteroid.Spawn(true);
+            }
+            // create medium-sized asteroids
+            for (int i = 0; i < mediumCount; ++i)
+            {
+                float radius = 24.0f + 16.0f * (float)random.NextDouble();
+
+                Asteroid asteroid = new Asteroid(this, radius);
+                asteroid.Spawn(true);
+            }
+            // create large asteroids
+            for (int i = 0; i < largeCount; ++i)
+            {
+                float radius = 32.0f + 64.0f * (float)random.NextDouble();
+
+                Asteroid asteroid = new Asteroid(this, radius);
+                asteroid.Spawn(true);
+            }
+        }
+        
+        
+        /// <summary>
+        /// Create a new power-up in the world, if possible
+        /// </summary>
+        void SpawnPowerUp()
+        {
+            // check if there is a powerup in the world
+            for (int i = 0; i < actors.Count; ++i)
+            {
+                if (actors[i] is PowerUp)
+                {
+                    return;
+                }
+            }
+            // create the new power-up
+            PowerUp powerup = null;
+            switch (random.Next(3))
+            {
+                case 0:
+                    powerup = new DoubleLaserPowerUp(this);
+                    break;
+                case 1:
+                    powerup = new TripleLaserPowerUp(this);
+                    break;
+                case 2:
+                    powerup = new RocketPowerUp(this);
+                    break;
+            }
+            // add the new power-up to the world
+            powerup.Spawn(true);
+        }        
+        #endregion
+
+        #region Update and Draw
+        /// <summary>
+        /// Update the world simulation.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        public void Update(float elapsedTime)
+        {
+            // update all actors
+            for (int i = 0; i < actors.Count; i++)
+            {
+                actors[i].Update(elapsedTime);
+            }
+
+            // update collision
+            MoveWorld(elapsedTime);
+
+            // update particle systems
+            for (int i = 0; i < particleSystems.Count; i++)
+            {
+                particleSystems[i].Update(elapsedTime);
+                if (particleSystems[i].IsActive == false)
+                {
+                    particleSystems.Garbage.Add(particleSystems[i]);
+                }
+            }
+
+            // update the starfield
+            Vector2 starfieldTarget = Vector2.Zero;
+            int playingPlayers = 0;
+            for (int i = 0; i < ships.Length; i++)
+            {
+                if (ships[i].Playing)
+                {
+                    starfieldTarget += ships[i].Position;
+                    playingPlayers++;
+                }
+            }
+            if (playingPlayers > 0)
+            {
+                starfield.SetTargetPosition(starfieldTarget / playingPlayers);
+            }
+            starfield.Update(elapsedTime);
+
+            // check if we can create a new power-up yet
+            if (powerUpTimer > 0f)
+            {
+                powerUpTimer = Math.Max(powerUpTimer - elapsedTime, 0f);
+            }
+            if (powerUpTimer <= 0.0f)
+            {
+                SpawnPowerUp();
+                powerUpTimer = powerUpDelay;
+            }
+
+            // clean up the lists
+            actors.Collect();
+            particleSystems.Collect();
+        }
+
+
+        /// <summary>
+        /// Draw the walls.
+        /// </summary>
+        /// <param name="lineBatch">The LineBatch to render to.</param>
+        public void DrawWalls(LineBatch lineBatch)
+        {
+            if (lineBatch == null)
+            {
+                throw new ArgumentNullException("lineBatch");
+            }
+            // draw each wall-line
+            for (int wall = 0; wall < walls.Length / 2; wall++)
+            {
+                lineBatch.DrawLine(walls[wall * 2], walls[wall * 2 + 1], Color.Yellow);
+            }
+        }
+        #endregion
+
+        #region Collision
+        /// <summary>
+        /// Move all of the actors in the world.
+        /// </summary>
+        /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
+        private void MoveWorld(float elapsedTime)
+        {
+            Vector2 point = Vector2.Zero;
+            // move each actor
+            for (int i = 0; i < actors.Count; ++i)
+            {
+                if (actors[i].Dead)
+                {
+                    continue;
+                }
+                // determine how far they are going to move
+                Vector2 movement = actors[i].Velocity * elapsedTime;
+                // only allow actors that have not collided yet this frame to collide
+                // -- otherwise, objects can "double-hit" and trade their momentum fast
+                if (actors[i].CollidedThisFrame == false)
+                {
+                    movement = MoveAndCollide(actors[i], movement);
+                }
+                // determine the new position
+                actors[i].Position += movement;
+                // determine if their new position taks them through a wall
+                for (int w = 0; w < walls.Length / 2; ++w)
+                {
+                    if (actors[i] is Projectile)
+                    {
+                        if (Collision.LineLineIntersect(actors[i].Position, 
+                            actors[i].Position - movement, walls[w * 2], 
+                            walls[w * 2 + 1], out point))
+                        {
+                            actors[i].Touch(actors[0]);
+                        }
+                    }
+                    else
+                    {
+                        Collision.CircleLineCollisionResult result = 
+                            new Collision.CircleLineCollisionResult();
+                        if (Collision.CircleLineCollide(actors[i].Position, 
+                            actors[i].Radius, walls[w * 2], walls[w * 2 + 1], 
+                            ref result))
+                        {
+                            // if a non-projectile hits a wall, bounce slightly
+                            float vn = Vector2.Dot(actors[i].Velocity, result.Normal);
+                            actors[i].Velocity -= (2.0f * vn) * result.Normal;
+                            actors[i].Position += result.Normal * result.Distance;
+                        }
+                    }
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Move the given actor by the given movement, colliding and adjusting
+        /// as necessary.
+        /// </summary>
+        /// <param name="actor">The actor who is moving.</param>
+        /// <param name="movement">The desired movement vector for this update.</param>
+        /// <returns>The movement vector after considering all collisions.</returns>
+        private Vector2 MoveAndCollide(Actor actor, Vector2 movement)
+        {
+            if (actor == null)
+            {
+                throw new ArgumentNullException("actor");
+            }
+            // make sure we care about where this actor goes
+            if (actor.Dead || (actor.Collidable == false))
+            {
+                return movement;
+            }
+            // make sure the movement is significant
+            if (movement.LengthSquared() <= 0f)
+            {
+                return movement;
+            }
+
+            // generate the list of collisions
+            Collide(actor, movement);
+
+            // determine if we had any collisions
+            if (collisionResults.Count > 0)
+            {
+                collisionResults.Sort(CollisionResult.Compare);
+                foreach (CollisionResult collision in collisionResults)
+                {
+                    // let the two actors touch each other, and see what happens
+                    if (actor.Touch(collision.Actor) && collision.Actor.Touch(actor))
+                    {
+                        actor.CollidedThisFrame = 
+                            collision.Actor.CollidedThisFrame = true;
+                        // they should react to the other, even if they just died
+                        AdjustVelocities(actor, collision.Actor);
+                        return Vector2.Zero;
+                    }
+                }
+            }
+
+            return movement;
+        }
+
+
+        /// <summary>
+        /// Determine all collisions that will happen as the given actor moves.
+        /// </summary>
+        /// <param name="actor">The actor that is moving.</param>
+        /// <param name="movement">The actor's movement vector this update.</param>
+        /// <remarks>The results are stored in the cached list.</remarks>
+        public void Collide(Actor actor, Vector2 movement)
+        {
+            collisionResults.Clear();
+
+            if (actor == null)
+            {
+                throw new ArgumentNullException("actor");
+            }
+            if (actor.Dead || (actor.Collidable == false))
+            {
+                return;
+            }
+
+            // determine the movement direction and scalar
+            float movementLength = movement.Length();
+            if (movementLength <= 0f)
+            {
+                return;
+            }
+
+            // check each actor
+            foreach (Actor checkActor in actors)
+            {
+                if ((actor == checkActor) || checkActor.Dead || !checkActor.Collidable)
+                {
+                    continue;
+                }
+
+                // calculate the target vector
+                Vector2 checkVector = checkActor.Position - actor.Position;
+                float distanceBetween = checkVector.Length() - 
+                    (checkActor.Radius + actor.Radius);
+
+                // check if they could possibly touch no matter the direction
+                if (movementLength < distanceBetween)
+                {
+                    continue;
+                }
+
+                // determine how much of the movement is bringing the two together
+                float movementTowards = Vector2.Dot(movement, checkVector);
+
+                // check to see if the movement is away from each other
+                if (movementTowards < 0f)
+                {
+                    continue;
+                }
+
+                if (movementTowards < distanceBetween)
+                {
+                    continue;
+                }
+
+                CollisionResult result = new CollisionResult();
+                result.Distance = distanceBetween;
+                result.Normal = Vector2.Normalize(checkVector);
+                result.Actor = checkActor;
+
+                collisionResults.Add(result);
+            }
+        }
+
+
+        /// <summary>
+        /// Adjust the velocities of the two actors as if they have collided,
+        /// distributing their velocities according to their masses.
+        /// </summary>
+        /// <param name="actor1">The first actor.</param>
+        /// <param name="actor2">The second actor.</param>
+        private static void AdjustVelocities(Actor actor1, Actor actor2)
+        {
+            // don't adjust velocities if at least one has negative mass
+            if ((actor1.Mass <= 0f) || (actor2.Mass <= 0f))
+            {
+                return;
+            }
+
+            // determine the vectors normal and tangent to the collision
+            Vector2 collisionNormal = Vector2.Normalize(
+                actor2.Position - actor1.Position);
+            Vector2 collisionTangent = new Vector2(
+                -collisionNormal.Y, collisionNormal.X);
+
+            // determine the velocity components along the normal and tangent vectors
+            float velocityNormal1 = Vector2.Dot(actor1.Velocity, collisionNormal);
+            float velocityTangent1 = Vector2.Dot(actor1.Velocity, collisionTangent);
+            float velocityNormal2 = Vector2.Dot(actor2.Velocity, collisionNormal);
+            float velocityTangent2 = Vector2.Dot(actor2.Velocity, collisionTangent);
+
+            // determine the new velocities along the normal
+            float velocityNormal1New = ((velocityNormal1 * (actor1.Mass - actor2.Mass))
+                + (2f * actor2.Mass * velocityNormal2)) / (actor1.Mass + actor2.Mass);
+            float velocityNormal2New = ((velocityNormal2 * (actor2.Mass - actor1.Mass))
+                + (2f * actor1.Mass * velocityNormal1)) / (actor1.Mass + actor2.Mass);
+
+            // determine the new total velocities
+            actor1.Velocity = (velocityNormal1New * collisionNormal) + 
+                (velocityTangent1 * collisionTangent);
+            actor2.Velocity = (velocityNormal2New * collisionNormal) + 
+                (velocityTangent2 * collisionTangent);
+        }
+        
+        
+        /// <summary>
+        /// Find a valid point for the actor to spawn.
+        /// </summary>
+        /// <param name="actor">The actor to find a location for.</param>
+        /// <remarks>This query is not bounded, which would be needed in a more complex
+        /// game with a likelihood of no valid spawn locations.</remarks>
+        /// <returns>A valid location for the user to spawn.</returns>
+        public Vector2 FindSpawnPoint(Actor actor)
+        {
+            if (actor == null)
+            {
+                throw new ArgumentNullException("actor");
+            }
+
+            Vector2 spawnPoint;
+            float radius = actor.Radius;
+
+            // fudge the radius slightly so we're not right on top of another actor
+            if (actor is Ship)
+            {
+                radius *= 2f;
+            }
+            else
+            {
+                radius *= 1.1f;
+            }
+            radius = (float)Math.Ceiling(radius);
+
+            Vector2 spawnMinimum = new Vector2(
+                safeDimensions.X + radius, 
+                safeDimensions.Y + radius);
+            Vector2 spawnDimensions = new Vector2(
+                (float)Math.Floor(safeDimensions.Width - 2f * radius),
+                (float)Math.Floor(safeDimensions.Height - 2f * radius));
+            Vector2 spawnMaximum = spawnMinimum + spawnDimensions;
+
+            Collision.CircleLineCollisionResult result = 
+                new Collision.CircleLineCollisionResult();
+            bool valid = true;
+            while (true)
+            {
+                valid = true;
+                // generate a new spawn point
+                spawnPoint = new Vector2(
+                    spawnMinimum.X + spawnDimensions.X * (float)random.NextDouble(),
+                    spawnMinimum.Y + spawnDimensions.Y * (float)random.NextDouble());
+                if ((spawnPoint.X < spawnMinimum.X) ||
+                    (spawnPoint.Y < spawnMinimum.Y) ||
+                    (spawnPoint.X > spawnMaximum.X) ||
+                    (spawnPoint.Y > spawnMaximum.Y))
+                {
+                    continue;
+                }
+                // if we don't collide, then one is good enough
+                if (actor.Collidable == false)
+                {
+                    break; 
+                }
+                // check against the walls
+                if (valid == true)
+                {
+                    for (int wall = 0; wall < walls.Length / 2; wall++)
+                    {
+                        if (Collision.CircleLineCollide(spawnPoint, radius, 
+                            walls[wall * 2], walls[wall * 2 + 1], ref result))
+                        {
+                            valid = false;
+                            break;
+                        }
+                    }
+                }
+                // check against all other actors
+                if (valid == true)
+                {
+                    foreach (Actor checkActor in actors)
+                    {
+                        if ((actor == checkActor) || checkActor.Dead)
+                        {
+                            continue;
+                        }
+                        if (Collision.CircleCircleIntersect(checkActor.Position,
+                            checkActor.Radius, spawnPoint, radius))
+                        {
+                            valid = false;
+                            break;
+                        }
+                    }
+                }
+                // if we have gotten this far, then the spawn point is good
+                if (valid == true)
+                {
+                    break;
+                }
+            }
+
+            return spawnPoint;
+        }
+        #endregion
+    }
+}

+ 34 - 0
StarterKits/MacOS/VectorRumble/Simulation/WorldActor.cs

@@ -0,0 +1,34 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// WorldActor.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+#endregion
+
+namespace VectorRumble
+{
+    /// <summary>
+    /// An actor that stands in for the world, in wall collisions, etc.
+    /// </summary>
+    class WorldActor : Actor
+    {
+        #region Initialization
+        /// <summary>
+        /// Construct a new world actor.
+        /// </summary>
+        /// <param name="world">The world that this world-actor belongs to.</param>
+        public WorldActor(World world)
+            : base(world)
+        {
+            radius = 0f;
+            mass = 100f;
+        }
+        #endregion
+    }
+}

+ 43 - 0
StarterKits/MacOS/VectorRumble/Simulation/WorldRules.cs

@@ -0,0 +1,43 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// WorldRules.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+#endregion
+
+namespace VectorRumble
+{
+    public enum AsteroidDensity
+    {
+        None = 0,
+        Low = 1,
+        Medium = 2,
+        High = 3
+    }
+
+    public enum WallStyle
+    {
+        None = 0,
+        One = 1,
+        Two = 2,
+        Three = 3
+    }
+
+    /// <summary>
+    /// Adjustable game settings.
+    /// </summary>
+    public static class WorldRules
+    {
+        public static int ScoreLimit = 10;
+        public static AsteroidDensity AsteroidDensity = AsteroidDensity.Low;
+        public static WallStyle WallStyle = WallStyle.Three;
+        public static bool MotionBlur = true;
+        public static bool NeonEffect = true;
+    }
+}

+ 128 - 0
StarterKits/MacOS/VectorRumble/VectorRumble.csproj

@@ -0,0 +1,128 @@
+<?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>{CB7B2B69-7640-4225-89F3-CE3BF428F2F3}</ProjectGuid>
+    <ProjectTypeGuids>{948B3504-5B70-4649-8FE4-BDE1FB46EC69};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>VectorRumble</RootNamespace>
+    <AssemblyName>VectorRumble</AssemblyName>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;MONOMAC</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" />
+    <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 Include="VectorRumble.png" />
+    <None Include="Content\Effects\BloomCombine.fx" />
+    <None Include="Content\Effects\BloomExtract.fx" />
+    <None Include="Content\Effects\GaussianBlur.fx" />
+    <None Include="Content\Fonts\retroMedium.spritefont" />
+    <None Include="Content\Fonts\retroSmall.spritefont" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\Mono\MonoMac\v0.0\Mono.MonoMac.targets" />
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="AudioManager.cs" />
+    <Compile Include="CollectCollection.cs" />
+    <Compile Include="Game.cs" />
+    <Compile Include="BloomPostprocess\BloomComponent.cs" />
+    <Compile Include="BloomPostprocess\BloomSettings.cs" />
+    <Compile Include="Gameplay\Asteroid.cs" />
+    <Compile Include="Gameplay\DoubleLaserPowerUp.cs" />
+    <Compile Include="Gameplay\DoubleLaserWeapon.cs" />
+    <Compile Include="Gameplay\LaserProjectile.cs" />
+    <Compile Include="Gameplay\LaserWeapon.cs" />
+    <Compile Include="Gameplay\MineProjectile.cs" />
+    <Compile Include="Gameplay\MineWeapon.cs" />
+    <Compile Include="Gameplay\RocketPowerUp.cs" />
+    <Compile Include="Gameplay\RocketProjectile.cs" />
+    <Compile Include="Gameplay\RocketWeapon.cs" />
+    <Compile Include="Gameplay\Ship.cs" />
+    <Compile Include="Gameplay\TripleLaserPowerUp.cs" />
+    <Compile Include="Gameplay\TripleLaserWeapon.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Rendering\LineBatch.cs" />
+    <Compile Include="Rendering\Particle.cs" />
+    <Compile Include="Rendering\ParticleSystem.cs" />
+    <Compile Include="Rendering\Starfield.cs" />
+    <Compile Include="Rendering\VectorPolygon.cs" />
+    <Compile Include="ScreenManager\GameScreen.cs" />
+    <Compile Include="ScreenManager\InputState.cs" />
+    <Compile Include="ScreenManager\ScreenManager.cs" />
+    <Compile Include="Screens\BackgroundScreen.cs" />
+    <Compile Include="Screens\GameOverScreen.cs" />
+    <Compile Include="Screens\GameplayScreen.cs" />
+    <Compile Include="Screens\LoadingScreen.cs" />
+    <Compile Include="Screens\MainMenuScreen.cs" />
+    <Compile Include="Screens\MenuEntry.cs" />
+    <Compile Include="Screens\MenuScreen.cs" />
+    <Compile Include="Screens\MessageBoxScreen.cs" />
+    <Compile Include="Screens\OptionsMenuScreen.cs" />
+    <Compile Include="Screens\PauseMenuScreen.cs" />
+    <Compile Include="Simulation\Actor.cs" />
+    <Compile Include="Simulation\Collision.cs" />
+    <Compile Include="Simulation\CollisionResult.cs" />
+    <Compile Include="Simulation\PowerUp.cs" />
+    <Compile Include="Simulation\Projectile.cs" />
+    <Compile Include="Simulation\Weapon.cs" />
+    <Compile Include="Simulation\World.cs" />
+    <Compile Include="Simulation\WorldActor.cs" />
+    <Compile Include="Simulation\WorldRules.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Content\Audio\Waves\damage1.wav" />
+    <Content Include="Content\Audio\Waves\engine_3.wav" />
+    <Content Include="Content\Audio\Waves\explosion1.wav" />
+    <Content Include="Content\Audio\Waves\explosion2.wav" />
+    <Content Include="Content\Audio\Waves\explosion3.wav" />
+    <Content Include="Content\Audio\Waves\hax2_fire.wav" />
+    <Content Include="Content\Audio\Waves\hax2_fire2.wav" />
+    <Content Include="Content\Audio\Waves\menu_back.wav" />
+    <Content Include="Content\Audio\Waves\menu_scroll.wav" />
+    <Content Include="Content\Audio\Waves\menu_select3.wav" />
+    <Content Include="Content\Audio\Waves\phase_expire_alt.wav" />
+    <Content Include="Content\Audio\Waves\RacingGame Game Music 1.wav" />
+    <Content Include="Content\Audio\Waves\tx0_fire1.wav" />
+    <Content Include="Content\Audio\Waves\tx0_fire2.wav" />
+    <Content Include="Content\Audio\Waves\tx0_fire3.wav" />
+    <Content Include="Content\Audio\Waves\weapon_pickup_alt_retro.wav" />
+    <Content Include="Content\Textures\blank.png" />
+    <Content Include="Content\Textures\title.png" />
+    <Content Include="Content\Fonts\retroMedium.xnb" />
+    <Content Include="Content\Fonts\retroSmall.xnb" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\..\MonoGame\MonoGame.Framework\MonoGame.Framework.MacOS.csproj">
+      <Project>{36C538E6-C32A-4A8D-A39C-566173D7118E}</Project>
+      <Name>MonoGame.Framework.MacOS</Name>
+    </ProjectReference>
+  </ItemGroup>
+</Project>

BIN
StarterKits/MacOS/VectorRumble/VectorRumble.png


+ 8 - 0
StarterKits/MonoGame.StarterKits.MacOS.sln

@@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlackJack.MacOS", "MacOS\Ca
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Platformer", "MacOS\Platformer\Platformer.csproj", "{1D099BD4-805E-4C04-B33D-9F2D16D42B86}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VectorRumble", "MacOS\VectorRumble\VectorRumble.csproj", "{CB7B2B69-7640-4225-89F3-CE3BF428F2F3}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -50,6 +52,12 @@ Global
 		{B35097FE-CBF0-4CAD-8B93-36C9587057DE}.Distribution|Any CPU.Build.0 = Debug|Any CPU
 		{B35097FE-CBF0-4CAD-8B93-36C9587057DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{B35097FE-CBF0-4CAD-8B93-36C9587057DE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{CB7B2B69-7640-4225-89F3-CE3BF428F2F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{CB7B2B69-7640-4225-89F3-CE3BF428F2F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{CB7B2B69-7640-4225-89F3-CE3BF428F2F3}.Distribution|Any CPU.ActiveCfg = Debug|Any CPU
+		{CB7B2B69-7640-4225-89F3-CE3BF428F2F3}.Distribution|Any CPU.Build.0 = Debug|Any CPU
+		{CB7B2B69-7640-4225-89F3-CE3BF428F2F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{CB7B2B69-7640-4225-89F3-CE3BF428F2F3}.Release|Any CPU.Build.0 = Release|Any CPU
 		{F4D3F373-0259-43BD-9EC3-6DDAD2AE2C1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{F4D3F373-0259-43BD-9EC3-6DDAD2AE2C1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{F4D3F373-0259-43BD-9EC3-6DDAD2AE2C1B}.Distribution|Any CPU.ActiveCfg = Debug|Any CPU

+ 8 - 0
StarterKits/iOS/MemoryMadness/Info.plist

@@ -0,0 +1,8 @@
+<?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>MinimumOSVersion</key>
+	<string>3.1</string>
+</dict>
+</plist>

+ 15 - 0
StarterKits/iOS/VectorRumble/Info.plist

@@ -0,0 +1,15 @@
+<?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>UIDeviceFamily</key>
+	<array>
+		<string>2</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>

+ 44 - 0
StarterKits/iOS/VectorRumble/Program.cs

@@ -0,0 +1,44 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Program.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+
+#if IOS
+using MonoTouch.Foundation;
+using MonoTouch.UIKit;
+#endif
+
+
+#endregion
+
+namespace VectorRumble
+{
+	[Register ("AppDelegate")]
+	class Program : UIApplicationDelegate 
+	{
+		public override void FinishedLaunching (UIApplication app)
+		{
+			// Fun begins..
+			using (VectorRumbleGame game = new VectorRumbleGame())
+            {
+                game.Run();
+            }
+			
+			//MediaLibrary lib = new MediaLibrary();
+			//object result = lib.Playlists;
+		}
+
+		static void Main (string [] args)
+		{
+			UIApplication.Main (args,null,"AppDelegate");
+		}
+	}
+}
+

+ 296 - 0
StarterKits/iOS/VectorRumble/VectorRumble.iOS.csproj

@@ -0,0 +1,296 @@
+<?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>10.0.0</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{1112251F-412F-443D-AA19-BDB5FA6B3CBA}</ProjectGuid>
+    <ProjectTypeGuids>{6BC8ED88-2882-458C-8E55-DFD12B67127B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>VectorRumble.iOS</RootNamespace>
+    <AssemblyName>VectorRumbleiOS</AssemblyName>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
+    <DefineConstants>DEBUG;IOS</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <MtouchLink>None</MtouchLink>
+    <MtouchDebug>true</MtouchDebug>
+    <MtouchI18n />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
+    <DebugType>none</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\iPhoneSimulator\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <MtouchLink>None</MtouchLink>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\iPhone\Debug</OutputPath>
+    <DefineConstants>DEBUG;</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <MtouchDebug>true</MtouchDebug>
+    <CodesignKey>iPhone Developer</CodesignKey>
+    <IpaPackageName />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
+    <DebugType>none</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\iPhone\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <CodesignKey>iPhone Developer</CodesignKey>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Core" />
+    <Reference Include="monotouch" />
+    <Reference Include="OpenTK" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Info.plist" />
+    <None Include="..\..\MacOS\VectorRumble\Content\Effects\BloomCombine.fx">
+      <Link>Content\Effects\BloomCombine.fx</Link>
+    </None>
+    <None Include="..\..\MacOS\VectorRumble\Content\Effects\BloomExtract.fx">
+      <Link>Content\Effects\BloomExtract.fx</Link>
+    </None>
+    <None Include="..\..\MacOS\VectorRumble\Content\Effects\GaussianBlur.fx">
+      <Link>Content\Effects\GaussianBlur.fx</Link>
+    </None>
+    <None Include="..\..\MacOS\VectorRumble\Content\Fonts\retroMedium.spritefont">
+      <Link>Content\Fonts\retroMedium.spritefont</Link>
+    </None>
+    <None Include="..\..\MacOS\VectorRumble\Content\Fonts\retroSmall.spritefont">
+      <Link>Content\Fonts\retroSmall.spritefont</Link>
+    </None>
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="..\..\MacOS\VectorRumble\AudioManager.cs">
+      <Link>AudioManager.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\CollectCollection.cs">
+      <Link>CollectCollection.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Game.cs">
+      <Link>Game.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\BloomPostprocess\BloomComponent.cs">
+      <Link>BloomPostprocess\BloomComponent.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\BloomPostprocess\BloomSettings.cs">
+      <Link>BloomPostprocess\BloomSettings.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\Asteroid.cs">
+      <Link>Gameplay\Asteroid.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\DoubleLaserPowerUp.cs">
+      <Link>Gameplay\DoubleLaserPowerUp.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\DoubleLaserWeapon.cs">
+      <Link>Gameplay\DoubleLaserWeapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\LaserProjectile.cs">
+      <Link>Gameplay\LaserProjectile.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\LaserWeapon.cs">
+      <Link>Gameplay\LaserWeapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\MineProjectile.cs">
+      <Link>Gameplay\MineProjectile.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\MineWeapon.cs">
+      <Link>Gameplay\MineWeapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\RocketPowerUp.cs">
+      <Link>Gameplay\RocketPowerUp.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\RocketProjectile.cs">
+      <Link>Gameplay\RocketProjectile.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\RocketWeapon.cs">
+      <Link>Gameplay\RocketWeapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\Ship.cs">
+      <Link>Gameplay\Ship.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\TripleLaserPowerUp.cs">
+      <Link>Gameplay\TripleLaserPowerUp.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Gameplay\TripleLaserWeapon.cs">
+      <Link>Gameplay\TripleLaserWeapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Properties\AssemblyInfo.cs">
+      <Link>Properties\AssemblyInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Rendering\LineBatch.cs">
+      <Link>Rendering\LineBatch.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Rendering\Particle.cs">
+      <Link>Rendering\Particle.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Rendering\ParticleSystem.cs">
+      <Link>Rendering\ParticleSystem.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Rendering\Starfield.cs">
+      <Link>Rendering\Starfield.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Rendering\VectorPolygon.cs">
+      <Link>Rendering\VectorPolygon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\ScreenManager\GameScreen.cs">
+      <Link>ScreenManager\GameScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\ScreenManager\InputState.cs">
+      <Link>ScreenManager\InputState.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\ScreenManager\ScreenManager.cs">
+      <Link>ScreenManager\ScreenManager.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\BackgroundScreen.cs">
+      <Link>Screens\BackgroundScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\GameOverScreen.cs">
+      <Link>Screens\GameOverScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\GameplayScreen.cs">
+      <Link>Screens\GameplayScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\LoadingScreen.cs">
+      <Link>Screens\LoadingScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\MainMenuScreen.cs">
+      <Link>Screens\MainMenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\MenuEntry.cs">
+      <Link>Screens\MenuEntry.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\MenuScreen.cs">
+      <Link>Screens\MenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\MessageBoxScreen.cs">
+      <Link>Screens\MessageBoxScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\OptionsMenuScreen.cs">
+      <Link>Screens\OptionsMenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Screens\PauseMenuScreen.cs">
+      <Link>Screens\PauseMenuScreen.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\Actor.cs">
+      <Link>Simulation\Actor.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\Collision.cs">
+      <Link>Simulation\Collision.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\CollisionResult.cs">
+      <Link>Simulation\CollisionResult.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\PowerUp.cs">
+      <Link>Simulation\PowerUp.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\Projectile.cs">
+      <Link>Simulation\Projectile.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\Weapon.cs">
+      <Link>Simulation\Weapon.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\World.cs">
+      <Link>Simulation\World.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\WorldActor.cs">
+      <Link>Simulation\WorldActor.cs</Link>
+    </Compile>
+    <Compile Include="..\..\MacOS\VectorRumble\Simulation\WorldRules.cs">
+      <Link>Simulation\WorldRules.cs</Link>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\..\MonoGame\MonoGame.Framework\MonoGame.Framework.iOS.csproj">
+      <Project>{DB8508BB-9849-4CC2-BC0F-8EB5DACB3C47}</Project>
+      <Name>MonoGame.Framework.iOS</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="..\..\MacOS\VectorRumble\VectorRumble.png">
+      <Link>VectorRumble.png</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Textures\blank.png">
+      <Link>Content\Textures\blank.png</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Textures\title.png">
+      <Link>Content\Textures\title.png</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\damage1.wav">
+      <Link>Content\Audio\Waves\damage1.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\engine_3.wav">
+      <Link>Content\Audio\Waves\engine_3.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\explosion1.wav">
+      <Link>Content\Audio\Waves\explosion1.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\explosion2.wav">
+      <Link>Content\Audio\Waves\explosion2.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\explosion3.wav">
+      <Link>Content\Audio\Waves\explosion3.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\hax2_fire.wav">
+      <Link>Content\Audio\Waves\hax2_fire.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\hax2_fire2.wav">
+      <Link>Content\Audio\Waves\hax2_fire2.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\menu_back.wav">
+      <Link>Content\Audio\Waves\menu_back.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\menu_scroll.wav">
+      <Link>Content\Audio\Waves\menu_scroll.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\menu_select3.wav">
+      <Link>Content\Audio\Waves\menu_select3.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\phase_expire_alt.wav">
+      <Link>Content\Audio\Waves\phase_expire_alt.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\RacingGame Game Music 1.wav">
+      <Link>Content\Audio\Waves\RacingGame Game Music 1.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\tx0_fire1.wav">
+      <Link>Content\Audio\Waves\tx0_fire1.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\tx0_fire2.wav">
+      <Link>Content\Audio\Waves\tx0_fire2.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\tx0_fire3.wav">
+      <Link>Content\Audio\Waves\tx0_fire3.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Audio\Waves\weapon_pickup_alt_retro.wav">
+      <Link>Content\Audio\Waves\weapon_pickup_alt_retro.wav</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Fonts\retroMedium.xnb">
+      <Link>Content\Fonts\retroMedium.xnb</Link>
+    </Content>
+    <Content Include="..\..\MacOS\VectorRumble\Content\Fonts\retroSmall.xnb">
+      <Link>Content\Fonts\retroSmall.xnb</Link>
+    </Content>
+  </ItemGroup>
+</Project>