Staying with the XNA techie theme, now a word about performing sprite positioning in a game. A sprite is something that has a texture (the image to be displayed when the sprite is drawn) and a position (the place to draw it). Generally sprites map onto objects in a game. If I am creating a game and I need to draw a bat, ball, spaceship, alien or asteroid I will use a sprite to do this.
There are two different ways to position sprites in XNA and each has its advantages and disadvantages. However, it is worth knowing about both.
Rectangle Positioning
With this form you create a rectangle which tells XNA where to draw the item, and how big it is:
Rectangle r = new Rectangle(0, 0, 200, 100);
spriteBatch.Draw(texture, r, Color.White);
This draws our texture in the top left hand corner (0,0) , in a rectangle 200 pixels wide and 100 pixels high.
Using a rectangle like this works well, it also gives you a ready made “bounding box” which you can use to test for collisions:
if (rect1.Intersects(rect2)) {
// We have a collision between rect1 and rect2
}
However, there are some bad things about using rectangles that make me less keen on them
- Rectangles are positioned using integers. The x and y properties you use describe where they are do not have a fractional part. This means that if you want to move a rectangle slowly around the screen (i.e. less than a pixel per update) you can’t just use the x and y properties to do this.
- You can use rectangles to scale drawing, but this gets a bit tedious as you have to work out the size of rectangle you need, and you also need to be mindful of the aspect ratio of the item you are drawing so that it doesn’t end up squashed or stretched.
- It is impossible to rotate a sprite which is positioned using a rectangle
So, rectangles are good for very simple sprites, but once you have become more familiar with XNA I think it is worth moving on to Vector positioning.
Vector Positioning
A vector is something that has direction and magnitude. That sounds posh. Actually it just contains an X and Y value, just like a coordinate. The “direction and magnitude” bit kicks in if you draw a line from the origin (0,0) to the X, Y position given. This line is the vector. The direction is the way the line points, and the magnitude is how long the line is. In XNA terms the Vector2 type is the one you can use to position 2D sprites:
Vector2 v = new Vector2(10, 10);
spriteBatch.Draw(texture, v, Color.White);
This code draws the texture at position (10,10) . The texture is drawn in whatever size it happens to be, i.e. if the texture image was 200 pixels by 100 it would be drawn that size. This means that you might need to scale your textures to fit a particular screen size – but as we shall see later this is not a huge problem.
There are quite a few good things about vectors though.
- The X and Y values of a vector are floating point, so you have very good control of sprite speed
- The XNA framework supports vector mathematics directly. So you can write code like this:
position = position + speed;
- where position and speed are both vectors
When it comes to scaling and rotating a sprite positioned using a vector you can use a more complex version of the draw command (find a detailed description here) to do all this. Also, bear in mind that if you targeting Windows Phone you can fix your resolution in the game to a particular value and then make all your to assets fit.
graphics.PreferredBackBufferWidth = 480;
graphics.PreferredBackBufferHeight = 800;
The phone hardware will scale the display automatically to match whatever size you specify, now and in the future.
If you want to detect bounding box collisions you can write code like this:
public bool Contains(Vector2 pos)
{
if (pos.X < position.X) return false;
if (pos.X > (position.X + texture.Width)) return false;
if (pos.Y < position.Y) return false;
if (pos.Y > (position.Y + texture.Height)) return false;
return true;
}
This returns true if the area covered by the sprite contains the given position.
For bounding box collisions you can use this:
public bool Intersects(Sprite c)
{
if (pos.X + texture.Width < c.pos.X) return false;
if (c.pos.X + c.texture.Width < pos.X) return false;
if (pos.Y + texture.Height < c.pos.Y) return false;
if (c.pos.Y + c.texture.Height < pos.Y) return false;
return true;
}
This test will tell you if the bounding box around the two sprites intersects. However, if the sprites are textures that don’t fill the entire sprite rectangle this test is not a very accurate one. I’ll be covering pixel level collision detection next.