Part 2 – Sprite sheets

In Part 1 we saw how to create a physics simulation and how to render some objects to screen. Today we’ll go into more detail on rendering. Here’s a recap of our Draw method as it currently stands:

spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null,null, null, transformationMatrix);
foreach (var body in _bodies)
{
	spriteBatch.Draw(_box, body.Position * Scale, null, Color.White, body.Rotation, origin, 1f, SpriteEffects.None, 0f);
}
spriteBatch.End();

The SpriteBatch object is a core XNA object that allows us to draw 2D textures on the screen. As its name implies it does so in batches. The nature of graphics cards is that they perform really well when given tasks in large batches.
Imagine a typical game screen: a multitude of different textures. SpriteBatch will have a hard time coping with this.

In order to speed up rendering we will combine all our textures (from now on referenced to as sprites) into one huge texture, called a sprite sheet.

Bing the term ‘spritesheet’ and you’ll find thousands of examples: www.bing.com/images/search?q=spritesheet&FORM=BIFD

Luckily we don’t have to create such a sprite sheet manually. On the App Hub site we can find a sample project which contains a content processor that does just this job.

What is a content processor? A content processor takes as input an asset (= a list of all our individual sprites) and performs some operations on it to transform the original asset into a more desirable asset (a sprite sheet containing all individual sprites combined).
It goes without saying this transformation happens at compile time, not at run-time, since we want run-time to be as fast as possible.

Let’s dive into code again:

Go to http://create.msdn.com/en-US/education/catalog/sample/sprite_sheet to download the sample code containing the sprite sheet content processor.

The sample code contains a solution where 2 projects are important for us:
SpriteSheetPipeline, containing the content processor.
SpriteSheetRuntime (Phone), containing a SpriteSheet class which is a wrapper to access our combined sprite sheet.

Add both projects to our own solution.

Add a reference from PigsFly to SpriteSheetRuntime. Add a reference from PigsFlyContent to SpriteSheetPipeline (Phone).

Upgrading our artwork from a simple red box, we now have 2 textures:

Texture for box1 Texture for box2

Save these images in the root of the PigsFlyContent project, but do not add them to the project. If you were to add them, they would become part of our game, which we don’t want. We only want the generated sprite sheet to be part of it.

Next add a new item to the PigsFlyContent project: Add | New Item -> XML File. Give it the name PigsFlySpriteSheet.xml

Add New Item - XML File

Edit the content of the new file to make it look like this:

<XnaContent>
	<Asset Type="System.String[]">
		<Item>box1.png</Item>
		<Item>box2.png</Item>
	</Asset>
</XnaContent>

Finally, take a look at the properties of the xml file and set the Content Processor to SpriteSheetProcessor:

Specify Content Processor

Given this preparation, we are now ready to use our brand new spritesheet.

We will revisit the LoadContent and Draw methods.

In LoadContent, load the sprite sheet as following:

SpriteSheet _sheet = Content.Load<SpriteSheet>("PigsFlySpriteSheet");

The SpriteSheet class has a Texture property, which returns the generated sprite sheet. We also have the following method at our disposal: Rectangle SourceRectangle(string spriteName).
Each sprite is defined by a Rectangle which contains the location on the sprite sheet where the sprite is located. Given the original sprite’sname, this method gives us that source rectangle. We will use this when rendering our bodies on screen.

Another change in LoadContent is that we will instruct each Body object which sprite it should use to render itself.

Farseer provides us with a UserData property on the Body class which can be used for any purpose we like. For now, we will store the original sprite name:

//Create 2 boxes
Body box1 = BodyFactory.CreateRectangle(_world, 0.4f, 0.4f, 1f);
box1.Position = new Vector2(4, 0.75f);
box1.BodyType = BodyType.Dynamic;
box1.UserData = "box1"; // <-- the name of the texture as it was called originally
_bodies.Add(box1);
Body box2 = BodyFactory.CreateRectangle(_world, 0.4f, 0.4f, 1f);
box2.Position = new Vector2(4.2f, 0.0f);
box2.BodyType = BodyType.Dynamic;
box2.UserData = "box2"; // <-- the name of the texture as it was called originally
_bodies.Add(box2);

We will now change our existing Draw method so it will render our two boxes with distinctive representations.
Because we use the same texture (= our sprite sheet) there will be no performance penalty for doing so.

spriteBatch.Begin();

foreach (var body in _bodies)
{
	spriteBatch.Draw(
			_sheet.Texture, // Spritesheet Texture
			body.Position * Scale, // Position
			_sheet.SourceRectangle((string) body.UserData), // Source rectangle, defines which subset of the spritesheet we will render
			Color.White,
			body.Rotation,
			origin,	// Origin, relative to source rectangle size
			1f, // Scale
			SpriteEffects.None,// No effects
			0f); // Z-layer
}

spriteBatch.End();

Here is the result:

Stalk me on twitter? Sure, why not? http://twitter.com/jodegreef

Advertisements

14 Responses to Part 2 – Sprite sheets

  1. Pingback: Develop an Angry Birds look-alike for windows phone 7 | Suddenelfilio.net

  2. Davis says:

    This is a great tutorial, I hope you continue this. There’s not so many farseer tutorials out there for xna at the moment. Thanks for writing these and please, do continue!

  3. Fred Beiderbecke says:

    Thanks for this, it is very informative.

    I am having some issues. I am not sure what I am doing wrong, but things are not working.

    First, should the references be to SpriteSheetPipeline and SpriteSheetRuntime(Phone)? In the article it is SpriteSheetPipeline(Phone) and SpriteSheetRuntime. I do no see other options.

    I only get get the SpriteSheet to work if I preface with SpriteSheetRuntime.

    In the spriteBatch.Draw, the _sheet does not exist in the current context. I added the references, it was declared in LoadContent, but does not appear to be visible in the Draw method.

    Any suggestions?

    Thanks.

    • jodegreef says:

      If you need to preface SpriteSheet with SpriteSheetRuntime, you are probably missing a Using statement at the top of your class file.

      As far as the missing _sheet variable, you’ll need to show me where you declared the variable. Is it in the scope of your class or only in the scope of the LoadContent method?

      • Fred Beiderbecke says:

        The Using fixed the problem with the prefacing.

        I declare SpriteSheet _sheet = Content.Load(“PigsFlySpriteSheet”); in the LoadContent method and then try to reference the _sheet in the Draw method.

        That is what I thought was expected based on the article.

        Thanks for the help.

      • jodegreef says:

        Looks like an error on my part. SpriteSheet _sheet; should be declared as a field on the game class. In LoadContent, it should simply say _sheet = Content....

  4. Chuck says:

    Add a reference from PigsFly to SpriteSheetRuntime. Add a reference from PigsFlyContent to SpriteSheetPipeline (Phone).

    I am not sure what I am missing here. Do I add something into SpriteSheetRuntime and SpriteSheetPipeline(Phone)? Or am I just adding a USING into our project?

  5. Kostas says:

    can you write all your list of using statement????

    i add the
    SpriteSheet _sheet; to the game class

    and also i delete
    SpriteSheet before _sheet In LoadContent

    i stil have this error

    Error 1 The type or namespace name ‘SpriteSheet’ could not be found (are you missing a using directive or an assembly reference?) C:\Users\Kostas\Documents\Visual Studio 2010\Projects\PigsFly\PigsFly\PigsFly\Game1.cs 28 9 PigsFly

  6. Chuck says:

    Thank you for pointing me in the correct direction.

    I appreciate your taking the time.

  7. jerry says:

    What’s the variable(or constant) origin?

  8. agata says:

    I have problem with make the SpriteSheet class available in game

    I Right-click the References item in my main game project. Click Add Reference. Click the Projects tab, and select the SpriteSheetRuntime project

    and i see error:

    A reference to SpriteSheet Runtime (phone) could not be added. references with diffrent refresh levels are not supported can u help me?

    • agata says:

      and probaly it give me that error

      Error 1 Missing asset “C:\Users\…\Downloads\pigsfly Bird Included\pigsfly Bird Included\pigsfly1\PigsFly\PigsFlyContent\SpriteSheetProcessor.cs”. C:\Users\…\Downloads\pigsfly Bird Included\pigsfly Bird Included\pigsfly1\PigsFly\PigsFlyContent\SpriteSheetProcessor.cs PigsFly

  9. Ali says:

    PLEASE HELP!

    Can I know how to solve this warning:

    Warning 1 The primary reference “C:\pigsfly Bird Included\pigsfly1\SpriteSheetPipeline\bin\x86\Debug\SpriteSheetPipeline.dll” could not be resolved because it has an indirect dependency on the framework assembly “mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089” which could not be resolved in the currently targeted framework. “.NETFramework,Version=v4.0,Profile=Client”. To resolve this problem, either remove the reference “C:\pigsfly Bird Included\pigsfly1\SpriteSheetPipeline\bin\x86\Debug\SpriteSheetPipeline.dll” or retarget your application to a framework version which contains “mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: