Project Description
A bounce ball XNA game Project. Shows that how to build an arkanoid/dx-ball like game in Visual Studio XNA with C#.

The project is written in C# using Windows Phone 7 XNA for educational purposes.
  • Shows how to build a game.
  • Explains the game architecture, objects and game-time.
  • How to use accelerometer in windows phone game.

After implementation you will get the result in windows phone like this picture.
WP_20130329_002.jpg

How to Build Bounce Ball

Resource Content

I have created a brick, bar, and ball for the game in Microsoft Power Point. Also much skilled Graphic Designer could do better than me.
YellowBrick.png
RedBar.png
RedBall.png
GridBlack.jpg

Game Object Definitions

First of all we need an abstract object base, which I called SpriteObject. For each Direct2D object in our game. Each object will derive from this SpriteObject.

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

namespace BounceBall.GameObjects
{
    public abstract class SpriteObject
    {
        /// <summary>
        /// Gets and Sets Texture data of the object
        /// </summary>
        protected Texture2D ObjectTexture;
        /// <summary>
        /// Gets and Sets Object Position
        /// </summary>
        public Vector2 Position;
        /// <summary>
        /// Gets and Sets Object Size Multiplayer
        /// </summary>
        public float Scale = 1;
        /// <summary>
        /// Gets or Sets moving speed of this object
        /// </summary>
        public Vector2 Speed = Vector2.One;
        /// <summary>
        /// Gets or Sets moving acc of this object
        /// </summary>
        public Vector2 Accerelation = Vector2.Zero;

        public void Initialize(Texture2D texture, Vector2 position)
        {
            Position = position;
            ObjectTexture = texture;
        }

        public void Initialize(Texture2D texture)
        {
            ObjectTexture = texture;
        }

        /// <summary>
        /// Object Bounds
        /// </summary>
        public virtual Rectangle ObjectBounds
        {
            get { return new Rectangle((int)this.Position.X, (int)this.Position.Y, (int)(ObjectTexture.Width * this.Scale), (int)(ObjectTexture.Height * this.Scale)); }
        }

        /// <summary>
        /// Object Center
        /// </summary>
        public virtual Vector2 ObjectCenter
        {
            get { return new Vector2((int)Position.X + ScaledWidth / 2, (int)Position.Y + ScaledWidth / 2); }
        }

        /// <summary>
        /// Gets the width of the Object
        /// </summary>
        public int Width
        {
            get { return ObjectTexture.Width; }
        }

        /// <summary>
        /// Gets the height of the Object
        /// </summary>
        public int Height
        {
            get { return ObjectTexture.Height; }
        }

        /// <summary>
        /// Gets the Scaled width of the Object
        /// </summary>
        public int ScaledWidth
        {
            get { return (int)(Width * Scale); }
        }

        /// <summary>
        /// gets the scaled height of the Object
        /// </summary>
        public int ScaledHeight
        {
            get { return (int)(Height * Scale); }
        }

        /// <summary>
        /// Updates the object in the game time
        /// </summary>
        /// <param name="graphics">Graphics Device</param>
        /// <param name="gameTime">Game Time</param>
        internal virtual void Update(GraphicsDeviceManager graphics, GameTime gameTime) { }

        /// <summary>
        /// Draws the object and the texture in the game arena
        /// </summary>
        /// <param name="spriteBatch"></param>
        /// <param name="gameTime"></param>
        internal virtual void Draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
            spriteBatch.Draw(ObjectTexture, Position, null, Color.White, 0, new Vector2(0, 0), Scale, SpriteEffects.None, 0);
        }

        /// <summary>
        /// Swaps Speed and Accerelation on Y direction
        /// </summary>
        protected void SwapSpeedY()
        {
            Speed.Y *= -1;
            Accerelation.Y *= -1;
        }

        /// <summary>
        /// Swaps Speed and Accerelation on X direction
        /// </summary>
        protected void SwapSpeedX()
        {
            Speed.X *= -1;
            Accerelation.X *= -1;
        }
    }
}

Now we can construct from that base the Ball, Brick and the Bar. Also you could extend that sprite object and build many Sprite Object and include into the game. So the world of the game could be enriched.

The Ball
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace BounceBall.GameObjects
{
    public class Ball : SpriteObject
    {
        public event EventHandler BallLost;

        public Ball()
        {
            base.Position = new Vector2(0, 450);
            base.Scale = 0.25F;
        }

        internal override void Update(GraphicsDeviceManager graphics, GameTime gameTime)
        {
            base.Speed += base.Accerelation;
            base.Position += base.Speed * (float)gameTime.ElapsedGameTime.TotalMilliseconds / 10;

            int MaxX = graphics.GraphicsDevice.Viewport.Width - this.ScaledWidth;
            int MinX = 0;
            int MaxY = graphics.GraphicsDevice.Viewport.Height - this.ScaledHeight;
            int MinY = 0;

            // Check for bounce.
            if (base.Position.X > MaxX)
            {
                base.SwapSpeedX();
                base.Position.X = MaxX;
            }
            else if (base.Position.X < MinX)
            {
                base.SwapSpeedX();
                base.Position.X = MinX;
            }

            if (base.Position.Y > MaxY)
            {
                base.SwapSpeedY();
                base.Position.Y = MaxY;
                //if (BallLost != null)
                //    BallLost(this, null);
            }
            else if (base.Position.Y < MinY)
            {
                base.SwapSpeedY();
                base.Position.Y = MinY;
            }

            base.Update(graphics, gameTime);
        }
    }
}

The Bar
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace BounceBall.GameObjects
{
    public class Bar : SpriteObject
    {
        public Bar()
        {
            base.Position = Vector2.Zero;
            base.Scale = 0.5F;
            base.Speed = Vector2.One * 2;
        }

        internal override void Update(GraphicsDeviceManager graphics, GameTime gameTime)
        {
            int MinX = 0;
            int MaxX = graphics.GraphicsDevice.Viewport.Width - ScaledWidth;

            if (Position.X + Speed.X < MaxX && Position.X + Speed.X >= MinX)
                Position.X += Speed.X;

            Position.Y = graphics.GraphicsDevice.Viewport.Height - ScaledHeight - 10;

            base.Update(graphics, gameTime);
        }

        public override Rectangle ObjectBounds
        {
            get { return new Rectangle((int)Position.X, (int)Position.Y, ScaledWidth, 1); }
        }
    }
}

This kind of bricks will know when the ball hits it, with the CheckCollision method with Ball parameter.

The Brick
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace BounceBall.GameObjects
{
    public class Brick : SpriteObject
    {
        public event EventHandler BallCollided;

        public Brick()
        {
            base.Scale = 0.50F;
        }

        public Brick(Vector2 position)
            : this()
        {
            base.Position = position;
        }

        /// <summary>
        /// Checks the brick Collided with a Ball
        /// </summary>
        /// <param name="b">The Ball of the player</param>
        public void CheckCollision(Ball b)
        {
            if (base.ObjectBounds.Intersects(b.ObjectBounds))
            {
                Rectangle BrickRect = base.ObjectBounds;
                Rectangle BallRect = b.ObjectBounds;

                if (BallCollided != null)
                    BallCollided(this, null);

                if (BallRect.Center.X > BrickRect.Left && BallRect.Center.X < BrickRect.Right)
                    b.Speed.Y *= -1;
                else if (BallRect.Center.Y > BrickRect.Top && BallRect.Center.Y < BrickRect.Bottom)
                    b.Speed.X *= -1;
                else
                    b.Speed *= -1;
            }
        }
    }
}
Also we could think the backround of the game as an static object and extend the sprite object to the game background.

The Game Background
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace BounceBall.GameObjects
{
    public class GameBackground : SpriteObject
    {
        public GameBackground()
        {
            this.Position = Vector2.Zero;
        }
    }
}

For creating the levels for the game I had created a level base.

The Level Base
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace BounceBall.GameObjects
{
    public abstract class LevelBase
    {
        /// <summary>
        /// Event of level complete
        /// </summary>
        public event EventHandler OnLevelCompleted;

        /// <summary>
        /// Bricks in this level
        /// </summary>
        public List<Brick> Bricks = new List<Brick>();

        protected bool _isCompleted = false;
        /// <summary>
        /// gets or sets whether the level is completed
        /// </summary>
        public bool IsCompleted
        {
            get { return _isCompleted; }
            set
            {
                _isCompleted = value;

                if (_isCompleted)
                    if (OnLevelCompleted != null)
                        OnLevelCompleted(this, null);
            }
        }

        /// <summary>
        /// inits all the elements in this level
        /// </summary>
        /// <param name="brickTexture"></param>
        internal virtual void Initialize(Texture2D brickTexture)
        {
            foreach (Brick b in Bricks)
            {
                b.Initialize(brickTexture);
                b.BallCollided += new EventHandler(BallCollided);
            }
        }

        protected virtual void BallCollided(object sender, EventArgs e)
        {
            Brick myBrick = sender as Brick;
            if (myBrick != null)
                Bricks.Remove(myBrick);
        }

        /// <summary>
        /// draws all the elements in this level
        /// </summary>
        /// <param name="spriteBatch"></param>
        /// <param name="gameTime"></param>
        internal virtual void Draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
            foreach (Brick b in Bricks)
                b.Draw(spriteBatch, gameTime);
        }

        /// <summary>
        /// Updates all the elements in this level
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="gameTime"></param>
        internal virtual void Update(GraphicsDeviceManager graphics, GameTime gameTime)
        {
            foreach (Brick b in Bricks)
                b.Update(graphics, gameTime);
        }

        /// <summary>
        /// Checks the collision with this brick and the ball
        /// </summary>
        /// <param name="ball">Ball of the current player</param>
        internal virtual void CheckCollision(Ball ball)
        {
            for (int i = 0; i < this.Bricks.Count; i++)
                Bricks[i].CheckCollision(ball);
        }
    }
}

From this level base we create a Level 1. It is a level with filled all with normal bricks, that will explode when the ball hits.

The Level 1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace BounceBall.GameObjects
{
    public class Level1 : LevelBase
    {
        private int _brickStartX = 50;
        private int _brickStartY = 50;
        private int _brickWidth = 100;
        private int _brickHeight = 50;

        public Level1()
        {
            for (int j = 0; j < 6; j++)
                for (int i = 0; i < 7; i++)
                    this.Bricks.Add(new Brick(new Vector2(_brickStartX + i * _brickWidth, _brickStartY + j * _brickHeight)));
        }
    }
}

In the fallowing code I had developed the gaming behavior, character of the player and the game. On the other hand Player Class also manages the game.

The Player
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace BounceBall.GameObjects
{
    public class Player
    {
        public Player() { }

        public Player(string name)
        {
            Name = name;
        }

        /// <summary>
        /// Gets or Sets Name of this Player
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Gets or Sets Score of this player
        /// </summary>
        public int Score { get; set; }

        private Ball _myBall = new Ball();
        /// <summary>
        /// gets the ball of this player
        /// </summary>
        public Ball MyBall
        {
            get { return _myBall; }
        }

        private Bar _myBar = new Bar();

        /// <summary>
        /// draws the players objects
        /// </summary>
        /// <param name="spriteBatch"></param>
        /// <param name="gameTime"></param>
        internal void Draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
            _myBar.Draw(spriteBatch, gameTime);
            _myBall.Draw(spriteBatch, gameTime);
        }

        /// <summary>
        /// Inits the players objects
        /// </summary>
        /// <param name="barTexture">bar Texture</param>
        /// <param name="ballTexture">ball texture</param>
        internal void Initialize(Texture2D barTexture, Texture2D ballTexture)
        {
            _myBar.Initialize(barTexture);
            _myBall.Initialize(ballTexture);
        }

        /// <summary>
        /// updates the players objects
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="gameTime"></param>
        internal void Update(GraphicsDeviceManager graphics, GameTime gameTime)
        {
            _myBall.Update(graphics, gameTime);
            _myBar.Update(graphics, gameTime);

            bool BallCollidedWithBar = _myBar.ObjectBounds.Intersects(_myBall.ObjectBounds);
            if (BallCollidedWithBar)
            {
                float unitAcc = 0.01F;
                _myBall.Speed.Y *= -1;
                _myBall.Speed.X = _myBar.Speed.X * unitAcc * 10;

                if (_myBall.Accerelation.X > 0)
                    _myBall.Accerelation.X = -unitAcc;
                else
                    _myBall.Accerelation.X = unitAcc;
            }
        }

        /// <summary>
        /// Changes the players bar slide speed
        /// </summary>
        /// <param name="p">slide speed (Y axis in accerelometer)</param>
        internal void ChangeBarSpeed(double p)
        {
            _myBar.Speed.X = -1 * (int)(p * 100);
        }
    }
}

Develop The Game Time

Here we handle the user interaction with the game. I have chosen to get the accelerometer values from the device. When user moves the device the accelerometer values are changing. This creates a nice gaming experience.

using System;
using System.Collections.Generic;
using System.Linq;
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.Input.Touch;
using Microsoft.Xna.Framework.Media;
using Microsoft.Devices.Sensors;

using BounceBall.GameObjects;

namespace BounceBall
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class BounceBallGame : Game
    {
        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;

        private Player myPlayer = new Player();
        private GameBackground myGameBackground = new GameBackground();

        private bool OnMenu = false;
        private LevelBase myLevel = new Level1();

        private Accelerometer accelerometer = new Accelerometer();
        private bool accelerometerIsDisabled = false;

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

            // Frame rate is 30 fps by default for Windows Phone.
            TargetElapsedTime = TimeSpan.FromTicks(333333);

            // Extend battery life under lock.
            InactiveSleepTime = TimeSpan.FromSeconds(1);

            try
            {
                accelerometer.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(accelerometer_ReadingChanged);
                accelerometer.Start();
            }
            catch (Microsoft.Devices.Sensors.AccelerometerFailedException ex)
            {
#if DEBUG
                Console.WriteLine(ex.ToString());
#endif
                accelerometerIsDisabled = true;
            }
        }

        protected void accelerometer_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
        {
            if (accelerometerIsDisabled) return;

            this.myPlayer.ChangeBarSpeed(e.Y);
        }

        /// <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()
        {
            // TODO: Add your initialization logic here

            this.Window.Title = "Bounce Ball";
            this.IsMouseVisible = true;
            this.Window.AllowUserResizing = false;

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

            // TODO: use this.Content to load your game content here
            //myGameBackground.Initialize(Content.Load<Texture2D>("Background"));
            myGameBackground.Initialize(Content.Load<Texture2D>("GridBlack"));
            myPlayer.Initialize(Content.Load<Texture2D>("RedBar"), Content.Load<Texture2D>("RedBall"));
            myLevel.Initialize(Content.Load<Texture2D>("YellowBrick"));
        }

        /// <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();

            if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape))
                this.Exit();

            

            // TODO: Add your update logic here
            if (!OnMenu)
            {
                myPlayer.Update(graphics, gameTime);
                myLevel.Update(graphics, gameTime);
                myLevel.CheckCollision(myPlayer.MyBall);
            }

            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)
        {
            //GraphicsDevice.Clear(new Color(20, 20, 40));
            GraphicsDevice.Clear(Color.Black);

            // TODO: Add your drawing code here
            // Draw the sprite.            
            spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
            myGameBackground.Draw(spriteBatch, gameTime);

            if (!OnMenu)
            {
                myPlayer.Draw(spriteBatch, gameTime);
                myLevel.Draw(spriteBatch, gameTime);
            }

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Last edited Mar 29, 2013 at 9:37 AM by ogu, version 8