Chapter 8. Working with Images

Modern computer games are very graphical. They include a bevy of different graphics and backgrounds on which graphics are displayed and moved about. This chapter will show you how to add graphics to your XNA applications and how to display them on the screen. You will learn how to build an electronic picture viewer application and will lay the foundation you will need in Chapters 10 and 11 to create arcade-style computer games.

An overview of the major topics covered in this chapter includes learning:

  • How to add graphics to your XNA games

  • About the different graphic formats supported by XNA

  • How to control the size of graphics

  • How to specify the placement of graphics in the game window

Graphics Can Make or Break Your Games

One of the first things players notice when playing a new game is the quality of the game’s graphics. Dull or poorly drawn graphics may drive away some players before they even give your games a chance. The creation of graphics for computer games is not a simple endeavor. Graphic design is a complicated process that requires advanced software and considerable artistic skills.

It you have good artistic skills, then you should be good to go. If not, perhaps you know someone with a knack for graphic development and can partner with that person when developing your computer games. If this is not the case, you’ll find plenty of sources of quality graphics available on the internet. Some are free and some can be purchased. When acquiring graphics over the internet, be sure you comply with any copyright restrictions the creator may impose.

As important as graphics are to the overall experience of playing computer games, it is fair to say that you can easily create some very cool and very popular games using only the simplest graphics. Take Tetris-style games as an example. Their graphics are colorful but simple and yet these games have become enormously popular.

Great graphics are no guarantee of success. There are plenty of eye-popping games that nobody wants to play. All the graphics in the world won’t make a poorly designed game fun to play.

Adding Graphics to Your XNA Games

XNA allows you to draw graphic images that you supply on the game window. As with drawing text, the drawing of graphics involves the placement of graphic images in the game window based on a coordinate system whose origin begins at the upper-left corner (0,0). This coordinate system is based on pixels. A pixel is the smallest addressable location on the game window.

Trap

It is important to keep a close eye on the size of your graphics files. While XNA will automatically scale them as specified within your applications, using graphics that are larger than you need them to be requires the excess use of system memory, which can have a direct impact on the performance of your applications. Since the Xbox and most personal computers have plenty of memory, this issue is somewhat mitigated. However, the Zune player has limited memory. In addition, the Zune player is limited to a maximum display size of 320 × 240 pixels. Any XNA games that you generate for the Zune Player will benefit from the use of graphics smaller than 320 × 240.

XNA works with a wide range of graphic file formats, each of which has its own specific set of properties. Graphic file formats are identified by their assigned three or four character file extension. For example, Portable Network Graphics (.png) files can be used to store images with lots of detail. PNG files also support transparency, which is most useful in games where graphics move about and interact with one another. Table 8.1 lists and describes all of the different graphics file types that XNA supports.

Table 8.1. XNA Supported Graphic File Types

File Type

Description

.bmp

Bitmap files are uncompressed graphic files that tend to be relatively large compared to other graphic formats.

.jpg/.jpeg

Joint Photographic Experts Group files are compressed files commonly used to store photographs. It is best used for images with fewer than 256 colors but which require great detail.

.dib

Device-independent bitmap files are a variation of BMP files.

.dds

DirectDraw Surface files are DirectX graphic files designed to work with DirectX and feature transparency.

.hdr

High Dynamic Range files are used to store high-quality photographs requiring high levels of detail.

.pfm

A bitmap file type developed by Adobe.

.png

Portable Network Graphics files are compressed graphic files best used for photos and other graphics files that contain a large range of colors. PNG files are generally preferred by XNA developers because of its support for lossless graphics and transparency.

.ppm

Portable Pixel Map files are bitmap files supported by various graphic editor programs.

.tga

Targa files are similar to PNG files but support less color depth.

Managing Game Content

XNA games involve the use of many different types of content, including fonts, graphics, and audio. These types of contents are often referred to as assets. Although it has not been highlighted up to this point in this book, assets are added to your XNA projects and managed through a mechanism referred to as the content pipeline. The content pipeline is responsible for taking the content you supply and converting it to an internal format that XNA can work with.

Once added to the content pipeline, XNA manages all project content for you, alleviating you of any concerns about differences in the types of files you may have added to your projects. As such you can, for example, create XNA games that use JPEG, PNG, and BMP graphic files without having to worry about the format of each individual file.

To verify that an asset has been recognized by XNA, expand the content folder in the Solution Explorer window and select the asset. Properties for the selected asset are then displayed in the Properties window (click on View > Properties Window if that window is not currently open). For example, Figure 8.3 shows the properties belonging to a graphic file named ball.png. The asset name that XNA automatically creates for this file is ball (e.g., the filename less its file extension). Seeing data populated in the Properties window for a graphic file is an indication that XNA is able to work with it and that you will not get any compile errors as a result of adding the graphic file to your XNA project.

Trap

Assets must be uniquely named within the Content folder in order to prevent an error from occurring. However, you can add subfolders to the Content folder in order to further organize your application’s assets, creating, for example, separate subfolders for graphics and audio files. In doing so, your application can support assets with duplication names. To add a new subfolder to the Content folder, right-click on the Content folder and select Add > New Folder. Figure 8.1 shows an example where two subfolders have been added to the application. Note that the Audio folder contains a file named ball.wav and the Graphics folder contains files named ball.png and redbrick.png, resulting in duplication asset names (e.g., two instances of ball), which is okay since they are maintained in different folders.

The Graphics folder contains three graphic files.

Figure 8.1. The Graphics folder contains three graphic files.

Adding Graphic Content to Your XNA Projects

You add graphic files to your applications via the content pipeline in much the same way that you add other types of content (including fonts). The first step in incorporating the use of graphics into your XNA games is to import them into your XNA projects. This is done by right-clicking on the Content element located in the Solution Explorer window and then selecting Add > Existing Item option as shown in Figure 8.2.

Adding a new graphic asset to an XNA project.

Figure 8.2. Adding a new graphic asset to an XNA project.

The Add Existing Item – Content window then appears, as shown in Figure 8.3. Using this window you can select the graphic file that you want to import into the application.

To finish adding the graphic file, select it and click on Add.

Figure 8.3. To finish adding the graphic file, select it and click on Add.

Using this simple set of steps you can add any number of the various types of graphic file types listed in Table 8.1 to your XNA projects.

Trick

XNA makes copies of any resources you add to your projects and places them in its Content folder (e.g., in Solution Explorer). If you want to use the same resource in several XNA applications, you can optionally add a link to the resource. To do so, right-click on the Content folder and click on Add > Existing Item and then click on the down arrow located on the right-hand side of the Add button and then select the Add As Link option as shown in Figure 8.4.

XNA will follow the link to the resource when your XNA application is compiled.

Figure 8.4. XNA will follow the link to the resource when your XNA application is compiled.

A big advantage of using this option is that it allows you to modify a resource external to your XNA projects and then to incorporate the modified resource into your XNA applications the next time you compile them.

Defining Variables for Your Assets

Once you have imported the graphic files your XNA game requires and the XNA content pipeline has generated an asset name for each file, you can begin making use of them within the application. In order to be able to programmatically interact with and control the use of graphics in your XNA games, you must define and associate a unique variable for each asset. For example, the following statement defines a variable named gameBall.

Texture2D gameBall;

In XNA, the graphic images that are drawn are referred to as textures. These textures are drawn on top of 2D or 3D models. In the case of the examples presented in this book, you’ll be working with a 2D model of type Texture2D. This data type manipulates two-dimensional graphics, drawing them on a flat surface area of the screen. Note that as is the case when defining any variable, you must specify the type of variable being defined. In the case of the gameBall variable, a type of Texture2D has been specified. Texture2D is a 2D image that is applied to an object.

Hint

Texture2D is just one of many different types of textures supported by XNA. Other types of textures support the application of images on top of different types of 3D models.

Loading Game Assets

Once you have loaded content into the content pipeline and then defined variables with which to reference them, you can load the assets, allowing you to programmatically work with them. To load an asset, you must use the Content property as shown below. The property belongs to the ContentManager class. The Content property provides access to any asset in the content pipeline. The ContentManager also supports the Load method, which provides the ability to load any asset that has been defined within your game.

gameBall = Content.Load<Texture2D>(@"Graphicsall");

The Load method is a generic method that can be used to load any asset. As such, you can use the method to retrieve and load any type of content resource that you add to your XNA application, be it a 2D or a 3D graphic or an audio file. In order to work, you must tell the method the type of resource being loaded. Note the specification of the @ character, which instructs XNA to process the path string that follows exactly as shown, beginning in the Content folder.

Hint

In the previous example, it was assumed that the ball asset had been defined within the Graphics subfolder located within the Content folder. If the ball asset were instead defined directly within the Content folder, then the following statement would need to be used to load the asset.

gameBall = Content.Load<Texture2D>(@"ball");

Using the Rectangle Structure to Manage the Placement of Graphics

When you load and manipulate graphics into your games, the graphics are often referred to by game developers as sprites. A sprite then is a preloaded graphic file displayed and moved about the screen during game play. In order to control the placement and movement of sprites on the screen, you can use the Rectangle structure.

The Rectangle structure is used to store coordinate and size location for the rectangle (and therefore for its contents). To define a Rectangle, you must adhere to the following syntax.

variableName = new Rectangle(x, y, width, height);

Here, x and y represent coordinate values and width and height specify the size of the graphic in pixels. Figure 8.5 depicts the relationship of these four arguments to the placement of the sprite on the game window.

You can use the Rectangle structure to control the placement of your sprites.

Figure 8.5. You can use the Rectangle structure to control the placement of your sprites.

Working with Assets

Now that you have defined a local variable with which to set up a reference to a graphic image, have loaded it into memory, and have created a rectangle with which to control its placement, you are ready to display it on the screen. To do so, you need to draw it, which you can do using the SpriteBatch class. The SpriteBatch class provides you with access to methods that allow you to begin, end, and perform draw operations.

Hint

To make things easy, XNA automatically defines a SpriteBatch variable for you in the LoadContent method of every new XNA project, as shown here.

   protected override void LoadContent()
   {
       //Create a new SpriteBatch, which can be used to draw textures.
       spriteBatch = new SpriteBatch(GraphicsDevice);
   }

Note that the variable declared here begins with a lowercase character (e.g., spriteBatch) whereas the reference to the SpriteBatch class begins with a capital letter.

Drawing operations should occur with the XNA application’s Draw method. Before you can begin drawing, you must execute the SpriteBatch class’s Begin method, as shown here.

spriteBatch.Begin();

You can then use the SpriteBatch class’s Draw method to draw on the screen. The Draw method has the following syntax.

spriteBatch.Draw(Texture, Position, Color);

The draw method accepts three arguments. Texture identifies the name of a Texture2D variable that specifies the graphic image to be drawn. Position specifies the coordinate location at which the graphic image is to be drawn. Color specifies the tinting color to be applied to (or shined on) the graphic image.

When done drawing, you must execute the SpriteBatch class’s End method, as demonstrated here.

spriteBatch.End();

Bringing It All Together

Okay, let’s put together everything you have learned so far into an example application that displays a picture on the game window. Begin by creating a new XNA project named DisplayingGraphics. Since this application will display a picture, you will need a graphic file. If you do not have one, you can download a copy of the kittens.jpg file along with the source code for this project from the book’s companion web page located at www.courseptr.com/downloads/.

Once you have a graphic file, add it to your new application. At this point you are ready to begin the coding process. Start by adding the following variable declaration statements to your program, shown below in bold. These statements represent game world data and as such should be placed at the beginning of the program, at the start of the Game1 class definition, outside of any of the program’s methods. This will make the variables globally accessible throughout the program.

public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;

    //Define a texture variable to be used to display a graphic
    Texture2D Kittens;

    //Define rectangle with which to display graphics
    Rectangle DisplayRect;
    .
    .
    .

As you can see, the first variable declaration specifies a variable named Kittens as a Texture2D type. This variable will be used to load and reference the graphic file named kittens.jpg later in the program. The second variable is named Display-Rect. It is of type rectangle and will be used to specify the size and location at which the graphic is displayed on the application window.

Now that you have defined global variables representing a graphic and a rectangle, which the application will use to manage the display of its picture, you need to create an instance of a rectangle object within your program. The best place to do this is within the Initialize() method, as shown below. The Initialize() method is the first method that the program executes when it starts. This will ensure its immediate availability in the program.

protected override void Initialize()
{
    //Instantiate the rectangle
    DisplayRect = new Rectangle(0, 0, 640, 480);

    base.Initialize();
}

Here, a Rectangle object has been instantiated. The arguments passed to the Rectangle() method instruct XNA to place the rectangle at coordinates 0,0 (the upper-left hand corner of the application window) and to make it 640 pixels wide by 480 pixels high. The newly created Rectangle is then assigned to the DisplayRect variable, providing you with a means of referring to it in the program.

Now you are ready to load a copy of the kittens.jpg file into memory. To do so, you must use the ContentManager class’s Content property and Load method, as shown here:

protected override void LoadContent()
{
    //Create a new SpriteBatch, which can be used to draw textures.
    spriteBatch = new SpriteBatch(GraphicsDevice);

    //Load the specified graphic file
    Kittens = Content.Load<Texture2D>(@"Kittens");
}

As you can see, the LoadContent() method has been chosen as the best place in which to load the graphic into memory, which makes perfect sense because the LoadContent() method’s job is to load the resources required by the game.

The last step in the development of the DisplayingGraphics application is to perform the actual drawing of the graphic on the application window. To facilitate this, you need to add a little program code to the Draw() method, as shown here:

Hint

Because this application is small and performs a single action (i.e., displaying a picture) you do not need to place any code of your own in the Update() method. However, 99.9% of the time, you’ll find that the Update() method is the central location within your C# program where all the program’s high-level controlling logic is placed.

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.White);

    //Draw the currently specified picture on the application window
    spriteBatch.Begin();
    spriteBatch.Draw(Kittens, DisplayRect, Color.White);
    spriteBatch.End();

    base.Draw(gameTime);
}

As you can see, the SpriteBatch class’s Draw method, embedded within the class’s Begin and End method, is used to draw the graphic on the application window. Note that the Draw method is passed three arguments, specifying the variable name of the graphic, the variable name of the Rectangle within which the graphic will be shown, and the color to be applied to the image. Figure 8.6 shows an example of how the application looks when run.

By default XNA displays the picture starting at the upper-left corner of the window.

Figure 8.6. By default XNA displays the picture starting at the upper-left corner of the window.

Creating a Picture Viewer

The DisplayGraphics application displays its graphic at actual size starting in the upper-left corner of the screen. If the graphic is smaller than the screen size, then the picture ends up displaying on a color background, as is the case of the example shown in Figure 8.6. If you want, you can stretch the images in order to make it fit the entire screen.

Hint

Depending on the current size and dimension of the graphic file, the resulting quality and distortion of the image when stretched across the full application window may vary.

To stretch a graphic across the screen, you must determine the width and height of the screen. Once this information is acquired, you can use it to resize the rectangle within which the image is displayed.

To capture width and height information you can use the GraphicsDevice class’s Viewport structure’s Width and Height properties to retrieve the application window’s width and height, as demonstrated here:

DisplayRect = new Rectangle(0, 0, GraphicsDevice.Viewport.Width,
    GraphicsDevice.Viewport.Height);

As you can see, this statement sets the coordinates for the rectangle, starting at coordinates 0, 0, with a width set equal to the width of the screen and a height set equal to the height of the screen. (See Figure 8.7.)

Retrieving screen width and height.

Figure 8.7. Retrieving screen width and height.

Hint

A viewport is a structure made up of four members---X, Y, Width, and Height. It defines the area within which a graphic is rendered. The Viewport structure belongs to the GraphicsDevice object, which itself is also an object that supports Width and Height properties of its own, representing the screen width and height.

To better understand how to fill the entire application window with a graphic, let’s create a new XNA Windows application. This new application will be a modified version of the DisplayGraphics application. Begin by highlighting and copying all of the code statements that make up the DisplayGraphics application’s Game1.cs file. Now, create a new XNA Windows application and name it Picture Viewer. Next, add Kittens.jpg to the application’s content pipeline. Now paste the code statements you copied from the DisplayGraphics application into the Game1.cs file for your new application, replacing all existing statements. Next, change the application’s namespace statement from

namespace DisplayGraphics

to

namespace Picture_Viewer

At this point, the Picture Viewer application operates identically to the DisplayGraphics application. Now it is time to change that, modifying the application so that it displays the picture in full screen. To make this happen, all you need to do is modify the application’s Initialize() method. The changes to be made to the Initialize() method are shown below in bold.

protected override void Initialize()
{
    int PicWidth; //Define variable used to specify picture width
    int PicHeight; //Define variable used to specify picture height

    //Set picture width and height to be the size of the window
    PicWidth = GraphicsDevice.Viewport.Width;
    PicHeight = GraphicsDevice.Viewport.Height;

    //Instantiate the rectangle
    DisplayRect = new Rectangle(0, 0, PicWidth, PicHeight);

    base.Initialize();
}

The first two statements in the Initialize() method define a pair of variables named PicWidth and PicHeight. The next two statements assign the width and height of the application window to the PicWidth and PicHeight variables. This data is retrieved using the Width and Height properties belonging to the GraphicsDevice class’s Viewport structure. The last change you need to make to the Initialize() method is to modify the statement that instantiates the Rectangle object. As you can see, this statement has replaced width and height arguments of 640 and 480 with the PicWidth and PicHeight variables (e.g., the width and height of the application window).

Figure 8.8 shows how the Picture Viewer application looks when executed. Now the picture takes up the entire application window.

Now the application uses the graphic to fill the entire window.

Figure 8.8. Now the application uses the graphic to fill the entire window.

Creating an Electronic Picture Viewer

Other than displaying a window with an interesting background, there is not much to this application to keep the user interested. Let’s make things more interesting by turning the Picture Viewer application into an electronic picture viewer that displays an assortment of different pictures at two-second intervals. To set this up, you need to make a number of changes to the Picture Viewer application.

For starters, you must add a couple of new picture files to the application. You’ll find copies of two picture files named Newborns.jpg and Pup.jpg available for download from this book’s companion web page located at www.courseptr.com/downloads. Once you have updated the application’s content, you can start making the required code modifications.

The first set of changes to be made are to the application’s game world data and as such are made at the beginning of the program, at the start of the Game1 class definition. These changes are shown below in bold.

public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;

    int timer;         //Used to control timer execution

    //Define texture variables used to display graphics

    Texture2D Kittens;
    Texture2D Pup;
    Texture2D Newborns;
    Texture2D SelectedPic;

    //Define rectangle with which to display graphics
    Rectangle DisplayRect;
    .
    .
    .

As you can see, a number of new code statements have been added. The first of these statements define an integer variable named timer, which the application will use later as part of an internal timer mechanism that controls the display of pictures at two-second intervals. The rest of the code statements define a series of variables. These first two variables (Pup and Newborns) will be used to reference the additional two graphic files. The third variable (SelectedPic) will be used to specify which of the application’s graphics should be displayed at any given moment in time.

The next set of code changes need to be made to the Initialize() method. These changes are highlighted below in bold.

protected override void Initialize()
{
    int PicWidth;  //Define variable used to specify picture width
    int PicHeight; //Define variable used to specify picture height

    timer = 0;   //Set timer to zero
    SelectedPic = Kittens; //Specify the default picture

    //Set picture width and height to be the size of the window
    PicWidth = GraphicsDevice.Viewport.Width;
    PicHeight = GraphicsDevice.Viewport.Height;

    //Instantiate the rectangle
    DisplayRect = new Rectangle(0, 0, PicWidth, PicHeight);

    base.Initialize();
}

The first highlighted statement assigned a starting value of 0 to the timer variable. The second highlighted statement sets the value of SelectedPic to the value assigned to Kittens, specifying the image that is initially displayed by the application.

Now that you have defined the variables the application will use to reference the application’s graphic files, you need to modify the LoadContent() method so that it loads the two additional graphic files into memory. The code statement changes that make this happen are highlighted below in bold.

protected override void LoadContent()
{
    //Create a new SpriteBatch, which can be used to draw textures.
    spriteBatch = new SpriteBatch(GraphicsDevice);

    //Load the application's graphic files
    Kittens = Content.Load<Texture2D>(@"Kittens");
    Pup = Content.Load<Texture2D>(@"Pup");
    Newborns = Content.Load<Texture2D>(@"Newborns");
}

In order to control the timing of the application so that it displays a different picture every two seconds, you need to implement a timer mechanism, making use of the timer variable, which you just added to the application’s game data. To do so, modify the Update() method as shown here.

protected override void Update(GameTime gameTime)
{
    // Allows the game to exit
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
        ButtonState.Pressed)
        this.Exit();

    //Switch picture every two seconds
    switch (timer)
    {
           case 0:
               SelectedPic = Kittens;
               break;
           case 120:
               SelectedPic = Pup;
               break;
           case 240:
               SelectedPic = Newborns;
               break;

           case 360:
               timer = -1;
               break;
    }
    timer++; //Increment value by one

    base.Update(gameTime);
}

As you can see, these statements test the value assigned to the timer variable (defined at the beginning of the program and assigned an initial value of 0). Using a switch code block, four case statements are specified that change the value assigned to SelectedPic when the value of time is equal to 0, 120, 240, and 360, respectively. The value assigned to SelectedPic determines which image file is displayed on the screen.

Remember that by default the Update() method executes 60 times a second. So timer will be equal to 0 when the application first starts. As a result, the value of SelectedPic is set to Kittens. At 2 seconds (e.g., when timer = 120) SelectedPic is set to Pup. At 4 seconds, the value of SelectedPic is set to Newborns. In addition, the value of timer is reset back to zero, restarting the sequence again. Also note that the value of time is automatically incremented by 1 at the end of the Update() method.

The last change you need to make is highlighted below in the Draw() method.

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

    //Draw the currently specified picture on the application window
    spriteBatch.Begin();
    spriteBatch.Draw(SelectedPic, DisplayRect, Color.White);
    spriteBatch.End();

    base.Draw(gameTime);
}

Instead of hardcoding the variable name of the picture to be displayed (e.g., Kitten, Pup, or Newborns), the selectedPic variable is used within the sprite-Batch.Draw() statement. This way the application displays whichever picture the value of selectedPic has been set to.

Once you have made all of the changes that have been outlined, you are ready to test your new electronic picture viewer. Figure 8.9 shows a series of three screen shots, showing each of the three pictures displayed by the application as it executes.

The Electronic Picture Viewer application displays a different picture every two seconds.

Figure 8.9. The Electronic Picture Viewer application displays a different picture every two seconds.

Summary

In this chapter you learned how to make your XNA games much more interesting through the use of graphics. You learned about the various types of graphic types that XNA supports and how XNA’s content pipeline manages the graphics you import into your applications. In addition to learning how to add and display graphics, you learned how to control the placement and size of graphics in the game window. Using the information provided in this chapter, you learned how to create an electronic picture viewer application and in doing so established a foundation needed in Chapters 10 and 11 to begin creating arcade-style computer games.

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.141.173