My First XNA Application: The Bouncing Ball
My first application in XNA! It’s a… well, it’s a ball that bounces.
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!
Leave a comment