Warning: Parameter 1 to wp_default_scripts() expected to be a reference, value given in /var/www/xnafan.net/public_html/wp-includes/plugin.php on line 571

Warning: Parameter 1 to wp_default_styles() expected to be a reference, value given in /var/www/xnafan.net/public_html/wp-includes/plugin.php on line 571
xnaFan's Blog » Blog Archive » Simple platformer game in XNA tutorial – part one “The basic Sprite”

Simple platformer game in XNA tutorial – part one “The basic Sprite”

This tutorial covers all the steps in coding your own platformer for use in your games. We will cover a bit of the principles in Object Oriented Design and Clean Code along the way Smiley.

What we want to end up making is something like this:

If you just want to try it out and play around with it, you can download the complete source code here.

The agenda

Here’s the agenda.

  • Setting up
  • The Game class
  • The Sprite class
  • The Tile and Board class
  • The Jumper class
  • Introducing simple collision detection
  • Improving our collision detection
  • Adding gravity and jump ability
  • Refactoring code for readability

In this first part of the tutorial I will cover the first three bulletpoints Smiley

Qualifications needed to understand this tutorial

I expect you to know C#, some Object Oriented programming principles (like how to create a class, and add properties to it), and have a basic understanding of how XNA works (LoadContent/Update/Draw).

If you aren’t at this level – then you’re welcome to try and keep up anyway, and ask in the comments if there’s something you want to understand and aren’t able to figure out by searching the web Smiley.

Setting up

Create a new solution with a XNA game project and a Content project. I have named my project “SimplePlatformer”. You may want to follow my naming to stay in sync Smiley.

If you want to, you can create your own graphics, or you can download the tile.png (64x64 pixels) and jumper.png (40 x 40 pixels) textures here:

jumper  tile

right click –> save as… and save them somewhere where you can find them again. Then add them to the content project by right clicking the project - like this:

image

So your content project looks like this:

image

The Game class

Next we’ll clean up the Game1.cs file. Rename it to SimplePlatformerGame.cs, and you will be asked

image

say yes, so the class name is also updated everywhere in the solution.

Now clean away all unnecessary code in the SimplePlatformerGame class so you only have the following methods left:

The constructor “SimplePlatFormerGame()”
LoadContent()
Update()
Draw()

Compile to check that everything is working.

Your SimplePlatformerGame class should look like this
(“usings” and namespace declaration omitted for brevity in most samples in this tutorial)

public class SimplePlatformerGame : Game
{
    private GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch;

    public SimplePlatformerGame()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);
    }

    protected override void Update(GameTime gameTime)
    {
        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        base.Draw(gameTime);
    }
}

You can see that I’ve renamed the graphics to _graphics and spriteBatch to _spriteBatch. I do this so that I will always know when using a variable somewhere in my code whether this is a local variable or a member variable, which a lot of other methods are using.

The Sprite class

The Sprite class is basically just a help for combining a texture and a position. It will make it easier for us to place all the elements which we will need to create a complete game.

Create a new classfile Sprite.cs in your project class and the variables to store a Texture2D with a Vector2 for position:

public class Sprite
{
    public Vector2 Position { get; set; }
    public Texture2D Texture { set; get; }
}

Are we satisfied?

Now while this would be enough if we implemented the code for setting Texture and Position outside the Sprite, and had the drawing code outside (e.g. in the SimplePlatformerGame class) we would have a satisfactory class.

We could initialize it like this in the SimplePlatformerGame class’ LoadContent() method:

Sprite mySprite =
 new Sprite {Texture = Content.Load("tile"), Position = new Vector2(100,50)};

and draw it like this in the SimplePlatformerGame class’ Draw() method:

SpriteBatch.Draw(mySprite.Texture, mySprite.Position, Color.White);

We can do better…

And we will, because we want each tile to be able to draw itself and work as a self-contained unit with data and functionality in one neat package. So we will also add

  • a SpriteBatch variable which the Sprite will use to draw itself.
  • a constructor to ensure that a Sprite object can not be created from the Sprite class without the necessary data for the Sprite to function (which is a main task for a constructor)
  • a Draw method which uses the provided SpriteBatch to draw to the graphics memory (see code sample below)
public class Sprite
{
    public Vector2 Position { get; set; }
    public Texture2D Texture { set; get; }
    public SpriteBatch SpriteBatch { get; set; }

    public Sprite(Texture2D texture, Vector2 position, SpriteBatch batch)
    {
        Texture = texture;
        Position = position;
        SpriteBatch = batch;
    }

    public void Draw()
    {
        SpriteBatch.Draw(Texture, Position, Color.White);
    }
}

Now it’s easy peasy

…to initialize, and use the Sprite class from our SimplePlatformerGame:

We can create a new Sprite object like this:

Sprite mySprite = new Sprite(_tileTexture, new Vector2(40,40), _spriteBatch);

(provided we have a texture stored in the variable _tileTexture, and a reference to the SimplePlatformerGame object’s SpriteBatch in the variable _spriteBatch)

In case you didn’t catch the difference between the creation of the Sprites in the two sections above: In the first case I used an object initializer, where

Sprite mySprite = new Sprite {Texture = _aTexture, Position = _aPosition};

is just shorthand for

Sprite mySprite = new Sprite(); 
mySprite.Texture = _aTexture; 
mySprite.Position = _aPosition;

in the other sample I used a constructor with parameters which ensures that it is no longer possible to instantiate the object like this:

Sprite mySprite = new Sprite();

because the parameterless constructor (or “default constructor”) is removed when you explicitly create one yourself. The logic of the compiler being “oh – the programmer wants to control object-creation, I better mind my own business then Smiley).

Now, whenever we need the Sprite drawn, we just call the Sprite’s Draw() method from the SimplePlatformerGame class’ Draw method:

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.WhiteSmoke);
    _spriteBatch.Begin();
    base.Draw(gameTime);
    mySprite.Draw();
    _spriteBatch.End();
}

The call to base.Draw(gameTime) is a best practice when we are overriding a method (notice the override void Draw(…)). Since our SimplePlatformerGame class inherits Microsofts Game class, we actually don’t know what is going on inside the SimplePlatformerGame.Draw method. To ensure we don’t break any internal mechanics, we always call the overriden method’s base implementation (unless we really know what we’re doing Smiley, der blinker).

Let’s test it

Add two Texture2D member variables to the top of the SimplePlatformerGame class, where the _graphics and _spriteBatch are declared, and a Sprite (just for testing, that variable will be deleted shortly):

private GraphicsDeviceManager _graphics;
private SpriteBatch _spriteBatch;
private Texture2D _tileTexture, _jumperTexture;
private Sprite _aSpriteForTesting

Add code to LoadContent() to load and store the two textures:

protected override void LoadContent()
{
    _spriteBatch = new SpriteBatch(GraphicsDevice);
    _tileTexture = Content.Load("tile");
    _jumperTexture = Content.Load("jumper");
    _aSpriteForTesting = new Sprite(_jumperTexture, new Vector2(50,50), _spriteBatch);
}

Finally call the Sprite’s Draw() from the SimplePlatformerGame’s Draw():

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.WhiteSmoke);
    _spriteBatch.Begin();
    base.Draw(gameTime);
    _aSpriteForTesting.Draw();
    _spriteBatch.End();
}

Run the game (F5) and you should see something like this:

image

Yay! We’ve got something on the screen Smiley

Assignment for the reader

To ensure you’ve understood what’s going on – try adding another Sprite variable and load a Sprite with the Tile texture underneath and to the right.

image

If you get stuck – the solution code is here:

public class SimplePlatformerGame : Game
{
    private GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch;
    private Texture2D _tileTexture, _jumperTexture;
    private Sprite _aSpriteForTesting, _anotherSpriteForTesting;

    public SimplePlatformerGame()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);
        _tileTexture = Content.Load("tile");
        _jumperTexture = Content.Load("jumper");

        _aSpriteForTesting = 
            new Sprite(_jumperTexture, new Vector2(50, 50), _spriteBatch);
        _anotherSpriteForTesting = 
            new Sprite(_tileTexture, new Vector2(90, 120), _spriteBatch);
    }

    protected override void Update(GameTime gameTime)
    {
        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.WhiteSmoke);
        _spriteBatch.Begin();
        base.Draw(gameTime);
        _aSpriteForTesting.Draw();
        _anotherSpriteForTesting.Draw();
        _spriteBatch.End();
    }
}

What we’ve covered

After reading this part one of the tutorial, you should have learnt that

  • a class is a nifty way to combine data which should be logically paired, such as the Texture and Position in the Sprite class
  • a class can be used to encapsulate both data (image, position, spritebatch) and functionality (draw, constructor) in a little package which can then be interacted with using very little code (the Sprite.Draw() method)
  • you can use a constructor to ensure that an object can only be constructed from a class if all the parameters are passed along (though null values can be substituted)

Solution so far

Here’s the code at the stage we’re at now, with the extra Sprite variable removed and the remaining sprite variable renamed _jumper

Class diagram

image

Here we see that a Sprite has three properties: Position, SpriteBatch and Texture, and implements a Draw method and a Sprite constructor.

It doesn’t show that Sprite implicitly inherits Object, but if a class doesn’t explicitly inherit another class, it is a subclass of Object.

Next up: the Tile and Board

In the next part we will look at the Tile and Board class…

4 Responses to “Simple platformer game in XNA tutorial – part one “The basic Sprite””

  1. Richard Says:

    Hey,

    I tried to download the source code for http://xnafan.net/2013/04/simple-platformer-game-in-xna-tutorial-part-one/ to play around with it but the link requires an username and password...

    Could you help?

    Thanks!

    Richard

  2. admin Says:

    Thanks for pointing it out Richard :)
    Fixed!

  3. Stephen Says:

    Hi there

    Just a quick note:

    In order to get this to work I had to tweak the LoadContent() code. It needed the object type '' as per the following code:

    _tileTexture = Content.Load("tile");
    _jumperTexture = Content.Load("jumper");

    The original code was:
    _tileTexture = Content.Load("tile");
    _jumperTexture = Content.Load("jumper");

  4. admin Says:

    Hi Stephen :)
    Thanks for your feedback.
    I'm guessing the comment editor stripped some tags, as I don't see any difference in your two codesamples.
    If you could email me the changes you needed to implement I'll see if I can figure out why you would need to change something to make it work.

    Kind regards - Jakob

Leave a Reply