4. Moving the Graphic
Static images aren't too interesting, so let's make the graphic move. At the moment, we are specifying its position within the Draw procedure by passing a Vector2
structure initialized with the values 100 and 100. These two values are
the x and y positions at which the graphic will be drawn, and represent
its top-left corner. This position is known as a coordinate.
When coordinates are written
down, they are enclosed within parentheses inside which the two values
are placed, separated by a comma. The first value is the x coordinate,
and the second is the y coordinate. For example, (20, 50) represents a
coordinate with an x position of 20 and a y position of 50.
The coordinate system used by
XNA sprites starts from (0, 0) at the top-left corner of the screen. It
then extends across the screen's width and height so that the point at
the bottom-right corner of the screen is (Window.ClientBounds.Width - 1, Window.ClientBounds.Height - 1). All sprite coordinates are measured in pixels.
To make the sprite move,
we just need to remember its coordinate from one draw to the next and
modify its values so that the sprite's position changes. We can do this
very easily. First we will declare another class-level variable to store
the sprite position, as shown in Listing 5.
Example 5. Declaring a variable to hold the position of the graphic
private Vector2 _smileyPosition;
|
Next we need to provide an initial position for the graphic. The default uninitialized Vector2
object has a coordinate of (0, 0), corresponding to the top-left corner
of the screen. Because our game window doesn't actually quite fill the
screen (the top section is currently being used by the operating system
to display status information), this is in fact outside the bounds of
our game display and will result in the graphic being partly obscured by
the status bar.
We will continue to use the
coordinate (100, 100) as our initial position. We have a couple of
options for setting it, one of which is to specify the position as part
of the variable declaration in Listing 5. It may be useful to be able to reset the position later on, however, so we will create a new procedure called ResetGame and set the coordinate here, as shown in Listing 6.
Example 6. Setting the initial position for the smiley graphic
/// <summary> /// Reset the game to its default state /// </summary> private void ResetGame() { // Set the initial smiley position _smileyPosition = new Vector2(100, 100); }
|
To get this code to run, we will call the ResetGame procedure from the existing Initialize procedure that was generated automatically when the project was created. The modified procedure is shown in Listing 7.
Example 7. Calling the ResetGame procedure
/// <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() { // Reset the game ResetGame();
base.Initialize(); }
|
The Draw code now needs to be modified to use the new position variable. This is achieved by removing the Vector2 position variable, and instead using the _smileyPosition variable, as shown in Listing 8.
Example 8. Drawing the sprite from the class-level variable
// Draw the sprite spriteBatch.Draw(_smileyTexture, _smileyPosition, Color.White);
|
Finally, we need to change the position stored in the _smileyPosition variable so that the graphic actually moves. Although it would be easy to do this in the DrawDraw function should be entirely focused on the drawing operation alone and not on updating the game variables. Instead we use the Update procedure, which once again was added when the project was created, to make these variable changes. Listing 9 code, this wouldn't be the appropriate place to make this change; the
shows a simple Update implementation that moves the smiley face toward
the bottom of the screen and resets its position back to the top once
the bottom is reached.
Example 9. The Update procedure, modified to update our graphic position
/// <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();
// Update the game state _smileyPosition.Y += 5; if (_smileyPosition.Y >= Window.ClientBounds.Bottom) _smileyPosition.Y = 0;
base.Update(gameTime); }
|
If you now run the project
again you should find that the graphic moves down the screen, wrapping
back around to the top after it leaves the bottom.
The separation of updating
the game and drawing the game is something that you will see across all
our XNA samples and is something you should try to stick to in your own
projects. There is nothing physically stopping you from updating the
game variables in the Draw function (nor, in fact, from drawing in the Update
function), but both for readability and also to ensure that your game
works in the way XNA expects, it is strongly advised to keep the
appropriate functionality in each of these two procedures.