2D Graphics

A fast-paced, hands-on guide to building a 3D game for the Windows Phone 7 platform using XNA 4.0

(For more resources related to this topic, see here.)

Adding content

Create a new project and call it Chapter2Demo. XNA Game Studio created a class called Game1. Rename it to MainGame so it has a proper name.

When we take a look at our solution, we can see two projects. A game project called Chapter2Demo that contains all our code, and a content project called Chapter2DemoContent. This content project will hold all our assets, and compile them to an intermediate file format (xnb). This is often done in game development to make sure our games start faster. The resulting files are uncompressed, and thus larger, but can be read directly into memory without extra processing.

Note that we can have more than one content project in a solution. We might add one per platform, but this is beyond the scope of this article.

Navigate to the content project using Windows Explorer, and place our textures in there.

The start files can be downloaded from the previously mentioned link. Then add the files to the content project by right-clicking on it in the Solution Explorer and choosing the Add | Existing Item.... Make sure to place the assets in a folder called Game2D.

When we click on the hero texture in the content project, we can see several properties. First of all, our texture has a name, Hero. We can use that name to load our texture in code. Note that this has no extension, because the files will be compiled to an intermediate format anyway.

We can also specify a Content Importer and Content Processor. Our .png file gets recognized as texture so XNA Game studio automatically selects the Texture importer and processor for us. An importer will convert our assets into the "Content Document Object Model", a format that can be read by the processor. The processor will compile the asset into a managed code object, which can then be serialized into the intermediate .xnb file. That file will then be loaded at runtime.

Drawing sprites

Everything is set up for us to begin. Let's start drawing some images. We'll draw a background, an enemy, and our hero.

Adding fields

At the top of our MainGame, we need to add a field for each of our objects.The type used here is Texture2D.

Texture2D _background, _enemy, _hero;

Loading textures

In the LoadContent method, we need to load our textures using the content manager.

// TODO: use this.Content to load your game content here _background = Content.Load<Texture2D>("Game2D/Background"); _enemy = Content.Load<Texture2D>("Game2D/Enemy"); _hero = Content.Load<Texture2D>("Game2D/Hero");

The content manager has a generic method called Load. Generic meaning we can specify a type, in this case Texture2D. It has one argument, being the asset name. Note that you do not specify an extension, the asset name corresponds with the folder structure and then the name of the asset that you specified in the properties. This is because the content is compiled to .xnb format by our content project anyway, so the files we load with the content manager all have the same extension. Also note that we do not specify the root directory of our content, because we've set it in the game's constructor.

Drawing textures

Before we start drawing textures, we need to make sure our game runs in full screen. This is because the emulator has a bug and our sprites wouldn't show up correctly. You can enable full screen by adding the following code to the constructor:

graphics.IsFullScreen = true;

Now we can go to the Draw method. Rendering textures is always done in a specific way:

  1. First we call the SpriteBatch.Begin() method. This will make sure all the correct states necessary for drawing 2D images are set properly.
  2. Next we draw all our sprites using the Draw method of the sprite batch. This method has several overloads. The first is the texture to draw. The second an object of type Vector2D that will store the position of the object. And the last argument is a color that will tint your texture. Specify Color.White if you don't want to tint your texture.
  3. Finally we call the SpriteBatch.End() method. This will sort all sprites we've rendered (according the the specified sort mode) and actually draw them.

If we apply the previous steps, they result in the following code:

// TODO: Add your drawing code here spriteBatch.Begin(); spriteBatch.Draw(_background, new Vector2(0, 0), Color.White); spriteBatch.Draw(_enemy, new Vector2(10, 10), Color.White); spriteBatch.Draw(_hero, new Vector2(10, 348), Color.White); spriteBatch.End();

Run the game by pressing F5. The result is shown in the following screenshot:

Refactoring our code

In the previous code, we've drawn three textures from our game class. We hardcoded the positions, something we shouldn't do. None of the textures were moving but if we want to add movement now, our game class would get cluttered, especially if we have many sprites. Therefore we will refactor our code and introduce some classes. We will create two classes: a GameObject2D class that is the base class for all 2D objects, and a GameSprite class, that will represent a sprite.

We will also create a RenderContext class. This class will hold our graphics device, sprite batch, and game time objects. We will use all these classes even more extensively when we begin building our own framework.

Render context

Create a class called RenderContext. To create a new class, do the following:

  1. Right-click on your solution.
  2. Click on Add | New Item.
  3. Select the Code template on the left.
  4. Select Class and name it RenderContext.
  5. Click on OK.

This class will contain three properties: SpriteBatch, GraphicsDevice, and GameTime. We will use an instance of this class to pass to the Update and Draw methods of all our objects. That way they can access the necessary information. Make sure the class has public as access specifier. The class is very simple:

public class RenderContext { public SpriteBatch SpriteBatch { get; set; } public GraphicsDevice GraphicsDevice { get; set; } public GameTime GameTime { get; set; } }

When you build this class, it will not recognize the terms SpriteBatch, GraphicsDevice, and GameTime. This is because they are stored in certain namespaces and we haven't told the compiler to look for them. Luckily, XNA Game Studio can find them for us automatically. If you hover over SpriteBatch, an icon like the one in the following screenshot will appear on the left-hand side. Click on it and choose the using Microsoft.Xna.Framework.Graphics; option. This will fix the using statement for you. Do it each time such a problem arises.

The base class

The base class is called GameObject2D. The only thing it does is store the position, scale, and rotation of the object and a Boolean that determines if the object should be drawn. It also contains four methods: Initialize, LoadContent, Draw, and Update. These methods currently have an empty body, but objects that will inherit from this base class later on will add an implementation. We will also use this base class for our scene graph, so don't worry if it still looks a bit empty.

Properties

We need to create four automatic properties. The Position and the Scale parameters are of type Vector2. The rotation is a float and the property that determines if the object should be drawn is a bool.

public Vector2 Position { get; set; } public Vector2 Scale { get; set; } public float Rotation { get; set; } public bool CanDraw { get; set; }

Constructor

In the constructor, we will set the Scale parameter to one (no scaling) and set the CanDraw parameter to true.

public GameObject2D() { Scale = Vector2.One; CanDraw = true; }

Methods

This class has four methods.

  1. Initialize: We will create all our new objects in this method.
  2. LoadContent: This method will be used for loading our content. It has one argument, being the content manager.
  3. Update: This method shall be used for updating our positions and game logic. It also has one argument, the render context.
  4. Draw: We will use this method to draw our 2D objects. It has one argument, the render context.

    public virtual void Initialize() { } public virtual void LoadContent(ContentManager contentManager) { } public virtual void Update(RenderContext renderContext) { } public virtual void Draw(RenderContext renderContext) { }

Summary

In this Article we have got used to the 2D coordinate system.

Resources for Article :


Further resources on this subject:


Books to Consider

Cocos2d for iPhone 1 Game Development Cookbook
$ 10.00
Cocos2d for iPhone 0.99 Beginner's Guide
$ 10.00
comments powered by Disqus