Running with Code

Like scissors, only more dangerous

  Home  |   Contact  |   Syndication    |   Login
  77 Posts | 0 Stories | 131 Comments | 2 Trackbacks

News



Archives

Post Categories

All Terralever

ASP.NET

Misc

My first application in XNA!  It's a... well, it's a ball that bounces.

It's bouncing!!

I created a new Windows XNA 2.0 application project.  I decided to abstract away a Ball object, as well as a GravitySource object.  There's a lot of cross-talk -- I'm not sure if this is good or not -- but it's happy enough for me. :-)

Gravity seems to be pretty straightforward to implement; the applied speed is going to be a vector direction with a constant speed as the magnitude.  Here's the code for the gravity source class:

   1: public class GravitySource
   2: {
   3:     private const float GRAVITY = 9.8f;
   4:  
   5:     private Vector2 m_gravitySpeed;
   6:  
   7:     public void ApplyGravity(ref Vector2 currentSpeed)
   8:     {
   9:         currentSpeed += m_gravitySpeed;
  10:     }
  11:  
  12:     public void ResetToDirection(Direction target)
  13:     {
  14:         switch (target)
  15:         {
  16:             case Direction.Up:
  17:                 m_gravitySpeed = new Vector2(0f, -GRAVITY);
  18:                 break;
  19:             case Direction.Left:
  20:                 m_gravitySpeed = new Vector2(-GRAVITY, 0f);
  21:                 break;
  22:             case Direction.Right:
  23:                 m_gravitySpeed = new Vector2(GRAVITY, 0f);
  24:                 break;
  25:             case Direction.Down:
  26:                 m_gravitySpeed = new Vector2(0f, GRAVITY);
  27:                 break;
  28:             case Direction.DownLeft:
  29:                 m_gravitySpeed = Vector2.Normalize(new Vector2(-1, 1)) * GRAVITY;
  30:                 break;
  31:             case Direction.DownRight:
  32:                 m_gravitySpeed = Vector2.Normalize(new Vector2(1, 1)) * GRAVITY;
  33:                 break;
  34:             case Direction.UpLeft:
  35:                 m_gravitySpeed = Vector2.Normalize(new Vector2(-1, -1)) * GRAVITY;
  36:                 break;
  37:             case Direction.UpRight:
  38:                 m_gravitySpeed = Vector2.Normalize(new Vector2(1, -1)) * GRAVITY;
  39:                 break;
  40:         }
  41:     }
  42: }

Note that I added a Direction enumeration to make it nice to read.

The game class has a reference to the ball object, and it handles the keyboard input to change the gravity direction all within the Update method:

   1: protected override void Update(GameTime gameTime)
   2: {
   3:     // Allows the game to exit
   4:     if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
   5:         this.Exit();
   6:  
   7:     KeyboardState keybstate = Keyboard.GetState();
   8:     if (keybstate.IsKeyDown(Keys.Down))
   9:     {
  10:         if (keybstate.IsKeyDown(Keys.Left))
  11:         {
  12:             m_grav.ResetToDirection(Direction.DownLeft);
  13:         }
  14:         else if (keybstate.IsKeyDown(Keys.Right))
  15:         {
  16:             m_grav.ResetToDirection(Direction.DownRight);
  17:         }
  18:         else
  19:         {
  20:             m_grav.ResetToDirection(Direction.Down);
  21:         }
  22:     }
  23:     else if (keybstate.IsKeyDown(Keys.Up))
  24:     {
  25:         if (keybstate.IsKeyDown(Keys.Left))
  26:         {
  27:             m_grav.ResetToDirection(Direction.UpLeft);
  28:         }
  29:         else if (keybstate.IsKeyDown(Keys.Right))
  30:         {
  31:             m_grav.ResetToDirection(Direction.UpRight);
  32:         }
  33:         else
  34:         {
  35:             m_grav.ResetToDirection(Direction.Up);
  36:         }
  37:     }
  38:     else if (keybstate.IsKeyDown(Keys.Left))
  39:     {
  40:         m_grav.ResetToDirection(Direction.Left);
  41:     }
  42:     else if (keybstate.IsKeyDown(Keys.Right))
  43:     {
  44:         m_grav.ResetToDirection(Direction.Right);
  45:     }
  46:  
  47:     m_ball.Update(gameTime, graphics.GraphicsDevice.Viewport);
  48:  
  49:     base.Update(gameTime);
  50: }

The game's drawing method is actually quite simple:

   1: protected override void Draw(GameTime gameTime)
   2: {
   3:     graphics.GraphicsDevice.Clear(Color.Black);
   4:  
   5:     spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
   6:     m_ball.Render(gameTime, spriteBatch);
   7:     spriteBatch.End();
   8:  
   9:     base.Draw(gameTime);
  10: }

And that gets implemented in the Ball class; here are the meat and potatoes:

   1: public void Render(GameTime time, SpriteBatch spriteBatch)
   2: {
   3:     spriteBatch.Draw(m_texture, m_position, Color.White);
   4: }
   5:  
   6: public void Update(GameTime time, Viewport bounds)
   7: {
   8:     m_position += m_speed * (float)time.ElapsedGameTime.TotalSeconds;
   9:  
  10:     int maxX = bounds.Width - m_texture.Width;
  11:     int maxY = bounds.Height - m_texture.Height;
  12:  
  13:     if (m_position.X > maxX)
  14:     {
  15:         m_speed.X *= -1.0f;
  16:         m_position.X = maxX;
  17:     }
  18:     else if (m_position.X < 0)
  19:     {
  20:         m_speed.X *= -1.0f;
  21:         m_position.X = 0;
  22:     }
  23:     else
  24:     {
  25:         // else we're in a freefall to the right!
  26:         m_gravity.ApplyGravity(ref m_speed);
  27:     }
  28:  
  29:     if (m_position.Y > maxY)
  30:     {
  31:         m_speed.Y *= -1.0f;
  32:         m_position.Y = maxY;
  33:     }
  34:     else if (m_position.Y < 0)
  35:     {
  36:         m_speed.Y *= -1.0f;
  37:         m_position.Y = 0;
  38:     }
  39:     else
  40:     {
  41:         // else we're in a freefall
  42:         // v(t) = at + k
  43:         // in this case a = -9.8
  44:         // Inverted because we're going down which is positive Y ;-)
  45:         m_gravity.ApplyGravity(ref m_speed);
  46:     }
  47: }

One kind of odd part is that it's designed to be simply a perfectly elastic ball (m_speed.Y *= -1.0f) but it actually bounces higher over time.

Source code is downloadable here.  Thanks to Betsy Aoki's samples online - they were incredibly helpful!

posted on Thursday, May 15, 2008 12:03 AM

Feedback

# re: My First XNA Application: The Bouncing Ball 5/15/2008 12:39 PM Ziggyware XNA News and Tutorials
Nice Work! :) Keep at it.

# re: My First XNA Application: The Bouncing Ball 7/28/2009 3:44 PM Robert vd Veer
This looks promising!

However, it isn't actually that odd ;)
Once you apply a bit of friction, the problem disappears.

In fact, your gravity class is a little bit too much. It actually does not do that much (yet!).

I would create two constants, one for the gravity (eg. 9.8f) and one for the friction that is applied (eg. 1.0035f).

Add the constant gravity to your ballSpeed.Y
then divide your ballSpeed by the friction constant.

Like this:

[code]
private void UpdateBall(GameTime gameTime, Viewport bounds)
{
//Determine the boundaries of our ball and window.
int maxX = bounds.Width - (int)(_ballTexture.Width * _ballScale);
int minX = 0;
int maxY = bounds.Height - (_bottomTexture.Height + (int)(_ballTexture.Height * _ballScale ));
int minY = 0;

//Check where our ball would collide (sides or bottom) and flip it's direction
if (_ballPosition.X < minX || _ballPosition.X > maxX )
{
_ballPosition.X = MathHelper.Clamp(_ballPosition.X, minX, maxX);
_ballVelocity.X *= -1;
}
if (_ballPosition.Y <minY || _ballPosition.Y > maxY)
{
_ballPosition.Y = MathHelper.Clamp(_ballPosition.Y, minY, maxY);
_ballVelocity.Y *= -1;
}

//Apply gravity and friction, to let it look a bit more realistic
_ballVelocity.Y += GRAVITY;
_ballVelocity /= FRICTION;

//Finally update the ball's current position by adding the calculated speed (acceleration)
_ballPosition += _ballVelocity * (float)gameTime.ElapsedGameTime.TotalSeconds;

}
[/code]


Here are the constants I referred to:

[code]
const float GRAVITY = 9.8f;
const float FRICTION = 1.0035f;
[/code]

You might want to tweak the FRICTION value a bit.

Although I realize this advise of mine comes rather late, I just hope it could help others.

Regards,

Robert

Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: