CS 489.02 - Computers and Games - Spring 2009
XNA Pick up the Peanuts (2D)


Loyola College > Department of Computer Science > Dr. James Glenn > CS 489.02 > Examples and Lecture Notes > XNA Pick up the Peanuts (2D)

Elephant.cs

using System;
using Microsoft.Xna.Framework.Graphics;

/**
 * A human player in Pick up the Peanuts.
 *
 * @author Jim Glenn
 * @version 0.1 1/21/2009
 */

namespace WindowsGame1
{
    public class Elephant : Sprite
    {
        /**
         * The size of an elephant.
         */

        public const double DefaultSize = 0.025;

        /**
         * The number of peanuts this elephant has eaten.
         */

        private int peanutsEaten;

        /**
         * The maximum number of peanuts an elephant can eat before...
         */

        private const int maxPeanuts = 4;

        /**
         * The state elephants are in when they've eaten too many peanuts.
         */

        public const int StateFull = 1;

        /**
         * The amount of time it takes for an elephant to recover after
         * eating too many peanuts.
         */
        
        public const double RecoveryTime = 2.0;

        /**
         * The amount of time it takes from eating the last peanut to digging.
         */

        private const double lastPeanutTime = 1.0;

        /**
         * Creates a new elephant at the given position.
         * The elephant will be gray.
         *
         * @param initX an x-coordinate within the world this elephant will be in
         * @param initY a y-coordinate within the world this elephant will be in
         */

        public Elephant(double initX, double initY)
            : base(initX, initY, DefaultSize, Color.Gray)
        {
            peanutsEaten = 0;
        }

        /**
         * Returns the maximum speed of this elephant.
         *
         * @return the speed
         */

        public override double GetMaximumSpeed()
        {
            return 0.2;
        }

        /**
         * Updates this elephant.
         *
         * @param t the time since the last update
         * @param m the model this player belongs to
         */

        public override void Update(double t, GameModel m)
        {
            if (GetState() != StateFull)
            {
                double peanutX = (m as PeanutsModel).GetPeanut().GetX();
                double peanutY = (m as PeanutsModel).GetPeanut().GetY();

                // get direction to peanut

                double dx = peanutX - GetX();
                double dy = peanutY - GetY();

                // change to one of 8 compass directions

                if (Math.Abs(dx) < GetBoundingRadius() / 4)
                {
                    dx = 0;
                }
                else
                {
                    dx = Math.Sign(dx);
                }

                if (Math.Abs(dy) < GetBoundingRadius() / 4)
                {
                    dy = 0;
                }
                else
                {
                    dy = Math.Sign(dy);
                }

                SetVelocity(dx * GetMaximumSpeed(), dy * GetMaximumSpeed());

                // check for too many peanuts

                if (peanutsEaten >= maxPeanuts && GetStateTime() > lastPeanutTime)
                {
                    SetState(StateFull);
                }
            }
            else
            {
                // elephant is...busy

                SetVelocity(0, 0);

                if (GetStateTime() > RecoveryTime)
                {
                    SetState(StateNormal);
                    m.AddSprite(new Hole(GetX(), GetY()));
                    peanutsEaten = 0;
                }
            }

            base.Update(t, m);
        }

        /**
         * Gives this elephant a peanut.
         */

        public void EatPeanut()
        {
            peanutsEaten++;
            SetState(StateNormal);
        }

        public override void GetOutline()
        {
        }

    }
}

Game1.cs

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace WindowsGame1
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private PeanutsModel model;
        private PeanutsView view;
        private Texture2D uniformTexture;
        private SpriteFont font;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            model = new PeanutsModel();
            view = new PeanutsView(model);

            base.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);

            uniformTexture = Content.Load<Texture2D>("blank");
            font = Content.Load<SpriteFont>("Arial");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <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)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // read user input

            KeyboardState kb = Keyboard.GetState();
            int dx = 0;
            int dy = 0;
            if (kb.IsKeyDown(Keys.Up))
            {
                   dy = -1;
            }
            if (kb.IsKeyDown(Keys.Down))
            {
                dy = 1;
            }
            if (kb.IsKeyDown(Keys.Left))
            {
                   dx = -1;
            }
            if (kb.IsKeyDown(Keys.Right))
            {
                dx = 1;
            }
            model.MovePlayer(dx, dy);

            // update the model

            model.Update();

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
            spriteBatch.Begin();
            view.Draw(graphics, spriteBatch, uniformTexture, font);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

GameModel.cs

using System;
using System.Collections.Generic;

/**
 * The current state of a game.
 *
 * @author Jim Glenn
 * @version 0.2 2/10/2009 C# version (still needs XML comments)
 * @version 0.1 1/19/2009 from LiberationGame of 1/8/2009
 */

namespace WindowsGame1
{
    public class GameModel
    {
        /**
         * The list of sprites in this game.
         */

        private IList<Sprite> sprites;

        /**
         * The list of sprites to be removed at the next update.  This is
         * necessary because as we iterate through sprites they may want to
         * remove other sprites, but we can't modify the list of sprites
         * as we iterate through it.
         */

        private IDictionary<Sprite, Sprite> toRemove;

        /**
         * The list of sprites to be added.
         */

        private IList<Sprite> toAdd;

        /**
         * The time of the last update to this game.
         */

        private long lastUpdate;

        /**
         * The number of milliseconds in a second.
         */

        private const double TicksPerSecond = 10000000.0;

        /**
         * Creates a new game.
         */

        public GameModel()
        {
            sprites = new List<Sprite>();
            toRemove = new Dictionary<Sprite, Sprite>();
            toAdd = new List<Sprite>();

            lastUpdate = -1;
        }

        /**
         * Returns a list of the active sprites in this game.
         *
         * @return a list of sprites in this game
         */

        public virtual IList<Sprite> GetSprites()
        {
            IList<Sprite> l = new List<Sprite>(sprites);

            foreach (Sprite s in sprites)
            {
                if (!toRemove.ContainsKey(s))
                {
                    l.Add(s);
                }
            }

            return l;
        }

        /**
         * Adds the given sprite to this game.
         *
         * @param s the sprite to add
         */

        public virtual void AddSprite(Sprite s)
        {
            toAdd.Add(s);
        }

        /**
         * Removes the given sprite from this game.
         *
         * @param s the sprite to remove
         */

        public virtual void RemoveSprite(Sprite s)
        {
            toRemove.Add(s, s);
        }

        /**
         * Returns the width of the playable area of this game.
         *
         * @return the width of this game
         */

        public virtual double GetWidth()
        {
            return 1.0;
        }

        /**
         * Returns the height of the playable area of this game.
         *
         * @return the height of this game
         */

        public virtual double GetHeight()
        {
            return 1.0;
        }

        /**
         * Removes sprites that are currently on the to-be-removed list.
         */

        private void ProcessRemovedSprites()
        {
            foreach (Sprite s in toRemove.Keys)
            {
                sprites.Remove(s);
            }

            toRemove.Clear();
        }

        /**
         * Removes sprites that are currently on the to-be-added list.
         */

        private void ProcessAddedSprites()
        {
            foreach (Sprite s in toAdd)
            {
                sprites.Add(s);
            }

            toAdd.Clear();
        }

        /**
         * Updates all the sprites in this world.
         */

        public virtual void Update()
        {
            // update sprites

            long currTime = DateTime.Now.Ticks;

            if (lastUpdate != -1)
            {
                foreach (Sprite s in sprites)
                {
                    s.Update((currTime - lastUpdate) / TicksPerSecond,
                         this);
                }
            }

            lastUpdate = currTime;

            // check for and handle collisions

            CheckCollisions();

            // process removed and added sprites

            ProcessRemovedSprites();
            ProcessAddedSprites();

            // notify view that is presumably waiting for updates from this model

            // SetChanged();
            // NotifyObservers();
        }

        /**
         * Checks for and handles collisions between sprites.  Collision
         * handling between a pair of sprites is delegated to the
         * <CODE>handleCollisions</CODE> method; subclasses should
         * override that method for actions appropriate to the particular
         * game.
         */

        protected virtual void CheckCollisions()
        {
            // iterate over the n(n-1)/2 distinct pairs of sprites

            for (int i = 0; i < sprites.Count; i++)
            {
                Sprite s1 = sprites[i];

                if (!toRemove.ContainsKey(s1))
                {
                    for (int j = i + 1; j < sprites.Count; j++)
                    {
                        Sprite s2 = sprites[j];

                        if (!toRemove.ContainsKey(s2)
                            && Close(s1, s2)
                            && s1.CollidesWith(s2))
                        {
                            HandleCollision(s1, s2);
                        }
                    }
                }
            }
        }

        /**
         * Checks if the two given sprites are close enough to collide.
         * This check is performed using the bounding radius: if the
         * distance between the control points of the sprites is less than
         * the sum of their radii then the sprites are close enough to
         * collide.  Because this is a very rough collision check, false
         * positives are possible: the return value may be <CODE>false</CODE>
         * even though the sprites are not in collision.  Therefore
         * a more precise collision test is necessary.
         * 
         *
         * @param s1 a sprite
         * @param s2 a sprite
         * @return false if the sprites are not in collision
         */

        private bool Close(Sprite s1, Sprite s2)
        {
            double sumRadii = s1.GetBoundingRadius() + s2.GetBoundingRadius();

            return (Math.Sqrt(Math.Pow(s1.GetX() - s2.GetX(), 2)
                      + Math.Pow(s1.GetY() - s2.GetY(), 2)) < sumRadii);
        }

        /**
         * Handles a collision between the given sprites.  This implementation
         * does nothing; subclasses should override this method to implement
         * the rules of a game.
         *
         * @param s1 a sprite
         * @param s2 a sprite that has collided with <CODE>s2</CODE>
         */

        protected virtual void HandleCollision(Sprite s1, Sprite s2)
        {
            Console.WriteLine("Oof: " + s1 + " " + s2);
        }
    }
}

Hole.cs

using Microsoft.Xna.Framework.Graphics;

/**
 * A hole dug by the elephant in Pick up the Peanuts.
 *
 * @author Jim Glenn
 * @version 0.1 1/21/2009
 */
namespace WindowsGame1
{
    public class Hole : Sprite
    {
        /**
         * The default size of a hole.
         */

        public const double DefaultSize = 0.01;

        /**
         * Creates a new hole at the given position.  The hole will be black.
         *
         * @param initX an x-coordinate within the world this hole will be in
         * @param initY a y-coordinate within the world this hole will be in
         */


        public Hole(double initX, double initY)
            : base(initX, initY, DefaultSize, Color.Black)
        {
        }
    }
}

Peanut.cs

using Microsoft.Xna.Framework.Graphics;

/**
 * A peanut in Pick up the Peanuts.
 *
 * @author Jim Glenn
 * @version 0.1 1/20/2009
 */

namespace WindowsGame1
{
    public class Peanut : Sprite
    {
        /**
         * The size of a peanut.
         */

        public const double DefaultSize = 0.01;

        /**
         * Creates a new peanut at the given position.
         * The peanut will be red.
         *
         * @param initX an x-coordinate within the world this peanut will be in
         * @param initY a y-coordinate within the world this peanut will be in
         */

        public Peanut(double initX, double initY)
            : base(initX, initY, DefaultSize, Color.Orange)
        {
        }

        /**
         * Returns the value of this peanut when eaten by the player.
         *
         * @return the value of this peanut
         */

        public int GetValue()
        {
            if (GetStateTime() < 2.0)
            {
                return 50;
            }
            else if (GetStateTime() < 4.0)
            {
                return 20;
            }
            else
            {
                return 10;
            }
        }
    }
}

PeanutsModel.cs

using System;

/**
 * A game of Pick up the Peanuts.  Pick up the Peanuts has a player
 * and an elephant who race to pick up peanuts.
 *
 * @author Jim Glenn
 * @version 2K9.02 translated to C# (still needs XML comments
 * @version 2K9 translated to Java
 * @version 1.0 1982 or so in Atari BASIC
 */

namespace WindowsGame1
{
    public class PeanutsModel : GameModel
    {
        /**
         * The objects in this game.  Distinguished objects (OK, all of them)
         * are kept separately from the list for easy access.
         */

        private Player player;
        private Elephant elephant;
        private Peanut peanut;

        private const long ticksPerSecond = 10000000;

        /**
         * The amount of time this game has been going, in seconds.
         */

        private long startTime;

        /**
         * The length of a game, in milliseconds.
         */

        private const long gameLength = 180 * ticksPerSecond;

        /**
         * The player's score in this game.
         */

        private int score;

        private Random rand;

        /**
         * Creates a new Pick up the Peanut game with player and elephant
         * at their starting positions (left and right respectively)
         * and a randomly placed peanut.
         */

        public PeanutsModel()
        {
            // create a random number generator

            rand = new Random();

            // create player near middle of left edge
            player = new Player(0.1, 0.5);
            AddSprite(player);

            // create elephant near middle of right edge

            elephant = new Elephant(0.9, 0.5);
            AddSprite(elephant);

            // throw a peanut at a random location

            MakePeanut();

            // initialize player's score

            score = 0;

            // record start time

            startTime = DateTime.Now.Ticks;
        }

        /**
         * Returns the player's score in this game.
         *
         * @return the player's score
         */

        public int GetPlayerScore()
        {
            return score;
        }

        /**
         * Returns the current peanut in this game.
         *
         * @return the peanut
         */

        public Peanut GetPeanut()
        {
            return peanut;
        }

        /**
         * Forwards move events to the player.  The direction is given
         * as an x- and a y-component.
         *
         * @param dx -1, 0, or 1
         * @param dy -1, 0, or 1
         */

        public void MovePlayer(int dx, int dy)
        {
            player.Move(dx, dy);
        }

        /**
         * Places a new peanut randomly in this game.
         */

        private void MakePeanut()
        {
            if (peanut != null)
            {
                RemoveSprite(peanut);
            }

            peanut = new Peanut(rand.NextDouble(), rand.NextDouble());
            AddSprite(peanut);
        }

        /**
         * Updates this game.
         */

        public override void Update()
        {
            if (DateTime.Now.Ticks - startTime < gameLength)
            {
                base.Update();
            }
        }

        /**
         * Returns the amount of time left in this game, in seconds.
         *
         * @return the time left
         */

        public int GetTimeLeft()
        {
            return (int)(gameLength - (DateTime.Now.Ticks - startTime) / ticksPerSecond);
        }


        /**
         * Handles collisions between the given objects.
         *
         * @param s1 a sprite
         * @param s2 a sprite that has collided with s1
         */

        protected override void HandleCollision(Sprite s1, Sprite s2)
        {
            // do nothing when something collides with a score

            if (s1 is ValueDisplay || s2 is ValueDisplay)
            {
                return;
            }

            if (s1 is Peanut || s2 is Peanut)
            {
                // swap to make s1 the one that ate the peanut

                if (s1 is Peanut)
                {
                    Sprite temp = s1;
                    s1 = s2;
                    s2 = temp;
                }

                if (s1 is Player)
                {
                    // player ate the peanut

                    Peanut p = s2 as Peanut;

                    int value = p.GetValue();

                    score += value;
                    AddSprite(new ValueDisplay(p.GetX(), p.GetY(), value));
                }
                else if (s1 is Elephant)
                {
                    // elephant ate the peanut

                    (s1 as Elephant).EatPeanut();
                }
                else
                {
                    // eww!
                }

                // make a new peanut

                MakePeanut();
            }
            else if (s1 is Elephant && s2 is Player
                 || s1 is Player && s2 is Elephant)
            {
                // elephant tramples player

                player.Trample();
            }
            else if (s1 is Player && s2 is Hole)
            {
                // player steps in hole

                player.StepInHole();
                RemoveSprite(s2);
            }
            else if (s1 is Hole && s2 is Player)
            {
                // hole steps on player

                player.StepInHole();
                RemoveSprite(s1);
            }
        }
    }

}

PeanutsView.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace WindowsGame1
{
    public class PeanutsView
    {
        private PeanutsModel model;

        public PeanutsView(PeanutsModel m)
        {
            model = m;
        }

        public void Draw(GraphicsDeviceManager graphics, SpriteBatch spriteBatch, Texture2D texture, SpriteFont font)
        {
            IList<Sprite> sprites = model.GetSprites();

            foreach (Sprite s in sprites)
            {
                int scale = Math.Min(graphics.GraphicsDevice.Viewport.Height, graphics.GraphicsDevice.Viewport.Width);
                int left = (int)((s.GetX() - s.GetBoundingRadius()) * scale);
                int top = (int)((s.GetY() - s.GetBoundingRadius()) * scale);
                int size = (int)(s.GetBoundingRadius() * scale);

              
                spriteBatch.Draw(texture, new Rectangle(left, top, size, size), s.GetColor());
            }
        }
    }
}

Player.cs

using Microsoft.Xna.Framework.Graphics;

/**
 * A human player in Pick up the Peanuts.
 *
 * @author Jim Glenn
 * @version 0.2 2/10/2009 C# version (still needs XML comments)
 * @version 0.1 1/21/2009
 */

namespace WindowsGame1
{
    public class Player : Sprite
    {
        /**
         * The size of the human player.
         */

        public const double DefaultSize = 0.025;

        /**
         * The amount of time, in seconds, that this player takes to recover
         * from being trampled.
         */

        private const double RecoveryTime = 0.5;

        /**
         * The amount of time, in seconds, that this player takes to climb
         * out of an elephant hole.
         */

        private const double ClimbingTime = 0.2;

        /**
         * Constants for state.
         */

        private const int stateTrampled = 1;

        /**
         * The direction this player will go when it recovers from being trampled.
         */

        private int recoverDx;
        private int recoverDy;

        /**
         * The amount of time it will take this player to recover from
         * whatever mishap has befalled it.
         */

        private double recoveryTime;

        /**
         * Creates a new human player at the given position.
         * The player will be red.
         *
         * @param initX an x-coordinate within the world this playwe will be in
         * @param initY a y-coordinate within the world this Player will be in
         */

        public Player(double initX, double initY)
            : base(initX, initY, DefaultSize, Color.Red)
        {
        }

        /**
         * Returns the maximum speed of this player.
         *
         * @return the speed
         */

        public override double GetMaximumSpeed()
        {
            return 0.2;
        }

        /**
         * Changes the velocity of this player so that it is moving in
         * the given direction.  Its speed will be set to the maximum.
         *
         * @param dx -1, 0, or 1
         * @param dy -1, 0, or 1
         */

        public void Move(int dx, int dy)
        {
            if (GetState() != stateTrampled)
            {
                SetVelocity(GetMaximumSpeed() * dx, GetMaximumSpeed() * dy);
            }
            else
            {
                recoverDx = dx;
                recoverDy = dy;
            }
        }

        /**
         * Tramples this player.  This player will not be able to move until
         * it recovers.
         */

        public void Trample()
        {
            if (GetState() != stateTrampled)
            {
                SetState(stateTrampled);
                recoverDx = 0;
                recoverDy = 0;
                recoveryTime = RecoveryTime;
            }
        }

        /**
         * Puts the player in a hole.  The player will not be able
         * to move until it climbs out.
         */

        public void StepInHole()
        {
            // treat the same as a trampling, but with a different recovery time

            if (GetState() != stateTrampled)
            {
                SetState(stateTrampled);
                recoverDx = 0;
                recoverDy = 0;
                recoveryTime = ClimbingTime;
            }
        }

        /**
         * Updates this player.
         *
         * @param t the time since the last update
         * @param m the model this player belongs to
         */

        public override void Update(double t, GameModel m)
        {
            if (GetState() == stateTrampled)
            {
                if (GetStateTime() > recoveryTime)
                {
                    SetState(StateNormal);
                    SetVelocity(recoverDx, recoverDy);
                }
                else
                {
                    SetVelocity(0, 0);
                }
            }

            base.Update(t, m);

            // do wraparound

            if (x < GetBoundingRadius() / 2)
            {
                x = m.GetWidth() - GetBoundingRadius() / 2;
            }
            else if (x + GetBoundingRadius() / 2 > m.GetWidth())
            {
                x = GetBoundingRadius() / 2;
            }

            if (y < GetBoundingRadius() / 2)
            {
                y = m.GetHeight() - GetBoundingRadius() / 2;
            }
            else if (y + GetBoundingRadius() / 2 > m.GetHeight())
            {
                y = GetBoundingRadius() / 2;
            }
        }
    }

}

Program.cs

using System;

namespace WindowsGame1
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main(string[] args)
        {
            using (Game1 game = new Game1())
            {
                game.Run();
            }
        }
    }
}

Sprite.cs

using System;
using Microsoft.Xna.Framework.Graphics;

/**
 * A moving object in a game.  Sprites have positions, velocities, and
 * sizes.  The size is given as the radius of a circle that surrounds the
 * sprite.  Such a size is intended only for an approximate determination
 * if two sprites collide.
 *
 * @author Jim Glenn
 * @version 0.1 1/19/2009 from Liberation's Sprite.java of 1/8/2009
 */

namespace WindowsGame1
{
    public class Sprite
    {
        /**
         * The x-coordinate of this sprite.
         */

        protected double x;

        /**
         * The y-coordinate of this sprite.
         */

        protected double y;

        /**
         * The x-component of this sprite's velocity.
         */

        protected double vx;

        /**
         * The y-component of this sprite's velocity.
         */

        protected double vy;

        /**
         * The size of a circle that encloses this sprite.
         */

        protected double radius;

        /**
         * The color of this sprite.
         */

        protected Color color;

        /**
         * The state of this sprite.
         */

        protected int state;

        /**
         * The normal state of a sprite.  This constant is defined to be 0.
         */

        public const int StateNormal = 0;

        /**
         * The amount of time this sprite has spent in its current state.
         * Measured in seconds.
         */

        private double stateTime;

        /**
         * Creates a new sprite at the given position.
         * The sprite will be black and stationary.
         *
         * @param initX the x-coordinate of the new sprite
         * @param initY the y-coordinate of the new sprite
         * @param r the radius of a circle that bounds the new sprite
         */

        public Sprite(double initX, double initY, double r)
            : this(initX, initY, r, 0.0, 0.0, Color.Black)
        {
        }

        /**
         * Creates a new sprite at the given position.
         * The sprite will be stationary.
         *
         * @param initX the x-coordinate of the new sprite
         * @param initY the y-coordinate of the new sprite
         * @param r the radius of a circle that bounds the new sprite
         * @param c the color of the new sprite
         */

        public Sprite(double initX, double initY, double r, Color c)
            : this(initX, initY, r, 0.0, 0.0, c)
        {
        }

        /**
         * Creates a new sprite at the given position.
         *
         * @param initX the x-coordinate of the new sprite
         * @param initY the y-coordinate of the new sprite
         * @param r the radius of a circle that bounds the new sprite
         * @param initVx the x-component of the velocity of the new sprite
         * @param initVy the y-component of the velocity of the new sprite
         * @param c the color of the new sprite
         */

        public Sprite(double initX, double initY, double r, double initVx, double initVy, Color c)
        {
            x = initX;
            y = initY;
            radius = r;
            vx = initVx;
            vy = initVy;
            color = c;
            state = StateNormal;
            stateTime = 0.0;
        }

        /**
         * Returns the x-coordinate of this sprite.
         *
         * @return the x-coordinate of this sprite
         */

        public double GetX()
        {
            return x;
        }

        /**
         * Returns the y-coordinate of this sprite.
         *
         * @return the y-coordinate of this sprite
         */

        public double GetY()
        {
            return y;
        }

        /**
         * Returns the color of this sprite.
         *
         * @return the color of this sprite
         */

        public virtual Color GetColor()
        {
            return color;
        }

        /**
         * Returns the maximum speed of this sprite.  This implementation
         * returns positive infinity, indicating no limit.  Subclasses should
         * override this method so that <CODE>update</CODE> will take
         * a maximum speed into account.
         */

        public virtual double GetMaximumSpeed()
        {
            return Double.PositiveInfinity;
        }

        /**
         * Sets the velocity of this sprite.  If the given velocity exceeds the
         * maximum, it will be reduced so that it equals the maximum.  In such
         * a case the direction will remain the same.
         *
         * @param vx the new velocity in the horizontal direction
         * @param vy the new velocity in the vertical direction
         */

        public void SetVelocity(double vxNew, double vyNew)
        {
            vx = vxNew;
            vy = vyNew;

            // limit speed

            double v = Math.Sqrt(vx * vx + vy * vy);
            double maxV = GetMaximumSpeed();

            if (v > maxV)
            {
                vx /= v / maxV;
                vy /= v / maxV;
            }
        }

        /**
         * Returns the radius of a circle that encloses this sprite.
         *
         * @return the bounding radius of this sprite
         */

        public double GetBoundingRadius()
        {
            return radius;
        }

        /**
         * Returns the polygonal outline of this Sprite.  This implementation
         * returns the square inscribed in the bounding circle as defined
         * by <CODE>getBoundingRadius</CODE> and that has sides parallel to the
         * axes of the coordinate system.  Subclasses should override this
         * method for sprites of other shapes.
         *
         * @return the outline of this sprite
         */

        public virtual void GetOutline()
        {
        }

        /**
         * Determines if this sprite collides with the given sprite.
         * Two sprites are considered in collision if their outlines intersect.
         * (Note that this ignores the special case of one sprite completely
         * inside the other.)
         *
         * @return true if and only if the two sprites are in collision
         */

        public bool CollidesWith(Sprite s)
        {
            return false;
        }

        /**
         * Returns the list of points on the outline of this sprite.  The
         * starting point is on the list at the start and at the end.
         * This implementation currently returns an <CODE>ArrayList</CODE>
         * in order to facilitate random access to the points.
         *
         * @return a list of points on the outline of this sprite
         */

        private void GetPointsList()
        {
        }

        /**
         * Updates this sprite's position and velocity.  The velocity will be
         * adjusted so that it does not exceed the maximum speed as specified by
         * the <CODE>getMaximumSpeed</CODE> method.
         *
         * @param t the time since the last update, in seconds
         * @param w the world this sprite belongs to
         */

        public virtual void Update(double t, GameModel m)
        {
            stateTime += t;

            // update position

            x += vx * t;
            y += vy * t;
        }

        /**
         * Returns the state of this sprite.
         *
         * @return the current state
         */

        public int GetState()
        {
            return state;
        }

        /**
         * Sets the state of this sprite.  The state timer is reset to 0.
         *
         * @param s the new state
         */

        public void SetState(int s)
        {
            state = s;
            stateTime = 0.0;
        }

        /**
         * Returns the amount of time this sprite has spent in its current state.
         *
         * @return the time in the current state
         */

        public double GetStateTime()
        {
            return stateTime;
        }
    }

}

ValueDisplay.cs

using Microsoft.Xna.Framework.Graphics;

/**
 * A sprite that shows the value of an eaten peanut.
 *
 * @author Jim Glenn
 * @version 0.2 2/10/2009 translated to C# (still needs XML comments)
 * @version 0.1 1/21/2009
 */
namespace WindowsGame1
{
    public class ValueDisplay : Sprite
    {
        /**
         * The value displayed.
         */

        private int value;

        /**
         * The default size of a display.
         */

        public const double DefaultSize = 0.03;

        /**
         * The default vertical speed of a display.
         */

        public const double DefaultSpeed = -0.03;

        /**
         * The default amount of time a display will exist.
         */

        public const double DefaultTime = 1.5;

        /**
         * Creates a new sprite that displays the given value.
         *
         * @param v the value to display
         */

        public ValueDisplay(double initX, double initY, int v)
            : base(initX, initY, DefaultSize, 0.0, DefaultSpeed, Color.Black)
        {
            value = v;
        }

        /**
         * Returns the value in this display.
         *
         * @return the displayed value
         */

        public int GetValue()
        {
            return value;
        }

        /**
         * Returns the current color of this display.
         *
         * @return the color of this display.
         */

        public override Color GetColor()
        {
            return new Color((byte)(GetStateTime() / DefaultTime * 255),
                            (byte)(GetStateTime() / DefaultTime * 255),
                            (byte)(GetStateTime() / DefaultTime * 255));
        }

        /**
         * Updates this sprite's position and checks for its expiration.
         *
         * @param t the time since the last update, in seconds
         * @param w the world this sprite belongs to
         */

        public override void Update(double t, GameModel m)
        {
            base.Update(t, m);

            // check for expiration

            if (GetStateTime() > DefaultTime)
            {
                m.RemoveSprite(this);
            }
        }
    }
}
This code can also be downloaded from the files
Elephant.cs, Game1.cs, GameModel.cs, Hole.cs, Peanut.cs, PeanutsModel.cs, PeanutsView.cs, Player.cs, Program.cs, Sprite.cs, and ValueDisplay.cs.