Source code: http://www.bluerosegames.com/spacerocks/SpaceRocks_3_Sprites_2.zip
In the last step we created a sprite class based on the Silverlight Templated control. We gave this class X and Y properties to position the sprite. This is a great start and combining that with our game loop from step 1 we could start putting together the basics for a game. In our Space Rocks game, sprites will move based on a velocity. Let’s see how we can add code to the sprite class to take care of this.
Velocities have an X and Y component and to calculate the position at any point in time we can calculate the distance traveled by multiplying the velocity by the time since the last time we set the position and add that to the previous position. This is where the SilverSprite core assembly can help us out. One of the things that this assembly provides for us in the XNA Vector2 data type. A Vector2 is a value data type (aka a struct) with an X and Y value and a bunch of helper methods to let us do game related things like multiplying by a value or adding two Vector2 values together. Let’s implement a Position property on the Sprite class which is of type Vector2 and also create a Velocity property. Add the following to Sprite.cs:
public Vector2 Position
{
get
{
return new Vector2((float)x, (float)y);
}
set
{
X = value.X;
Y = value.Y;
}
}
public Vector2 Velocity { get; set; }
You’ll also need a using statement for the Xna namespace:
using Microsoft.Xna.Framework;
For the Position property under the covers we are actually setting and getting the X and Y properties of the Sprite class. This allows us to use whichever makes the most sense for the task at hand.
One nice thing about using the SilverSprite core library to help us out is that you can start to get an idea about how to do some of the basics in XNA. This looks like it’s going to get more important with Windows Phone 7 Series if there is a game you want to develop that makes more sense to write in XNA. SilverSprite attempts to mimic the XNA APIs and doesn’t require XNA to be installed for you to use it.
Something that’s a little tricky about the Vector2 struct and most other things in XNA is that they use the float data type. Almost everything in Silverlight uses double. So we need to cast between the two when going back and forth. XNA uses float for its values for efficiency since these translate directly to the data structures used by the graphics card.
Let’s add a method to the Sprite class that we can call from our game loop to update the sprite position based on the current velocity and time since the last update.
public void Update(double elapsedSeconds)
{
Position += Velocity * (float)elapsedSeconds;
}
This is where Vector2 starts to shine. We can simply use addition and multiplication operators and we don’t have to worry about the X and Y components.
Now in the Game class, we’ll make some changes to use the Position and Velocity properties and we’ll keep a reference to the sprite object so that we can reference it in the GameLoop_Update method.
using System;
using System.Windows.Controls;
using Microsoft.Xna.Framework;
namespace SpaceRocks
{
public partial class Game : UserControl
{
Sprite sprite;
public Game()
{
InitializeComponent();
sprite = new Sprite();
sprite.Position = new Vector2(100, 50);
sprite.Velocity = new Vector2(100, 100);
LayoutRoot.Children.Add(sprite);
}
private void GameLoop_Update(object sender, SilverArcade.SilverSprite.SimpleEventArgs<TimeSpan> e)
{
double seconds = e.Result.TotalSeconds;
sprite.Update(seconds);
}
}
}
Now the sprite should move diagonally. You can play with the velocity values and see how it behaves. You can even use negative values for the X and Y components of the velocity.
One problem we have is that the sprite now moves right off of the edge of the screen. In our game we want these values to “wrap” and if they get too large or too small, we’ll move them to the other end of the screen. We can do this in our Sprite.Update method.
static Vector2 gameSize = new Vector2(640, 480);
public void Update(double elapsedSeconds)
{
Position += Velocity * (float)elapsedSeconds;
if (Position.X > gameSize.X) Position -= new Vector2(gameSize.X, 0);
if (Position.X < 0) Position += new Vector2(gameSize.X, 0);
if (Position.Y > gameSize.Y) Position -= new Vector2(0, gameSize.Y);
if (Position.Y < 0) Position += new Vector2(0, gameSize.Y);
}
Now if you run the game the sprite should wrap when it hits the edge. One thing you’ll probably notice is that the sprite goes off of the end of the game surface. The Canvas panel doesn’t clip to its bounds by default so we can add a Clip property to the game canvas.
<Canvas x:Name="LayoutRoot" Background="Black" Width="640" Height="480">
<Canvas.Clip>
<RectangleGeometry Rect="0,0,640,480"/>
</Canvas.Clip>
</Canvas>
This isn’t necessary if your game fills the entire Silverlight plug-in and has some overhead so only use it if you need it. Now if you run the game the edges should be clipped and you won’t see the sprite go off of the game surface.
In the next post we’ll look at inheriting from the Sprite class and creating the ship for our asteroids game.