Browse Source

Added the Platformer starter kit for android.

Clancey 14 years ago
parent
commit
5e82d89982
92 changed files with 2892 additions and 0 deletions
  1. 181 0
      StarterKits/Android/Platformer/Accelerometer.cs
  2. 18 0
      StarterKits/Android/Platformer/Activity1.cs
  3. 88 0
      StarterKits/Android/Platformer/Animation.cs
  4. 99 0
      StarterKits/Android/Platformer/AnimationPlayer.cs
  5. 19 0
      StarterKits/Android/Platformer/Assets/AboutAssets.txt
  6. BIN
      StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer0_0.png
  7. BIN
      StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer0_1.png
  8. BIN
      StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer0_2.png
  9. BIN
      StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer1_0.png
  10. BIN
      StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer1_1.png
  11. BIN
      StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer1_2.png
  12. BIN
      StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer2_0.png
  13. BIN
      StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer2_1.png
  14. BIN
      StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer2_2.png
  15. 315 0
      StarterKits/Android/Platformer/Assets/Content/Content.contentproj
  16. BIN
      StarterKits/Android/Platformer/Assets/Content/Fonts/Hud.xnb
  17. 15 0
      StarterKits/Android/Platformer/Assets/Content/Levels/0.txt
  18. 15 0
      StarterKits/Android/Platformer/Assets/Content/Levels/1.txt
  19. 15 0
      StarterKits/Android/Platformer/Assets/Content/Levels/2.txt
  20. BIN
      StarterKits/Android/Platformer/Assets/Content/Overlays/you_died.png
  21. BIN
      StarterKits/Android/Platformer/Assets/Content/Overlays/you_lose.png
  22. BIN
      StarterKits/Android/Platformer/Assets/Content/Overlays/you_win.png
  23. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/ExitReached.mp3
  24. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/ExitReached.wav
  25. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/ExitReached.wma
  26. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/GemCollected.mp3
  27. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/GemCollected.wav
  28. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/GemCollected.wma
  29. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/MonsterKilled.mp3
  30. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/MonsterKilled.wav
  31. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/MonsterKilled.wma
  32. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/Music.mp3
  33. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/Music.wav
  34. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/Music.wma
  35. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerFall.mp3
  36. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerFall.wav
  37. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerFall.wma
  38. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerJump.mp3
  39. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerJump.wav
  40. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerJump.wma
  41. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerKilled.mp3
  42. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerKilled.wav
  43. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerKilled.wma
  44. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/PowerUp.mp3
  45. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/PowerUp.wav
  46. BIN
      StarterKits/Android/Platformer/Assets/Content/Sounds/Powerup.wma
  47. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/Gem.png
  48. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterA/Idle.png
  49. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterA/Run.png
  50. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterB/Idle.png
  51. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterB/Run.png
  52. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterC/Idle.png
  53. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterC/Run.png
  54. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterD/Idle.png
  55. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterD/Run.png
  56. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/Player/Celebrate.png
  57. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/Player/Die.png
  58. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/Player/Idle.png
  59. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/Player/Jump.png
  60. BIN
      StarterKits/Android/Platformer/Assets/Content/Sprites/Player/Run.png
  61. BIN
      StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA0.png
  62. BIN
      StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA1.png
  63. BIN
      StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA2.png
  64. BIN
      StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA3.png
  65. BIN
      StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA4.png
  66. BIN
      StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA5.png
  67. BIN
      StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA6.png
  68. BIN
      StarterKits/Android/Platformer/Assets/Content/Tiles/BlockB0.png
  69. BIN
      StarterKits/Android/Platformer/Assets/Content/Tiles/BlockB1.png
  70. BIN
      StarterKits/Android/Platformer/Assets/Content/Tiles/Exit.png
  71. BIN
      StarterKits/Android/Platformer/Assets/Content/Tiles/Platform.png
  72. BIN
      StarterKits/Android/Platformer/Background.png
  73. 54 0
      StarterKits/Android/Platformer/Circle.cs
  74. 179 0
      StarterKits/Android/Platformer/Enemy.cs
  75. BIN
      StarterKits/Android/Platformer/Game.ico
  76. BIN
      StarterKits/Android/Platformer/GameThumbnail.png
  77. 118 0
      StarterKits/Android/Platformer/Gem.cs
  78. 553 0
      StarterKits/Android/Platformer/Level.cs
  79. 142 0
      StarterKits/Android/Platformer/Platformer.csproj
  80. 264 0
      StarterKits/Android/Platformer/PlatformerGame.cs
  81. 466 0
      StarterKits/Android/Platformer/Player.cs
  82. 6 0
      StarterKits/Android/Platformer/Properties/AppManifest.xml
  83. 34 0
      StarterKits/Android/Platformer/Properties/AssemblyInfo.cs
  84. 32 0
      StarterKits/Android/Platformer/Properties/WMAppManifest.xml
  85. 66 0
      StarterKits/Android/Platformer/RectangleExtensions.cs
  86. 44 0
      StarterKits/Android/Platformer/Resources/AboutResources.txt
  87. 59 0
      StarterKits/Android/Platformer/Resources/Resource.designer.cs
  88. BIN
      StarterKits/Android/Platformer/Resources/drawable/Icon.png
  89. 8 0
      StarterKits/Android/Platformer/Resources/layout/Main.axml
  90. 4 0
      StarterKits/Android/Platformer/Resources/values/Strings.xml
  91. 62 0
      StarterKits/Android/Platformer/Tile.cs
  92. 36 0
      StarterKits/Android/Platformer/TouchCollectionExtensions.cs

+ 181 - 0
StarterKits/Android/Platformer/Accelerometer.cs

@@ -0,0 +1,181 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Accelerometer.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.Input;
+#endregion
+
+namespace Platformer
+{
+    /// <summary>
+    /// A static encapsulation of accelerometer input to provide games with a polling-based
+    /// accelerometer system.
+    /// </summary>
+    public static class Accelerometer
+    {
+#if WINDOWS_PHONE
+        // the accelerometer sensor on the device
+        private static Microsoft.Devices.Sensors.Accelerometer accelerometer = new Microsoft.Devices.Sensors.Accelerometer();
+        
+        // we need an object for locking because the ReadingChanged event is fired
+        // on a different thread than our game
+        private static object threadLock = new object();
+
+        // we use this to keep the last known value from the accelerometer callback
+        private static Vector3 nextValue = new Vector3();
+#endif
+
+        // we want to prevent the Accelerometer from being initialized twice.
+        private static bool isInitialized = false;
+
+        // whether or not the accelerometer is active
+        private static bool isActive = false;
+
+        /// <summary>
+        /// Initializes the Accelerometer for the current game. This method can only be called once per game.
+        /// </summary>
+        public static void Initialize()
+        {
+            // make sure we don't initialize the Accelerometer twice
+            if (isInitialized)
+            {
+				return;
+                //throw new InvalidOperationException("Initialize can only be called once");
+            }
+
+#if WINDOWS_PHONE
+            // try to start the sensor only on devices, catching the exception if it fails            
+            if (Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Device)            
+            {
+                try
+                {
+                    accelerometer.ReadingChanged += new EventHandler<Microsoft.Devices.Sensors.AccelerometerReadingEventArgs>(sensor_ReadingChanged);
+                    accelerometer.Start();
+                    isActive = true;
+                }
+                catch (Microsoft.Devices.Sensors.AccelerometerFailedException)
+                {
+                    isActive = false;
+                }
+            }
+            else
+            {
+                // we always return isActive on emulator because we use the arrow
+                // keys for simulation which is always available.
+                isActive = true;
+            }
+#endif
+
+            // remember that we are initialized
+            isInitialized = true;
+        }
+        
+#if WINDOWS_PHONE
+        private static void sensor_ReadingChanged(object sender, Microsoft.Devices.Sensors.AccelerometerReadingEventArgs e)
+        {
+            // store the accelerometer value in our variable to be used on the next Update
+            lock (threadLock)
+            {
+                nextValue = new Vector3((float)e.X, (float)e.Y, (float)e.Z);
+            }
+        }
+#endif
+
+        /// <summary>
+        /// Gets the current state of the accelerometer.
+        /// </summary>
+        /// <returns>A new AccelerometerState with the current state of the accelerometer.</returns>
+        public static AccelerometerState GetState()
+        {
+            // make sure we've initialized the Accelerometer before we try to get the state
+            if (!isInitialized)
+            {
+                throw new InvalidOperationException("You must Initialize before you can call GetState");
+            }
+
+            // create a new value for our state
+            Vector3 stateValue = new Vector3();
+
+#if WINDOWS_PHONE
+            // if the accelerometer is active
+            if (isActive)
+            {
+                if (Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Device)
+                {
+                    // if we're on device, we'll just grab our latest reading from the accelerometer
+                    lock (threadLock)
+                    {
+                        stateValue = nextValue;
+                    }
+                }
+                else
+                {
+                    // if we're in the emulator, we'll generate a fake acceleration value using the arrow keys
+                    // press the pause/break key to toggle keyboard input for the emulator
+                    KeyboardState keyboardState = Keyboard.GetState();
+
+                    stateValue.Z = -1;
+
+                    if (keyboardState.IsKeyDown(Keys.Left))
+                        stateValue.X--;
+                    if (keyboardState.IsKeyDown(Keys.Right))
+                        stateValue.X++;
+                    if (keyboardState.IsKeyDown(Keys.Up))
+                        stateValue.Y++;
+                    if (keyboardState.IsKeyDown(Keys.Down))
+                        stateValue.Y--;
+
+                    stateValue.Normalize();
+                }
+            }
+#endif
+
+            return new AccelerometerState(stateValue, isActive);
+        }
+    }
+
+    /// <summary>
+    /// An encapsulation of the accelerometer's current state.
+    /// </summary>
+    public struct AccelerometerState
+    {
+        /// <summary>
+        /// Gets the accelerometer's current value in G-force.
+        /// </summary>
+        public Vector3 Acceleration { get; private set; }
+
+        /// <summary>
+        /// Gets whether or not the accelerometer is active and running.
+        /// </summary>
+        public bool IsActive { get; private set; }
+
+        /// <summary>
+        /// Initializes a new AccelerometerState.
+        /// </summary>
+        /// <param name="acceleration">The current acceleration (in G-force) of the accelerometer.</param>
+        /// <param name="isActive">Whether or not the accelerometer is active.</param>
+        public AccelerometerState(Vector3 acceleration, bool isActive)
+            : this()
+        {
+            Acceleration = acceleration;
+            IsActive = isActive;
+        }
+
+        /// <summary>
+        /// Returns a string containing the values of the Acceleration and IsActive properties.
+        /// </summary>
+        /// <returns>A new string describing the state.</returns>
+        public override string ToString()
+        {
+            return string.Format("Acceleration: {0}, IsActive: {1}", Acceleration, IsActive);
+        }
+    }
+}

+ 18 - 0
StarterKits/Android/Platformer/Activity1.cs

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

+ 88 - 0
StarterKits/Android/Platformer/Animation.cs

@@ -0,0 +1,88 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Animation.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Platformer
+{
+    /// <summary>
+    /// Represents an animated texture.
+    /// </summary>
+    /// <remarks>
+    /// Currently, this class assumes that each frame of animation is
+    /// as wide as each animation is tall. The number of frames in the
+    /// animation are inferred from this.
+    /// </remarks>
+    class Animation
+    {
+        /// <summary>
+        /// All frames in the animation arranged horizontally.
+        /// </summary>
+        public Texture2D Texture
+        {
+            get { return texture; }
+        }
+        Texture2D texture;
+
+        /// <summary>
+        /// Duration of time to show each frame.
+        /// </summary>
+        public float FrameTime
+        {
+            get { return frameTime; }
+        }
+        float frameTime;
+
+        /// <summary>
+        /// When the end of the animation is reached, should it
+        /// continue playing from the beginning?
+        /// </summary>
+        public bool IsLooping
+        {
+            get { return isLooping; }
+        }
+        bool isLooping;
+
+        /// <summary>
+        /// Gets the number of frames in the animation.
+        /// </summary>
+        public int FrameCount
+        {
+            get { return Texture.Width / FrameWidth; }
+        }
+
+        /// <summary>
+        /// Gets the width of a frame in the animation.
+        /// </summary>
+        public int FrameWidth
+        {
+            // Assume square frames.
+            get { return Texture.Height; }
+        }
+
+        /// <summary>
+        /// Gets the height of a frame in the animation.
+        /// </summary>
+        public int FrameHeight
+        {
+            get { return Texture.Height; }
+        }
+
+        /// <summary>
+        /// Constructors a new animation.
+        /// </summary>        
+        public Animation(Texture2D texture, float frameTime, bool isLooping)
+        {
+            this.texture = texture;
+            this.frameTime = frameTime;
+            this.isLooping = isLooping;
+        }
+    }
+}

+ 99 - 0
StarterKits/Android/Platformer/AnimationPlayer.cs

@@ -0,0 +1,99 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// AnimationPlayer.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Platformer
+{
+    /// <summary>
+    /// Controls playback of an Animation.
+    /// </summary>
+    struct AnimationPlayer
+    {
+        /// <summary>
+        /// Gets the animation which is currently playing.
+        /// </summary>
+        public Animation Animation
+        {
+            get { return animation; }
+        }
+        Animation animation;
+
+        /// <summary>
+        /// Gets the index of the current frame in the animation.
+        /// </summary>
+        public int FrameIndex
+        {
+            get { return frameIndex; }
+        }
+        int frameIndex;
+
+        /// <summary>
+        /// The amount of time in seconds that the current frame has been shown for.
+        /// </summary>
+        private float time;
+
+        /// <summary>
+        /// Gets a texture origin at the bottom center of each frame.
+        /// </summary>
+        public Vector2 Origin
+        {
+            get { return new Vector2(Animation.FrameWidth / 2.0f, Animation.FrameHeight); }
+        }
+
+        /// <summary>
+        /// Begins or continues playback of an animation.
+        /// </summary>
+        public void PlayAnimation(Animation animation)
+        {
+            // If this animation is already running, do not restart it.
+            if (Animation == animation)
+                return;
+
+            // Start the new animation.
+            this.animation = animation;
+            this.frameIndex = 0;
+            this.time = 0.0f;
+        }
+
+        /// <summary>
+        /// Advances the time position and draws the current frame of the animation.
+        /// </summary>
+        public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Vector2 position, SpriteEffects spriteEffects)
+        {
+            if (Animation == null)
+                throw new NotSupportedException("No animation is currently playing.");
+
+            // Process passing time.
+            time += (float)gameTime.ElapsedGameTime.TotalSeconds;
+            while (time > Animation.FrameTime)
+            {
+                time -= Animation.FrameTime;
+
+                // Advance the frame index; looping or clamping as appropriate.
+                if (Animation.IsLooping)
+                {
+                    frameIndex = (frameIndex + 1) % Animation.FrameCount;
+                }
+                else
+                {
+                    frameIndex = Math.Min(frameIndex + 1, Animation.FrameCount - 1);
+                }
+            }
+
+            // Calculate the source rectangle of the current frame.
+            Rectangle source = new Rectangle(FrameIndex * Animation.Texture.Height, 0, Animation.Texture.Height, Animation.Texture.Height);
+
+            // Draw the current frame.
+            spriteBatch.Draw(Animation.Texture, position, source, Color.White, 0.0f, Origin, 1.0f, spriteEffects, 0.0f);
+        }
+    }
+}

+ 19 - 0
StarterKits/Android/Platformer/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");

BIN
StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer0_0.png


BIN
StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer0_1.png


BIN
StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer0_2.png


BIN
StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer1_0.png


BIN
StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer1_1.png


BIN
StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer1_2.png


BIN
StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer2_0.png


BIN
StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer2_1.png


BIN
StarterKits/Android/Platformer/Assets/Content/Backgrounds/Layer2_2.png


+ 315 - 0
StarterKits/Android/Platformer/Assets/Content/Content.contentproj

@@ -0,0 +1,315 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
+  <PropertyGroup>
+    <ProjectGuid>{2FDB7EF3-D3C4-47A3-8E75-C901A05B8574}</ProjectGuid>
+    <ProjectTypeGuids>{96E2B04D-8817-42c6-938A-82C39BA4D311};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
+    <OutputPath>bin\$(Platform)\$(Configuration)</OutputPath>
+    <ContentRootDirectory>Content</ContentRootDirectory>
+  </PropertyGroup>
+  <PropertyGroup>
+    <RootNamespace>PhoneContent</RootNamespace>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.EffectImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.FBXImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.TextureImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.XImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.AudioImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.VideoImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Sprites\Gem.png">
+      <Name>Gem</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\MonsterA\Idle.png">
+      <Name>Idle</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\MonsterA\Run.png">
+      <Name>Run</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\MonsterB\Idle.png">
+      <Name>Idle</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\MonsterB\Run.png">
+      <Name>Run</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\MonsterC\Idle.png">
+      <Name>Idle</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\MonsterC\Run.png">
+      <Name>Run</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\MonsterD\Idle.png">
+      <Name>Idle</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\MonsterD\Run.png">
+      <Name>Run</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\Player\Celebrate.png">
+      <Name>Celebrate</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\Player\Die.png">
+      <Name>Die</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\Player\Idle.png">
+      <Name>Idle</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\Player\Jump.png">
+      <Name>Jump</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Sprites\Player\Run.png">
+      <Name>Run</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Tiles\BlockA0.png">
+      <Name>BlockA0</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Tiles\BlockA1.png">
+      <Name>BlockA1</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Tiles\BlockA2.png">
+      <Name>BlockA2</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Tiles\BlockA3.png">
+      <Name>BlockA3</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Tiles\BlockA4.png">
+      <Name>BlockA4</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Tiles\BlockA5.png">
+      <Name>BlockA5</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Tiles\BlockA6.png">
+      <Name>BlockA6</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Tiles\BlockB0.png">
+      <Name>BlockB0</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Tiles\BlockB1.png">
+      <Name>BlockB1</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Tiles\Platform.png">
+      <Name>Platform</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Fonts\Hud.spritefont">
+      <Name>Hud</Name>
+      <Importer>FontDescriptionImporter</Importer>
+      <Processor>FontDescriptionProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Backgrounds\Layer0_0.png">
+      <Name>Layer0_0</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Backgrounds\Layer0_1.png">
+      <Name>Layer0_1</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Backgrounds\Layer0_2.png">
+      <Name>Layer0_2</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Backgrounds\Layer1_0.png">
+      <Name>Layer1_0</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Backgrounds\Layer1_1.png">
+      <Name>Layer1_1</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Backgrounds\Layer1_2.png">
+      <Name>Layer1_2</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Backgrounds\Layer2_0.png">
+      <Name>Layer2_0</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Backgrounds\Layer2_1.png">
+      <Name>Layer2_1</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Backgrounds\Layer2_2.png">
+      <Name>Layer2_2</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <None Include="Levels\0.txt">
+      <Name>0</Name>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Levels\2.txt">
+      <Name>2</Name>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <Compile Include="Overlays\you_died.png">
+      <Name>you_died</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Overlays\you_lose.png">
+      <Name>you_lose</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="Overlays\you_win.png">
+      <Name>you_win</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Levels\1.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+      <Name>1</Name>
+    </None>
+  </ItemGroup>
+  <ItemGroup />
+  <ItemGroup>
+    <Compile Include="Tiles\Exit.png">
+      <Name>Exit</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Sounds\ExitReached.wma">
+      <Name>ExitReached</Name>
+      <Importer>WmaImporter</Importer>
+      <Processor>SoundEffectProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Sounds\GemCollected.wma">
+      <Name>GemCollected</Name>
+      <Importer>WmaImporter</Importer>
+      <Processor>SoundEffectProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Sounds\MonsterKilled.wma">
+      <Name>MonsterKilled</Name>
+      <Importer>WmaImporter</Importer>
+      <Processor>SoundEffectProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Sounds\Music.wma">
+      <Name>Music</Name>
+      <Importer>WmaImporter</Importer>
+      <Processor>SongProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Sounds\PlayerFall.wma">
+      <Name>PlayerFall</Name>
+      <Importer>WmaImporter</Importer>
+      <Processor>SoundEffectProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Sounds\PlayerJump.wma">
+      <Name>PlayerJump</Name>
+      <Importer>WmaImporter</Importer>
+      <Processor>SoundEffectProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Sounds\PlayerKilled.wma">
+      <Name>PlayerKilled</Name>
+      <Importer>WmaImporter</Importer>
+      <Processor>SoundEffectProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Sounds\Powerup.wma">
+      <Name>Powerup</Name>
+      <Importer>WmaImporter</Importer>
+      <Processor>SoundEffectProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\$(XnaFrameworkVersion)\Microsoft.Xna.GameStudio.ContentPipeline.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

BIN
StarterKits/Android/Platformer/Assets/Content/Fonts/Hud.xnb


+ 15 - 0
StarterKits/Android/Platformer/Assets/Content/Levels/0.txt

@@ -0,0 +1,15 @@
+....................
+....................
+....................
+....................
+....................
+....................
+....................
+.........GGG........
+.........###........
+....................
+....GGG.......GGG...
+....###.......###...
+....................
+.1................X.
+####################

+ 15 - 0
StarterKits/Android/Platformer/Assets/Content/Levels/1.txt

@@ -0,0 +1,15 @@
+....................
+....................
+..........X.........
+.......######.......
+..G..............G..
+####..G.G.G.G....###
+.......G.G.GCG......
+......--------......
+...--...........--..
+....................
+.G.G............G.G.
+####............####
+....................
+.1..................
+####################

+ 15 - 0
StarterKits/Android/Platformer/Assets/Content/Levels/2.txt

@@ -0,0 +1,15 @@
+....................
+...G............X...
+...--..G.......--...
+....G.--........G...
+...--..........--...
+...G......G....G....
+...--....--....--...
+....G...........G...
+...--........G.--...
+...G........--.G....
+...--..........--...
+....G...........G...
+...--..........--...
+.1..................
+####################

BIN
StarterKits/Android/Platformer/Assets/Content/Overlays/you_died.png


BIN
StarterKits/Android/Platformer/Assets/Content/Overlays/you_lose.png


BIN
StarterKits/Android/Platformer/Assets/Content/Overlays/you_win.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/ExitReached.mp3


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/ExitReached.wav


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/ExitReached.wma


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/GemCollected.mp3


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/GemCollected.wav


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/GemCollected.wma


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/MonsterKilled.mp3


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/MonsterKilled.wav


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/MonsterKilled.wma


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/Music.mp3


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/Music.wav


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/Music.wma


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerFall.mp3


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerFall.wav


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerFall.wma


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerJump.mp3


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerJump.wav


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerJump.wma


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerKilled.mp3


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerKilled.wav


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/PlayerKilled.wma


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/PowerUp.mp3


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/PowerUp.wav


BIN
StarterKits/Android/Platformer/Assets/Content/Sounds/Powerup.wma


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/Gem.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterA/Idle.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterA/Run.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterB/Idle.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterB/Run.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterC/Idle.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterC/Run.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterD/Idle.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/MonsterD/Run.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/Player/Celebrate.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/Player/Die.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/Player/Idle.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/Player/Jump.png


BIN
StarterKits/Android/Platformer/Assets/Content/Sprites/Player/Run.png


BIN
StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA0.png


BIN
StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA1.png


BIN
StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA2.png


BIN
StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA3.png


BIN
StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA4.png


BIN
StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA5.png


BIN
StarterKits/Android/Platformer/Assets/Content/Tiles/BlockA6.png


BIN
StarterKits/Android/Platformer/Assets/Content/Tiles/BlockB0.png


BIN
StarterKits/Android/Platformer/Assets/Content/Tiles/BlockB1.png


BIN
StarterKits/Android/Platformer/Assets/Content/Tiles/Exit.png


BIN
StarterKits/Android/Platformer/Assets/Content/Tiles/Platform.png


BIN
StarterKits/Android/Platformer/Background.png


+ 54 - 0
StarterKits/Android/Platformer/Circle.cs

@@ -0,0 +1,54 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Circle.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using Microsoft.Xna.Framework;
+
+namespace Platformer
+{
+    /// <summary>
+    /// Represents a 2D circle.
+    /// </summary>
+    struct Circle
+    {
+        /// <summary>
+        /// Center position of the circle.
+        /// </summary>
+        public Vector2 Center;
+
+        /// <summary>
+        /// Radius of the circle.
+        /// </summary>
+        public float Radius;
+
+        /// <summary>
+        /// Constructs a new circle.
+        /// </summary>
+        public Circle(Vector2 position, float radius)
+        {
+            Center = position;
+            Radius = radius;
+        }
+
+        /// <summary>
+        /// Determines if a circle intersects a rectangle.
+        /// </summary>
+        /// <returns>True if the circle and rectangle overlap. False otherwise.</returns>
+        public bool Intersects(Rectangle rectangle)
+        {
+            Vector2 v = new Vector2(MathHelper.Clamp(Center.X, rectangle.Left, rectangle.Right),
+                                    MathHelper.Clamp(Center.Y, rectangle.Top, rectangle.Bottom));
+
+            Vector2 direction = Center - v;
+            float distanceSquared = direction.LengthSquared();
+
+            return ((distanceSquared > 0) && (distanceSquared < Radius * Radius));
+        }
+    }
+}

+ 179 - 0
StarterKits/Android/Platformer/Enemy.cs

@@ -0,0 +1,179 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Enemy.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Platformer
+{
+    /// <summary>
+    /// Facing direction along the X axis.
+    /// </summary>
+    enum FaceDirection
+    {
+        Left = -1,
+        Right = 1,
+    }
+
+    /// <summary>
+    /// A monster who is impeding the progress of our fearless adventurer.
+    /// </summary>
+    class Enemy
+    {
+        public Level Level
+        {
+            get { return level; }
+        }
+        Level level;
+
+        /// <summary>
+        /// Position in world space of the bottom center of this enemy.
+        /// </summary>
+        public Vector2 Position
+        {
+            get { return position; }
+        }
+        Vector2 position;
+
+        private Rectangle localBounds;
+        /// <summary>
+        /// Gets a rectangle which bounds this enemy in world space.
+        /// </summary>
+        public Rectangle BoundingRectangle
+        {
+            get
+            {
+                int left = (int)Math.Round(Position.X - sprite.Origin.X) + localBounds.X;
+                int top = (int)Math.Round(Position.Y - sprite.Origin.Y) + localBounds.Y;
+
+                return new Rectangle(left, top, localBounds.Width, localBounds.Height);
+            }
+        }
+
+        // Animations
+        private Animation runAnimation;
+        private Animation idleAnimation;
+        private AnimationPlayer sprite;
+
+        /// <summary>
+        /// The direction this enemy is facing and moving along the X axis.
+        /// </summary>
+        private FaceDirection direction = FaceDirection.Left;
+
+        /// <summary>
+        /// How long this enemy has been waiting before turning around.
+        /// </summary>
+        private float waitTime;
+
+        /// <summary>
+        /// How long to wait before turning around.
+        /// </summary>
+        private const float MaxWaitTime = 0.5f;
+
+        /// <summary>
+        /// The speed at which this enemy moves along the X axis.
+        /// </summary>
+        private const float MoveSpeed = 64.0f;
+
+        /// <summary>
+        /// Constructs a new Enemy.
+        /// </summary>
+        public Enemy(Level level, Vector2 position, string spriteSet)
+        {
+            this.level = level;
+            this.position = position;
+
+            LoadContent(spriteSet);
+        }
+
+        /// <summary>
+        /// Loads a particular enemy sprite sheet and sounds.
+        /// </summary>
+        public void LoadContent(string spriteSet)
+        {
+            // Load animations.
+            spriteSet = "Sprites/" + spriteSet + "/";
+            runAnimation = new Animation(Level.Content.Load<Texture2D>(spriteSet + "Run"), 0.1f, true);
+            idleAnimation = new Animation(Level.Content.Load<Texture2D>(spriteSet + "Idle"), 0.15f, true);
+            sprite.PlayAnimation(idleAnimation);
+
+            // Calculate bounds within texture size.
+            int width = (int)(idleAnimation.FrameWidth * 0.35);
+            int left = (idleAnimation.FrameWidth - width) / 2;
+            int height = (int)(idleAnimation.FrameWidth * 0.7);
+            int top = idleAnimation.FrameHeight - height;
+            localBounds = new Rectangle(left, top, width, height);
+        }
+
+
+        /// <summary>
+        /// Paces back and forth along a platform, waiting at either end.
+        /// </summary>
+        public void Update(GameTime gameTime)
+        {
+            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+            // Calculate tile position based on the side we are walking towards.
+            float posX = Position.X + localBounds.Width / 2 * (int)direction;
+            int tileX = (int)Math.Floor(posX / Tile.Width) - (int)direction;
+            int tileY = (int)Math.Floor(Position.Y / Tile.Height);
+
+            if (waitTime > 0)
+            {
+                // Wait for some amount of time.
+                waitTime = Math.Max(0.0f, waitTime - (float)gameTime.ElapsedGameTime.TotalSeconds);
+                if (waitTime <= 0.0f)
+                {
+                    // Then turn around.
+                    direction = (FaceDirection)(-(int)direction);
+                }
+            }
+            else
+            {
+                // If we are about to run into a wall or off a cliff, start waiting.
+                if (Level.GetCollision(tileX + (int)direction, tileY - 1) == TileCollision.Impassable ||
+                    Level.GetCollision(tileX + (int)direction, tileY) == TileCollision.Passable)
+                {
+                    waitTime = MaxWaitTime;
+                }
+                else
+                {
+                    // Move in the current direction.
+                    Vector2 velocity = new Vector2((int)direction * MoveSpeed * elapsed, 0.0f);
+                    position = position + velocity;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Draws the animated enemy.
+        /// </summary>
+        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+        {
+            // Stop running when the game is paused or before turning around.
+            if (!Level.Player.IsAlive ||
+                Level.ReachedExit ||
+                Level.TimeRemaining == TimeSpan.Zero ||
+                waitTime > 0)
+            {
+                sprite.PlayAnimation(idleAnimation);
+            }
+            else
+            {
+                sprite.PlayAnimation(runAnimation);
+            }
+
+
+            // Draw facing the way the enemy is moving.
+            SpriteEffects flip = direction > 0 ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
+            sprite.Draw(gameTime, spriteBatch, Position, flip);
+        }
+    }
+}

BIN
StarterKits/Android/Platformer/Game.ico


BIN
StarterKits/Android/Platformer/GameThumbnail.png


+ 118 - 0
StarterKits/Android/Platformer/Gem.cs

@@ -0,0 +1,118 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Gem.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Audio;
+
+namespace Platformer
+{
+    /// <summary>
+    /// A valuable item the player can collect.
+    /// </summary>
+    class Gem
+    {
+        private Texture2D texture;
+        private Vector2 origin;
+        private SoundEffect collectedSound;
+
+        public const int PointValue = 30;
+        public readonly Color Color = Color.Yellow;
+
+        // The gem is animated from a base position along the Y axis.
+        private Vector2 basePosition;
+        private float bounce;
+
+        public Level Level
+        {
+            get { return level; }
+        }
+        Level level;
+
+        /// <summary>
+        /// Gets the current position of this gem in world space.
+        /// </summary>
+        public Vector2 Position
+        {
+            get
+            {
+                return basePosition + new Vector2(0.0f, bounce);
+            }
+        }
+
+        /// <summary>
+        /// Gets a circle which bounds this gem in world space.
+        /// </summary>
+        public Circle BoundingCircle
+        {
+            get
+            {
+                return new Circle(Position, Tile.Width / 3.0f);
+            }
+        }
+
+        /// <summary>
+        /// Constructs a new gem.
+        /// </summary>
+        public Gem(Level level, Vector2 position)
+        {
+            this.level = level;
+            this.basePosition = position;
+
+            LoadContent();
+        }
+
+        /// <summary>
+        /// Loads the gem texture and collected sound.
+        /// </summary>
+        public void LoadContent()
+        {
+            texture = Level.Content.Load<Texture2D>("Sprites/Gem");
+            origin = new Vector2(texture.Width / 2.0f, texture.Height / 2.0f);
+            collectedSound = Level.Content.Load<SoundEffect>("Sounds/GemCollected");
+        }
+
+        /// <summary>
+        /// Bounces up and down in the air to entice players to collect them.
+        /// </summary>
+        public void Update(GameTime gameTime)
+        {
+            // Bounce control constants
+            const float BounceHeight = 0.18f;
+            const float BounceRate = 3.0f;
+            const float BounceSync = -0.75f;
+
+            // Bounce along a sine curve over time.
+            // Include the X coordinate so that neighboring gems bounce in a nice wave pattern.            
+            double t = gameTime.TotalGameTime.TotalSeconds * BounceRate + Position.X * BounceSync;
+            bounce = (float)Math.Sin(t) * BounceHeight * texture.Height;
+        }
+
+        /// <summary>
+        /// Called when this gem has been collected by a player and removed from the level.
+        /// </summary>
+        /// <param name="collectedBy">
+        /// The player who collected this gem. Although currently not used, this parameter would be
+        /// useful for creating special powerup gems. For example, a gem could make the player invincible.
+        /// </param>
+        public void OnCollected(Player collectedBy)
+        {
+            collectedSound.Play();
+        }
+
+        /// <summary>
+        /// Draws a gem in the appropriate color.
+        /// </summary>
+        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+        {
+            spriteBatch.Draw(texture, Position, null, Color, 0.0f, origin, 1.0f, SpriteEffects.None, 0.0f);
+        }
+    }
+}

+ 553 - 0
StarterKits/Android/Platformer/Level.cs

@@ -0,0 +1,553 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Level.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Audio;
+using System.IO;
+using Microsoft.Xna.Framework.Input.Touch;
+using Microsoft.Xna.Framework.Input;
+
+namespace Platformer
+{
+    /// <summary>
+    /// A uniform grid of tiles with collections of gems and enemies.
+    /// The level owns the player and controls the game's win and lose
+    /// conditions as well as scoring.
+    /// </summary>
+    class Level : IDisposable
+    {
+        // Physical structure of the level.
+        private Tile[,] tiles;
+        private Texture2D[] layers;
+        // The layer which entities are drawn on top of.
+        private const int EntityLayer = 2;
+
+        // Entities in the level.
+        public Player Player
+        {
+            get { return player; }
+        }
+        Player player;
+
+        private List<Gem> gems = new List<Gem>();
+        private List<Enemy> enemies = new List<Enemy>();
+
+        // Key locations in the level.        
+        private Vector2 start;
+        private Point exit = InvalidPosition;
+        private static readonly Point InvalidPosition = new Point(-1, -1);
+
+        // Level game state.
+        private Random random = new Random(354668); // Arbitrary, but constant seed
+
+        public int Score
+        {
+            get { return score; }
+        }
+        int score;
+
+        public bool ReachedExit
+        {
+            get { return reachedExit; }
+        }
+        bool reachedExit;
+
+        public TimeSpan TimeRemaining
+        {
+            get { return timeRemaining; }
+        }
+        TimeSpan timeRemaining;
+
+        private const int PointsPerSecond = 5;
+
+        // Level content.        
+        public ContentManager Content
+        {
+            get { return content; }
+        }
+        ContentManager content;
+
+        private SoundEffect exitReachedSound;
+
+        #region Loading
+
+        /// <summary>
+        /// Constructs a new level.
+        /// </summary>
+        /// <param name="serviceProvider">
+        /// The service provider that will be used to construct a ContentManager.
+        /// </param>
+        /// <param name="fileStream">
+        /// A stream containing the tile data.
+        /// </param>
+        public Level(IServiceProvider serviceProvider, Stream fileStream, int levelIndex)
+        {
+            // Create a new content manager to load content used just by this level.
+            content = new ContentManager(serviceProvider, "Content");
+
+            timeRemaining = TimeSpan.FromMinutes(2.0);
+
+            LoadTiles(fileStream);
+
+            // Load background layer textures. For now, all levels must
+            // use the same backgrounds and only use the left-most part of them.
+            layers = new Texture2D[3];
+            for (int i = 0; i < layers.Length; ++i)
+            {
+                // Choose a random segment if each background layer for level variety.
+                int segmentIndex = levelIndex;
+                layers[i] = Content.Load<Texture2D>("Backgrounds/Layer" + i + "_" + segmentIndex);
+            }
+
+            // Load sounds.
+            exitReachedSound = Content.Load<SoundEffect>("Sounds/ExitReached");
+        }
+
+        /// <summary>
+        /// Iterates over every tile in the structure file and loads its
+        /// appearance and behavior. This method also validates that the
+        /// file is well-formed with a player start point, exit, etc.
+        /// </summary>
+        /// <param name="fileStream">
+        /// A stream containing the tile data.
+        /// </param>
+        private void LoadTiles(Stream fileStream)
+        {
+            // Load the level and ensure all of the lines are the same length.
+            int width;
+            List<string> lines = new List<string>();
+            using (StreamReader reader = new StreamReader(fileStream))
+            {
+                string line = reader.ReadLine();
+                width = line.Length;
+                while (line != null)
+                {
+                    lines.Add(line);
+                    if (line.Length != width)
+                        throw new Exception(String.Format("The length of line {0} is different from all preceeding lines.", lines.Count));
+                    line = reader.ReadLine();
+                }
+            }
+
+            // Allocate the tile grid.
+            tiles = new Tile[width, lines.Count];
+
+            // Loop over every tile position,
+            for (int y = 0; y < Height; ++y)
+            {
+                for (int x = 0; x < Width; ++x)
+                {
+                    // to load each tile.
+                    char tileType = lines[y][x];
+                    tiles[x, y] = LoadTile(tileType, x, y);
+                }
+            }
+
+            // Verify that the level has a beginning and an end.
+            if (Player == null)
+                throw new NotSupportedException("A level must have a starting point.");
+            if (exit == InvalidPosition)
+                throw new NotSupportedException("A level must have an exit.");
+
+        }
+
+        /// <summary>
+        /// Loads an individual tile's appearance and behavior.
+        /// </summary>
+        /// <param name="tileType">
+        /// The character loaded from the structure file which
+        /// indicates what should be loaded.
+        /// </param>
+        /// <param name="x">
+        /// The X location of this tile in tile space.
+        /// </param>
+        /// <param name="y">
+        /// The Y location of this tile in tile space.
+        /// </param>
+        /// <returns>The loaded tile.</returns>
+        private Tile LoadTile(char tileType, int x, int y)
+        {
+            switch (tileType)
+            {
+                // Blank space
+                case '.':
+                    return new Tile(null, TileCollision.Passable);
+
+                // Exit
+                case 'X':
+                    return LoadExitTile(x, y);
+
+                // Gem
+                case 'G':
+                    return LoadGemTile(x, y);
+
+                // Floating platform
+                case '-':
+                    return LoadTile("Platform", TileCollision.Platform);
+
+                // Various enemies
+                case 'A':
+                    return LoadEnemyTile(x, y, "MonsterA");
+                case 'B':
+                    return LoadEnemyTile(x, y, "MonsterB");
+                case 'C':
+                    return LoadEnemyTile(x, y, "MonsterC");
+                case 'D':
+                    return LoadEnemyTile(x, y, "MonsterD");
+
+                // Platform block
+                case '~':
+                    return LoadVarietyTile("BlockB", 2, TileCollision.Platform);
+
+                // Passable block
+                case ':':
+                    return LoadVarietyTile("BlockB", 2, TileCollision.Passable);
+
+                // Player 1 start point
+                case '1':
+                    return LoadStartTile(x, y);
+
+                // Impassable block
+                case '#':
+                    return LoadVarietyTile("BlockA", 7, TileCollision.Impassable);
+
+                // Unknown tile type character
+                default:
+                    throw new NotSupportedException(String.Format("Unsupported tile type character '{0}' at position {1}, {2}.", tileType, x, y));
+            }
+        }
+
+        /// <summary>
+        /// Creates a new tile. The other tile loading methods typically chain to this
+        /// method after performing their special logic.
+        /// </summary>
+        /// <param name="name">
+        /// Path to a tile texture relative to the Content/Tiles directory.
+        /// </param>
+        /// <param name="collision">
+        /// The tile collision type for the new tile.
+        /// </param>
+        /// <returns>The new tile.</returns>
+        private Tile LoadTile(string name, TileCollision collision)
+        {
+            return new Tile(Content.Load<Texture2D>("Tiles/" + name), collision);
+        }
+
+
+        /// <summary>
+        /// Loads a tile with a random appearance.
+        /// </summary>
+        /// <param name="baseName">
+        /// The content name prefix for this group of tile variations. Tile groups are
+        /// name LikeThis0.png and LikeThis1.png and LikeThis2.png.
+        /// </param>
+        /// <param name="variationCount">
+        /// The number of variations in this group.
+        /// </param>
+        private Tile LoadVarietyTile(string baseName, int variationCount, TileCollision collision)
+        {
+            int index = random.Next(variationCount);
+            return LoadTile(baseName + index, collision);
+        }
+
+
+        /// <summary>
+        /// Instantiates a player, puts him in the level, and remembers where to put him when he is resurrected.
+        /// </summary>
+        private Tile LoadStartTile(int x, int y)
+        {
+            if (Player != null)
+                throw new NotSupportedException("A level may only have one starting point.");
+
+            start = RectangleExtensions.GetBottomCenter(GetBounds(x, y));
+            player = new Player(this, start);
+
+            return new Tile(null, TileCollision.Passable);
+        }
+
+        /// <summary>
+        /// Remembers the location of the level's exit.
+        /// </summary>
+        private Tile LoadExitTile(int x, int y)
+        {
+            if (exit != InvalidPosition)
+                throw new NotSupportedException("A level may only have one exit.");
+
+            exit = GetBounds(x, y).Center;
+
+            return LoadTile("Exit", TileCollision.Passable);
+        }
+
+        /// <summary>
+        /// Instantiates an enemy and puts him in the level.
+        /// </summary>
+        private Tile LoadEnemyTile(int x, int y, string spriteSet)
+        {
+            Vector2 position = RectangleExtensions.GetBottomCenter(GetBounds(x, y));
+            enemies.Add(new Enemy(this, position, spriteSet));
+
+            return new Tile(null, TileCollision.Passable);
+        }
+
+        /// <summary>
+        /// Instantiates a gem and puts it in the level.
+        /// </summary>
+        private Tile LoadGemTile(int x, int y)
+        {
+            Point position = GetBounds(x, y).Center;
+            gems.Add(new Gem(this, new Vector2(position.X, position.Y)));
+
+            return new Tile(null, TileCollision.Passable);
+        }
+
+        /// <summary>
+        /// Unloads the level content.
+        /// </summary>
+        public void Dispose()
+        {
+            Content.Unload();
+        }
+
+        #endregion
+
+        #region Bounds and collision
+
+        /// <summary>
+        /// Gets the collision mode of the tile at a particular location.
+        /// This method handles tiles outside of the levels boundries by making it
+        /// impossible to escape past the left or right edges, but allowing things
+        /// to jump beyond the top of the level and fall off the bottom.
+        /// </summary>
+        public TileCollision GetCollision(int x, int y)
+        {
+            // Prevent escaping past the level ends.
+            if (x < 0 || x >= Width)
+                return TileCollision.Impassable;
+            // Allow jumping past the level top and falling through the bottom.
+            if (y < 0 || y >= Height)
+                return TileCollision.Passable;
+
+            return tiles[x, y].Collision;
+        }
+
+        /// <summary>
+        /// Gets the bounding rectangle of a tile in world space.
+        /// </summary>        
+        public Rectangle GetBounds(int x, int y)
+        {
+            return new Rectangle(x * Tile.Width, y * Tile.Height, Tile.Width, Tile.Height);
+        }
+
+        /// <summary>
+        /// Width of level measured in tiles.
+        /// </summary>
+        public int Width
+        {
+            get { return tiles.GetLength(0); }
+        }
+
+        /// <summary>
+        /// Height of the level measured in tiles.
+        /// </summary>
+        public int Height
+        {
+            get { return tiles.GetLength(1); }
+        }
+
+        #endregion
+
+        #region Update
+
+        /// <summary>
+        /// Updates all objects in the world, performs collision between them,
+        /// and handles the time limit with scoring.
+        /// </summary>
+        public void Update(
+            GameTime gameTime, 
+            KeyboardState keyboardState, 
+            GamePadState gamePadState, 
+            TouchCollection touchState, 
+            AccelerometerState accelState,
+            DisplayOrientation orientation)
+        {
+            // Pause while the player is dead or time is expired.
+            if (!Player.IsAlive || TimeRemaining == TimeSpan.Zero)
+            {
+                // Still want to perform physics on the player.
+                Player.ApplyPhysics(gameTime);
+            }
+            else if (ReachedExit)
+            {
+                // Animate the time being converted into points.
+                int seconds = (int)Math.Round(gameTime.ElapsedGameTime.TotalSeconds * 100.0f);
+                seconds = Math.Min(seconds, (int)Math.Ceiling(TimeRemaining.TotalSeconds));
+                timeRemaining -= TimeSpan.FromSeconds(seconds);
+                score += seconds * PointsPerSecond;
+            }
+            else
+            {
+                timeRemaining -= gameTime.ElapsedGameTime;
+                Player.Update(gameTime, keyboardState, gamePadState, touchState, accelState, orientation);
+                UpdateGems(gameTime);
+
+                // Falling off the bottom of the level kills the player.
+                if (Player.BoundingRectangle.Top >= Height * Tile.Height)
+                    OnPlayerKilled(null);
+
+                UpdateEnemies(gameTime);
+
+                // The player has reached the exit if they are standing on the ground and
+                // his bounding rectangle contains the center of the exit tile. They can only
+                // exit when they have collected all of the gems.
+                if (Player.IsAlive &&
+                    Player.IsOnGround &&
+                    Player.BoundingRectangle.Contains(exit))
+                {
+                    OnExitReached();
+                }
+            }
+
+            // Clamp the time remaining at zero.
+            if (timeRemaining < TimeSpan.Zero)
+                timeRemaining = TimeSpan.Zero;
+        }
+
+        /// <summary>
+        /// Animates each gem and checks to allows the player to collect them.
+        /// </summary>
+        private void UpdateGems(GameTime gameTime)
+        {
+            for (int i = 0; i < gems.Count; ++i)
+            {
+                Gem gem = gems[i];
+
+                gem.Update(gameTime);
+
+                if (gem.BoundingCircle.Intersects(Player.BoundingRectangle))
+                {
+                    gems.RemoveAt(i--);
+                    OnGemCollected(gem, Player);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Animates each enemy and allow them to kill the player.
+        /// </summary>
+        private void UpdateEnemies(GameTime gameTime)
+        {
+            foreach (Enemy enemy in enemies)
+            {
+                enemy.Update(gameTime);
+
+                // Touching an enemy instantly kills the player
+                if (enemy.BoundingRectangle.Intersects(Player.BoundingRectangle))
+                {
+                    OnPlayerKilled(enemy);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Called when a gem is collected.
+        /// </summary>
+        /// <param name="gem">The gem that was collected.</param>
+        /// <param name="collectedBy">The player who collected this gem.</param>
+        private void OnGemCollected(Gem gem, Player collectedBy)
+        {
+            score += Gem.PointValue;
+
+            gem.OnCollected(collectedBy);
+        }
+
+        /// <summary>
+        /// Called when the player is killed.
+        /// </summary>
+        /// <param name="killedBy">
+        /// The enemy who killed the player. This is null if the player was not killed by an
+        /// enemy, such as when a player falls into a hole.
+        /// </param>
+        private void OnPlayerKilled(Enemy killedBy)
+        {
+            Player.OnKilled(killedBy);
+        }
+
+        /// <summary>
+        /// Called when the player reaches the level's exit.
+        /// </summary>
+        private void OnExitReached()
+        {
+            Player.OnReachedExit();
+            exitReachedSound.Play();
+            reachedExit = true;
+        }
+
+        /// <summary>
+        /// Restores the player to the starting point to try the level again.
+        /// </summary>
+        public void StartNewLife()
+        {
+            Player.Reset(start);
+        }
+
+        #endregion
+
+        #region Draw
+
+        /// <summary>
+        /// Draw everything in the level from background to foreground.
+        /// </summary>
+        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+        {
+            for (int i = 0; i <= EntityLayer; ++i)
+                spriteBatch.Draw(layers[i], Vector2.Zero, Color.White);
+
+            DrawTiles(spriteBatch);
+
+            foreach (Gem gem in gems)
+                gem.Draw(gameTime, spriteBatch);
+
+            Player.Draw(gameTime, spriteBatch);
+
+            foreach (Enemy enemy in enemies)
+                enemy.Draw(gameTime, spriteBatch);
+
+            for (int i = EntityLayer + 1; i < layers.Length; ++i)
+                spriteBatch.Draw(layers[i], Vector2.Zero, Color.White);
+        }
+
+        /// <summary>
+        /// Draws each tile in the level.
+        /// </summary>
+        private void DrawTiles(SpriteBatch spriteBatch)
+        {
+            // For each tile position
+            for (int y = 0; y < Height; ++y)
+            {
+                for (int x = 0; x < Width; ++x)
+                {
+                    // If there is a visible tile in that position
+                    Texture2D texture = tiles[x, y].Texture;
+                    if (texture != null)
+                    {
+                        // Draw it in screen space.
+                        Vector2 position = new Vector2(x, y) * Tile.Size;
+                        spriteBatch.Draw(texture, position, Color.White);
+                    }
+                }
+            }
+        }
+
+        #endregion
+    }
+}

+ 142 - 0
StarterKits/Android/Platformer/Platformer.csproj

@@ -0,0 +1,142 @@
+<?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>{9FA77471-A020-4452-928B-7D72BF15C991}</ProjectGuid>
+    <ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <RootNamespace>PlatformerTwo</RootNamespace>
+    <MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
+    <MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
+    <AndroidResgenClass>Resource</AndroidResgenClass>
+    <AndroidApplication>True</AndroidApplication>
+    <AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
+    <AssemblyName>PlatformerTwo</AssemblyName>
+    <TargetFrameworkVersion>v2.2</TargetFrameworkVersion>
+  </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>
+    <AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
+    <ConsolePause>false</ConsolePause>
+    <AndroidLinkMode>SdkOnly</AndroidLinkMode>
+    <DefineConstants>ANDROID</DefineConstants>
+  </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="Animation.cs" />
+    <Compile Include="AnimationPlayer.cs" />
+    <Compile Include="Circle.cs" />
+    <Compile Include="Enemy.cs" />
+    <Compile Include="Gem.cs" />
+    <Compile Include="Level.cs" />
+    <Compile Include="PlatformerGame.cs" />
+    <Compile Include="Player.cs" />
+    <Compile Include="RectangleExtensions.cs" />
+    <Compile Include="Tile.cs" />
+    <Compile Include="TouchCollectionExtensions.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\AboutResources.txt" />
+    <None Include="Assets\AboutAssets.txt" />
+    <None Include="Background.png" />
+    <None Include="Game.ico" />
+    <None Include="GameThumbnail.png" />
+    <None Include="Properties\AppManifest.xml" />
+    <None Include="Properties\WMAppManifest.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>
+    <Folder Include="Properties\" />
+    <Folder Include="Assets\Content\Fonts\" />
+    <Folder Include="Assets\Content\Sounds\" />
+  </ItemGroup>
+  <ItemGroup>
+    <AndroidAsset Include="Assets\Content\Backgrounds\Layer0_0.png" />
+    <AndroidAsset Include="Assets\Content\Backgrounds\Layer0_1.png" />
+    <AndroidAsset Include="Assets\Content\Backgrounds\Layer0_2.png" />
+    <AndroidAsset Include="Assets\Content\Backgrounds\Layer1_0.png" />
+    <AndroidAsset Include="Assets\Content\Backgrounds\Layer1_1.png" />
+    <AndroidAsset Include="Assets\Content\Backgrounds\Layer1_2.png" />
+    <AndroidAsset Include="Assets\Content\Backgrounds\Layer2_0.png" />
+    <AndroidAsset Include="Assets\Content\Backgrounds\Layer2_1.png" />
+    <AndroidAsset Include="Assets\Content\Backgrounds\Layer2_2.png" />
+    <AndroidAsset Include="Assets\Content\Levels\0.txt" />
+    <AndroidAsset Include="Assets\Content\Levels\1.txt" />
+    <AndroidAsset Include="Assets\Content\Levels\2.txt" />
+    <AndroidAsset Include="Assets\Content\Overlays\you_died.png" />
+    <AndroidAsset Include="Assets\Content\Overlays\you_lose.png" />
+    <AndroidAsset Include="Assets\Content\Overlays\you_win.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\Gem.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\MonsterA\Idle.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\MonsterA\Run.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\MonsterB\Idle.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\MonsterB\Run.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\MonsterC\Idle.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\MonsterC\Run.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\MonsterD\Idle.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\MonsterD\Run.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\Player\Celebrate.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\Player\Die.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\Player\Idle.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\Player\Jump.png" />
+    <AndroidAsset Include="Assets\Content\Sprites\Player\Run.png" />
+    <AndroidAsset Include="Assets\Content\Tiles\BlockA0.png" />
+    <AndroidAsset Include="Assets\Content\Tiles\BlockA1.png" />
+    <AndroidAsset Include="Assets\Content\Tiles\BlockA2.png" />
+    <AndroidAsset Include="Assets\Content\Tiles\BlockA3.png" />
+    <AndroidAsset Include="Assets\Content\Tiles\BlockA4.png" />
+    <AndroidAsset Include="Assets\Content\Tiles\BlockA5.png" />
+    <AndroidAsset Include="Assets\Content\Tiles\BlockA6.png" />
+    <AndroidAsset Include="Assets\Content\Tiles\BlockB0.png" />
+    <AndroidAsset Include="Assets\Content\Tiles\BlockB1.png" />
+    <AndroidAsset Include="Assets\Content\Tiles\Exit.png" />
+    <AndroidAsset Include="Assets\Content\Tiles\Platform.png" />
+    <AndroidAsset Include="Assets\Content\Fonts\Hud.xnb" />
+    <AndroidAsset Include="Assets\Content\Sounds\ExitReached.wav" />
+    <AndroidAsset Include="Assets\Content\Sounds\GemCollected.wav" />
+    <AndroidAsset Include="Assets\Content\Sounds\MonsterKilled.wav" />
+    <AndroidAsset Include="Assets\Content\Sounds\Music.wav" />
+    <AndroidAsset Include="Assets\Content\Sounds\PlayerFall.wav" />
+    <AndroidAsset Include="Assets\Content\Sounds\PlayerJump.wav" />
+    <AndroidAsset Include="Assets\Content\Sounds\PlayerKilled.wav" />
+    <AndroidAsset Include="Assets\Content\Sounds\PowerUp.wav" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\MonoGame\MonoGame.Framework\MonoGame.Framework.Android.csproj">
+      <Project>{BA9476CF-99BA-4D03-92F2-73D2C5E58883}</Project>
+      <Name>MonoGame.Framework.Android</Name>
+    </ProjectReference>
+  </ItemGroup>
+</Project>

+ 264 - 0
StarterKits/Android/Platformer/PlatformerGame.cs

@@ -0,0 +1,264 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// PlatformerGame.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#if ANDROID
+using Android.App;
+#endif
+
+using System;
+using System.IO;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Media;
+using Microsoft.Xna.Framework.Input.Touch;
+
+
+namespace Platformer
+{
+    /// <summary>
+    /// This is the main type for your game
+    /// </summary>
+    public class PlatformerGame : Microsoft.Xna.Framework.Game
+    {
+        // Resources for drawing.
+        private GraphicsDeviceManager graphics;
+        private SpriteBatch spriteBatch;
+
+        // Global content.
+        private SpriteFont hudFont;
+
+        private Texture2D winOverlay;
+        private Texture2D loseOverlay;
+        private Texture2D diedOverlay;
+
+        // Meta-level game state.
+        private int levelIndex = -1;
+        private Level level;
+        private bool wasContinuePressed;
+
+        // When the time remaining is less than the warning time, it blinks on the hud
+        private static readonly TimeSpan WarningTime = TimeSpan.FromSeconds(30);
+
+        // We store our input states so that we only poll once per frame, 
+        // then we use the same input state wherever needed
+        private GamePadState gamePadState;
+        private KeyboardState keyboardState;
+        private TouchCollection touchState;
+        private AccelerometerState accelerometerState;
+        
+        // The number of levels in the Levels directory of our content. We assume that
+        // levels in our content are 0-based and that all numbers under this constant
+        // have a level file present. This allows us to not need to check for the file
+        // or handle exceptions, both of which can add unnecessary time to level loading.
+        private const int numberOfLevels = 3;
+
+#if ANDROID 
+        public PlatformerGame (Activity activity) : base (activity)
+#else 
+       public PlatformerGame()  
+#endif
+        {
+            graphics = new GraphicsDeviceManager(this);
+            Content.RootDirectory = "Content";
+
+#if WINDOWS_PHONE
+            graphics.IsFullScreen = true;
+            TargetElapsedTime = TimeSpan.FromTicks(333333);
+#endif
+            //Accelerometer.Initialize();
+        }
+
+        /// <summary>
+        /// LoadContent will be called once per game and is the place to load
+        /// all of your content.
+        /// </summary>
+        protected override void LoadContent()
+        {
+            // Create a new SpriteBatch, which can be used to draw textures.
+            spriteBatch = new SpriteBatch(GraphicsDevice);
+
+            // Load fonts
+            hudFont = Content.Load<SpriteFont>("Fonts/Hud");
+
+            // Load overlay textures
+            winOverlay = Content.Load<Texture2D>("Overlays/you_win");
+            loseOverlay = Content.Load<Texture2D>("Overlays/you_lose");
+            diedOverlay = Content.Load<Texture2D>("Overlays/you_died");
+
+            //Known issue that you get exceptions if you use Media PLayer while connected to your PC
+            //See http://social.msdn.microsoft.com/Forums/en/windowsphone7series/thread/c8a243d2-d360-46b1-96bd-62b1ef268c66
+            //Which means its impossible to test this from VS.
+            //So we have to catch the exception and throw it away
+            try
+            {
+                MediaPlayer.IsRepeating = true;
+                MediaPlayer.Play(Content.Load<Song>("Sounds/Music"));
+            }
+            catch { }
+
+            LoadNextLevel();
+        }
+
+        /// <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)
+        {
+            // Handle polling for our input and handling high-level input
+            HandleInput();
+
+            // update our level, passing down the GameTime along with all of our input states
+            level.Update(gameTime, keyboardState, gamePadState, touchState, 
+                         accelerometerState, Window.CurrentOrientation);
+
+            base.Update(gameTime);
+        }
+
+        private void HandleInput()
+        {
+            // get all of our input states
+            keyboardState = Keyboard.GetState();
+            gamePadState = GamePad.GetState(PlayerIndex.One);
+            touchState = TouchPanel.GetState();
+            accelerometerState = Accelerometer.GetState();
+
+            // Exit the game when back is pressed.
+            if (gamePadState.Buttons.Back == ButtonState.Pressed)
+                Exit();
+
+            bool continuePressed =
+                keyboardState.IsKeyDown(Keys.Space) ||
+                gamePadState.IsButtonDown(Buttons.A) ||
+                touchState.AnyTouch();
+
+            // Perform the appropriate action to advance the game and
+            // to get the player back to playing.
+            if (!wasContinuePressed && continuePressed)
+            {
+                if (!level.Player.IsAlive)
+                {
+                    level.StartNewLife();
+                }
+                else if (level.TimeRemaining == TimeSpan.Zero)
+                {
+                    if (level.ReachedExit)
+                        LoadNextLevel();
+                    else
+                        ReloadCurrentLevel();
+                }
+            }
+
+            wasContinuePressed = continuePressed;
+        }
+
+        private void LoadNextLevel()
+        {
+            // move to the next level
+            levelIndex = (levelIndex + 1) % numberOfLevels;
+			int j = 4 ;
+            // Unloads the content for the current level before loading the next one.
+            if (level != null)
+                level.Dispose();
+
+            // Load the level.
+            string levelPath = string.Format("Content/Levels/{0}.txt", levelIndex);
+            using (Stream fileStream = TitleContainer.OpenStream(levelPath))
+                level = new Level(Services, fileStream, levelIndex);
+			j = 4 ;
+        }
+
+        private void ReloadCurrentLevel()
+        {
+            --levelIndex;
+            LoadNextLevel();
+        }
+
+        /// <summary>
+        /// Draws the game from background to foreground.
+        /// </summary>
+        /// <param name="gameTime">Provides a snapshot of timing values.</param>
+        protected override void Draw(GameTime gameTime)
+        {
+            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
+
+
+            spriteBatch.Begin();
+
+            level.Draw(gameTime, spriteBatch);
+
+            DrawHud();
+
+            spriteBatch.End();
+
+            base.Draw(gameTime);
+        }
+
+        private void DrawHud()
+        {
+            Rectangle titleSafeArea = GraphicsDevice.Viewport.TitleSafeArea;
+            Vector2 hudLocation = new Vector2(titleSafeArea.X, titleSafeArea.Y);
+            Vector2 center = new Vector2(titleSafeArea.X + titleSafeArea.Width / 2.0f,
+                                         titleSafeArea.Y + titleSafeArea.Height / 2.0f);
+
+            // Draw time remaining. Uses modulo division to cause blinking when the
+            // player is running out of time.
+            string timeString = "TIME: " + level.TimeRemaining.Minutes.ToString("00") + ":" + level.TimeRemaining.Seconds.ToString("00");
+            Color timeColor;
+            if (level.TimeRemaining > WarningTime ||
+                level.ReachedExit ||
+                (int)level.TimeRemaining.TotalSeconds % 2 == 0)
+            {
+                timeColor = Color.Yellow;
+            }
+            else
+            {
+                timeColor = Color.Red;
+            }
+            DrawShadowedString(hudFont, timeString, hudLocation, timeColor);
+
+            // Draw score
+            float timeHeight = hudFont.MeasureString(timeString).Y;
+            DrawShadowedString(hudFont, "SCORE: " + level.Score.ToString(), hudLocation + new Vector2(0.0f, timeHeight * 1.2f), Color.Yellow);
+           
+            // Determine the status overlay message to show.
+            Texture2D status = null;
+            if (level.TimeRemaining == TimeSpan.Zero)
+            {
+                if (level.ReachedExit)
+                {
+                    status = winOverlay;
+                }
+                else
+                {
+                    status = loseOverlay;
+                }
+            }
+            else if (!level.Player.IsAlive)
+            {
+                status = diedOverlay;
+            }
+
+            if (status != null)
+            {
+                // Draw status message.
+                Vector2 statusSize = new Vector2(status.Width, status.Height);
+                spriteBatch.Draw(status, center - statusSize / 2, Color.White);
+            }
+        }
+
+        private void DrawShadowedString(SpriteFont font, string value, Vector2 position, Color color)
+        {
+            spriteBatch.DrawString(font, value, position + new Vector2(1.0f, 1.0f), Color.Black);
+            spriteBatch.DrawString(font, value, position, color);
+        }
+    }
+}

+ 466 - 0
StarterKits/Android/Platformer/Player.cs

@@ -0,0 +1,466 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Player.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Input.Touch;
+
+namespace Platformer
+{
+    /// <summary>
+    /// Our fearless adventurer!
+    /// </summary>
+    class Player
+    {
+        // Animations
+        private Animation idleAnimation;
+        private Animation runAnimation;
+        private Animation jumpAnimation;
+        private Animation celebrateAnimation;
+        private Animation dieAnimation;
+        private SpriteEffects flip = SpriteEffects.None;
+        private AnimationPlayer sprite;
+
+        // Sounds
+        private SoundEffect killedSound;
+        private SoundEffect jumpSound;
+        private SoundEffect fallSound;
+
+        public Level Level
+        {
+            get { return level; }
+        }
+        Level level;
+
+        public bool IsAlive
+        {
+            get { return isAlive; }
+        }
+        bool isAlive;
+
+        // Physics state
+        public Vector2 Position
+        {
+            get { return position; }
+            set { position = value; }
+        }
+        Vector2 position;
+
+        private float previousBottom;
+
+        public Vector2 Velocity
+        {
+            get { return velocity; }
+            set { velocity = value; }
+        }
+        Vector2 velocity;
+
+        // Constants for controling horizontal movement
+        private const float MoveAcceleration = 13000.0f;
+        private const float MaxMoveSpeed = 1750.0f;
+        private const float GroundDragFactor = 0.48f;
+        private const float AirDragFactor = 0.58f;
+
+        // Constants for controlling vertical movement
+        private const float MaxJumpTime = 0.35f;
+        private const float JumpLaunchVelocity = -3500.0f;
+        private const float GravityAcceleration = 3400.0f;
+        private const float MaxFallSpeed = 550.0f;
+        private const float JumpControlPower = 0.14f; 
+
+        // Input configuration
+        private const float MoveStickScale = 1.0f;
+        private const float AccelerometerScale = 1.5f;
+        private const Buttons JumpButton = Buttons.A;
+
+        /// <summary>
+        /// Gets whether or not the player's feet are on the ground.
+        /// </summary>
+        public bool IsOnGround
+        {
+            get { return isOnGround; }
+        }
+        bool isOnGround;
+
+        /// <summary>
+        /// Current user movement input.
+        /// </summary>
+        private float movement;
+
+        // Jumping state
+        private bool isJumping;
+        private bool wasJumping;
+        private float jumpTime;
+
+        private Rectangle localBounds;
+        /// <summary>
+        /// Gets a rectangle which bounds this player in world space.
+        /// </summary>
+        public Rectangle BoundingRectangle
+        {
+            get
+            {
+                int left = (int)Math.Round(Position.X - sprite.Origin.X) + localBounds.X;
+                int top = (int)Math.Round(Position.Y - sprite.Origin.Y) + localBounds.Y;
+
+                return new Rectangle(left, top, localBounds.Width, localBounds.Height);
+            }
+        }
+
+        /// <summary>
+        /// Constructors a new player.
+        /// </summary>
+        public Player(Level level, Vector2 position)
+        {
+            this.level = level;
+
+            LoadContent();
+
+            Reset(position);
+        }
+
+        /// <summary>
+        /// Loads the player sprite sheet and sounds.
+        /// </summary>
+        public void LoadContent()
+        {
+            // Load animated textures.
+            idleAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/Idle"), 0.1f, true);
+            runAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/Run"), 0.1f, true);
+            jumpAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/Jump"), 0.1f, false);
+            celebrateAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/Celebrate"), 0.1f, false);
+            dieAnimation = new Animation(Level.Content.Load<Texture2D>("Sprites/Player/Die"), 0.1f, false);
+
+            // Calculate bounds within texture size.            
+            int width = (int)(idleAnimation.FrameWidth * 0.4);
+            int left = (idleAnimation.FrameWidth - width) / 2;
+            int height = (int)(idleAnimation.FrameWidth * 0.8);
+            int top = idleAnimation.FrameHeight - height;
+            localBounds = new Rectangle(left, top, width, height);
+
+            // Load sounds.            
+            killedSound = Level.Content.Load<SoundEffect>("Sounds/PlayerKilled");
+            jumpSound = Level.Content.Load<SoundEffect>("Sounds/PlayerJump");
+            fallSound = Level.Content.Load<SoundEffect>("Sounds/PlayerFall");
+        }
+
+        /// <summary>
+        /// Resets the player to life.
+        /// </summary>
+        /// <param name="position">The position to come to life at.</param>
+        public void Reset(Vector2 position)
+        {
+            Position = position;
+            Velocity = Vector2.Zero;
+            isAlive = true;
+            sprite.PlayAnimation(idleAnimation);
+        }
+
+        /// <summary>
+        /// Handles input, performs physics, and animates the player sprite.
+        /// </summary>
+        /// <remarks>
+        /// We pass in all of the input states so that our game is only polling the hardware
+        /// once per frame. We also pass the game's orientation because when using the accelerometer,
+        /// we need to reverse our motion when the orientation is in the LandscapeRight orientation.
+        /// </remarks>
+        public void Update(
+            GameTime gameTime, 
+            KeyboardState keyboardState, 
+            GamePadState gamePadState, 
+            TouchCollection touchState, 
+            AccelerometerState accelState,
+            DisplayOrientation orientation)
+        {
+            GetInput(keyboardState, gamePadState, touchState, accelState, orientation);
+
+            ApplyPhysics(gameTime);
+
+            if (IsAlive && IsOnGround)
+            {
+                if (Math.Abs(Velocity.X) - 0.02f > 0)
+                {
+                    sprite.PlayAnimation(runAnimation);
+                }
+                else
+                {
+                    sprite.PlayAnimation(idleAnimation);
+                }
+            }
+
+            // Clear input.
+            movement = 0.0f;
+            isJumping = false;
+        }
+
+        /// <summary>
+        /// Gets player horizontal movement and jump commands from input.
+        /// </summary>
+        private void GetInput(
+            KeyboardState keyboardState, 
+            GamePadState gamePadState, 
+            TouchCollection touchState,
+            AccelerometerState accelState, 
+            DisplayOrientation orientation)
+        {
+            // Get analog horizontal movement.
+            movement = gamePadState.ThumbSticks.Left.X * MoveStickScale;
+
+            // Ignore small movements to prevent running in place.
+            if (Math.Abs(movement) < 0.5f)
+                movement = 0.0f;
+
+            // Move the player with accelerometer
+			float accel = 0;
+			if(orientation == DisplayOrientation.Portrait || orientation == DisplayOrientation.PortraitUpsideDown)
+				accel = accelState.Acceleration.X;
+			else
+				accel = accelState.Acceleration.Y;
+            if (Math.Abs(accel) > 0.10f)
+            {
+                // set our movement speed
+                movement = MathHelper.Clamp(-accel * AccelerometerScale, -1f, 1f);
+
+                // if we're in the LandscapeLeft orientation, we must reverse our movement
+                if (orientation == DisplayOrientation.LandscapeRight)
+                    movement = -movement;
+            }
+
+            // If any digital horizontal movement input is found, override the analog movement.
+            if (gamePadState.IsButtonDown(Buttons.DPadLeft) ||
+                keyboardState.IsKeyDown(Keys.Left) ||
+                keyboardState.IsKeyDown(Keys.A))
+            {
+                movement = -1.0f;
+            }
+            else if (gamePadState.IsButtonDown(Buttons.DPadRight) ||
+                     keyboardState.IsKeyDown(Keys.Right) ||
+                     keyboardState.IsKeyDown(Keys.D))
+            {
+                movement = 1.0f;
+            }
+
+            // Check if the player wants to jump.
+            isJumping =
+                gamePadState.IsButtonDown(JumpButton) ||
+                keyboardState.IsKeyDown(Keys.Space) ||
+                keyboardState.IsKeyDown(Keys.Up) ||
+                keyboardState.IsKeyDown(Keys.W) ||
+                touchState.AnyTouch();
+        }
+
+        /// <summary>
+        /// Updates the player's velocity and position based on input, gravity, etc.
+        /// </summary>
+        public void ApplyPhysics(GameTime gameTime)
+        {
+            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+            Vector2 previousPosition = Position;
+
+            // Base velocity is a combination of horizontal movement control and
+            // acceleration downward due to gravity.
+            velocity.X += movement * MoveAcceleration * elapsed;
+            velocity.Y = MathHelper.Clamp(velocity.Y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed);
+
+            velocity.Y = DoJump(velocity.Y, gameTime);
+
+            // Apply pseudo-drag horizontally.
+            if (IsOnGround)
+                velocity.X *= GroundDragFactor;
+            else
+                velocity.X *= AirDragFactor;
+
+            // Prevent the player from running faster than his top speed.            
+            velocity.X = MathHelper.Clamp(velocity.X, -MaxMoveSpeed, MaxMoveSpeed);
+
+            // Apply velocity.
+            Position += velocity * elapsed;
+            Position = new Vector2((float)Math.Round(Position.X), (float)Math.Round(Position.Y));
+
+            // If the player is now colliding with the level, separate them.
+            HandleCollisions();
+
+            // If the collision stopped us from moving, reset the velocity to zero.
+            if (Position.X == previousPosition.X)
+                velocity.X = 0;
+
+            if (Position.Y == previousPosition.Y)
+                velocity.Y = 0;
+        }
+
+        /// <summary>
+        /// Calculates the Y velocity accounting for jumping and
+        /// animates accordingly.
+        /// </summary>
+        /// <remarks>
+        /// During the accent of a jump, the Y velocity is completely
+        /// overridden by a power curve. During the decent, gravity takes
+        /// over. The jump velocity is controlled by the jumpTime field
+        /// which measures time into the accent of the current jump.
+        /// </remarks>
+        /// <param name="velocityY">
+        /// The player's current velocity along the Y axis.
+        /// </param>
+        /// <returns>
+        /// A new Y velocity if beginning or continuing a jump.
+        /// Otherwise, the existing Y velocity.
+        /// </returns>
+        private float DoJump(float velocityY, GameTime gameTime)
+        {
+            // If the player wants to jump
+            if (isJumping)
+            {
+                // Begin or continue a jump
+                if ((!wasJumping && IsOnGround) || jumpTime > 0.0f)
+                {
+                    if (jumpTime == 0.0f)
+                        jumpSound.Play();
+
+                    jumpTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
+                    sprite.PlayAnimation(jumpAnimation);
+                }
+
+                // If we are in the ascent of the jump
+                if (0.0f < jumpTime && jumpTime <= MaxJumpTime)
+                {
+                    // Fully override the vertical velocity with a power curve that gives players more control over the top of the jump
+                    velocityY = JumpLaunchVelocity * (1.0f - (float)Math.Pow(jumpTime / MaxJumpTime, JumpControlPower));
+                }
+                else
+                {
+                    // Reached the apex of the jump
+                    jumpTime = 0.0f;
+                }
+            }
+            else
+            {
+                // Continues not jumping or cancels a jump in progress
+                jumpTime = 0.0f;
+            }
+            wasJumping = isJumping;
+
+            return velocityY;
+        }
+
+        /// <summary>
+        /// Detects and resolves all collisions between the player and his neighboring
+        /// tiles. When a collision is detected, the player is pushed away along one
+        /// axis to prevent overlapping. There is some special logic for the Y axis to
+        /// handle platforms which behave differently depending on direction of movement.
+        /// </summary>
+        private void HandleCollisions()
+        {
+            // Get the player's bounding rectangle and find neighboring tiles.
+            Rectangle bounds = BoundingRectangle;
+            int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
+            int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
+            int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
+            int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;
+
+            // Reset flag to search for ground collision.
+            isOnGround = false;
+
+            // For each potentially colliding tile,
+            for (int y = topTile; y <= bottomTile; ++y)
+            {
+                for (int x = leftTile; x <= rightTile; ++x)
+                {
+                    // If this tile is collidable,
+                    TileCollision collision = Level.GetCollision(x, y);
+                    if (collision != TileCollision.Passable)
+                    {
+                        // Determine collision depth (with direction) and magnitude.
+                        Rectangle tileBounds = Level.GetBounds(x, y);
+                        Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
+                        if (depth != Vector2.Zero)
+                        {
+                            float absDepthX = Math.Abs(depth.X);
+                            float absDepthY = Math.Abs(depth.Y);
+
+                            // Resolve the collision along the shallow axis.
+                            if (absDepthY < absDepthX || collision == TileCollision.Platform)
+                            {
+                                // If we crossed the top of a tile, we are on the ground.
+                                if (previousBottom <= tileBounds.Top)
+                                    isOnGround = true;
+
+                                // Ignore platforms, unless we are on the ground.
+                                if (collision == TileCollision.Impassable || IsOnGround)
+                                {
+                                    // Resolve the collision along the Y axis.
+                                    Position = new Vector2(Position.X, Position.Y + depth.Y);
+
+                                    // Perform further collisions with the new bounds.
+                                    bounds = BoundingRectangle;
+                                }
+                            }
+                            else if (collision == TileCollision.Impassable) // Ignore platforms.
+                            {
+                                // Resolve the collision along the X axis.
+                                Position = new Vector2(Position.X + depth.X, Position.Y);
+
+                                // Perform further collisions with the new bounds.
+                                bounds = BoundingRectangle;
+                            }
+                        }
+                    }
+                }
+            }
+
+            // Save the new bounds bottom.
+            previousBottom = bounds.Bottom;
+        }
+
+        /// <summary>
+        /// Called when the player has been killed.
+        /// </summary>
+        /// <param name="killedBy">
+        /// The enemy who killed the player. This parameter is null if the player was
+        /// not killed by an enemy (fell into a hole).
+        /// </param>
+        public void OnKilled(Enemy killedBy)
+        {
+            isAlive = false;
+
+            if (killedBy != null)
+                killedSound.Play();
+            else
+                fallSound.Play();
+
+            sprite.PlayAnimation(dieAnimation);
+        }
+
+        /// <summary>
+        /// Called when this player reaches the level's exit.
+        /// </summary>
+        public void OnReachedExit()
+        {
+            sprite.PlayAnimation(celebrateAnimation);
+        }
+
+        /// <summary>
+        /// Draws the animated player.
+        /// </summary>
+        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+        {
+            // Flip the sprite to face the way we are moving.
+            if (Velocity.X > 0)
+                flip = SpriteEffects.FlipHorizontally;
+            else if (Velocity.X < 0)
+                flip = SpriteEffects.None;
+
+            // Draw that sprite.
+            sprite.Draw(gameTime, spriteBatch, Position, flip);
+        }
+    }
+}

+ 6 - 0
StarterKits/Android/Platformer/Properties/AppManifest.xml

@@ -0,0 +1,6 @@
+<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+>
+    <Deployment.Parts>
+    </Deployment.Parts>
+</Deployment>

+ 34 - 0
StarterKits/Android/Platformer/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("Platformer")]
+[assembly: AssemblyProduct("Platformer")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
+[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. Only Windows
+// assemblies support COM.
+[assembly: ComVisible(false)]
+
+// On Windows, the following GUID is for the ID of the typelib if this
+// project is exposed to COM. On other platforms, it unique identifies the
+// title storage container when deploying this assembly to the device.
+[assembly: Guid("e43a8e48-5f7f-4c98-9c2e-57d13f38c7c1")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]

+ 32 - 0
StarterKits/Android/Platformer/Properties/WMAppManifest.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2009/deployment" AppPlatformVersion="7.0">
+  <App xmlns="" ProductID="{e43a8e48-5f7f-4c98-9c2e-57d13f38c7c1}" Title="Platformer4" RuntimeType="XNA" Version="1.0.0.0" Genre="Apps.Normal" Author="" Description="" Publisher="">
+    <IconPath IsRelative="true" IsResource="false"></IconPath>
+    <Capabilities>
+      <Capability Name="ID_CAP_NETWORKING" />
+      <Capability Name="ID_CAP_LOCATION" />
+      <Capability Name="ID_CAP_SENSORS" />
+      <Capability Name="ID_CAP_MICROPHONE" />
+      <Capability Name="ID_CAP_MEDIALIB" />
+      <Capability Name="ID_CAP_GAMERSERVICES" />
+      <Capability Name="ID_CAP_PHONEDIALER" />
+      <Capability Name="ID_CAP_PUSH_NOTIFICATION" />
+      <Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
+      <Capability Name="ID_CAP_IDENTITY_USER" />
+      <Capability Name="ID_CAP_IDENTITY_DEVICE" />
+    </Capabilities>
+    <Tasks>
+      <DefaultTask Name="_default"/>
+    </Tasks>
+    <Tokens>
+      <PrimaryToken TokenID="Platformer4Token" TaskName="_default">
+        <TemplateType5>
+          <BackgroundImageURI IsRelative="true" IsResource="false"></BackgroundImageURI>
+          <Count>0</Count>
+          <Title></Title>
+        </TemplateType5>
+      </PrimaryToken>
+    </Tokens>
+  </App>
+</Deployment>
+

+ 66 - 0
StarterKits/Android/Platformer/RectangleExtensions.cs

@@ -0,0 +1,66 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// RectangleExtensions.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using Microsoft.Xna.Framework;
+
+namespace Platformer
+{
+    /// <summary>
+    /// A set of helpful methods for working with rectangles.
+    /// </summary>
+    public static class RectangleExtensions
+    {
+        /// <summary>
+        /// Calculates the signed depth of intersection between two rectangles.
+        /// </summary>
+        /// <returns>
+        /// The amount of overlap between two intersecting rectangles. These
+        /// depth values can be negative depending on which wides the rectangles
+        /// intersect. This allows callers to determine the correct direction
+        /// to push objects in order to resolve collisions.
+        /// If the rectangles are not intersecting, Vector2.Zero is returned.
+        /// </returns>
+        public static Vector2 GetIntersectionDepth(this Rectangle rectA, Rectangle rectB)
+        {
+            // Calculate half sizes.
+            float halfWidthA = rectA.Width / 2.0f;
+            float halfHeightA = rectA.Height / 2.0f;
+            float halfWidthB = rectB.Width / 2.0f;
+            float halfHeightB = rectB.Height / 2.0f;
+
+            // Calculate centers.
+            Vector2 centerA = new Vector2(rectA.Left + halfWidthA, rectA.Top + halfHeightA);
+            Vector2 centerB = new Vector2(rectB.Left + halfWidthB, rectB.Top + halfHeightB);
+
+            // Calculate current and minimum-non-intersecting distances between centers.
+            float distanceX = centerA.X - centerB.X;
+            float distanceY = centerA.Y - centerB.Y;
+            float minDistanceX = halfWidthA + halfWidthB;
+            float minDistanceY = halfHeightA + halfHeightB;
+
+            // If we are not intersecting at all, return (0, 0).
+            if (Math.Abs(distanceX) >= minDistanceX || Math.Abs(distanceY) >= minDistanceY)
+                return Vector2.Zero;
+
+            // Calculate and return intersection depths.
+            float depthX = distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX;
+            float depthY = distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY;
+            return new Vector2(depthX, depthY);
+        }
+
+        /// <summary>
+        /// Gets the position of the center of the bottom edge of the rectangle.
+        /// </summary>
+        public static Vector2 GetBottomCenter(this Rectangle rect)
+        {
+            return new Vector2(rect.X + rect.Width / 2.0f, rect.Bottom);
+        }
+    }
+}

+ 44 - 0
StarterKits/Android/Platformer/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/Platformer/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 PlatformerTwo
+{
+	
+	
+	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/Platformer/Resources/drawable/Icon.png


+ 8 - 0
StarterKits/Android/Platformer/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/Platformer/Resources/values/Strings.xml

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

+ 62 - 0
StarterKits/Android/Platformer/Tile.cs

@@ -0,0 +1,62 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Tile.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Platformer
+{
+    /// <summary>
+    /// Controls the collision detection and response behavior of a tile.
+    /// </summary>
+    enum TileCollision
+    {
+        /// <summary>
+        /// A passable tile is one which does not hinder player motion at all.
+        /// </summary>
+        Passable = 0,
+
+        /// <summary>
+        /// An impassable tile is one which does not allow the player to move through
+        /// it at all. It is completely solid.
+        /// </summary>
+        Impassable = 1,
+
+        /// <summary>
+        /// A platform tile is one which behaves like a passable tile except when the
+        /// player is above it. A player can jump up through a platform as well as move
+        /// past it to the left and right, but can not fall down through the top of it.
+        /// </summary>
+        Platform = 2,
+    }
+
+    /// <summary>
+    /// Stores the appearance and collision behavior of a tile.
+    /// </summary>
+    struct Tile
+    {
+        public Texture2D Texture;
+        public TileCollision Collision;
+
+        public const int Width = 40;
+        public const int Height = 32;
+
+        public static readonly Vector2 Size = new Vector2(Width, Height);
+
+        /// <summary>
+        /// Constructs a new tile.
+        /// </summary>
+        public Tile(Texture2D texture, TileCollision collision)
+        {
+            Texture = texture;
+            Collision = collision;
+        }
+    }
+}

+ 36 - 0
StarterKits/Android/Platformer/TouchCollectionExtensions.cs

@@ -0,0 +1,36 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// TouchCollectionExtensions.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+using Microsoft.Xna.Framework.Input.Touch;
+
+namespace Platformer
+{
+    /// <summary>
+    /// Provides extension methods for the TouchCollection type.
+    /// </summary>
+    public static class TouchCollectionExtensions
+    {
+        /// <summary>
+        /// Determines if there are any touches on the screen.
+        /// </summary>
+        /// <param name="touchState">The current TouchCollection.</param>
+        /// <returns>True if there are any touches in the Pressed or Moved state, false otherwise</returns>
+        public static bool AnyTouch(this TouchCollection touchState)
+        {
+            foreach (TouchLocation location in touchState)
+            {
+                if (location.State == TouchLocationState.Pressed || location.State == TouchLocationState.Moved)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+}