IT tutorials
 
Mobile
 

Windows Phone 7 : Designing the Game Framework (part 1) - The GameObjectBase Class, The SpriteObject Class

2/25/2013 6:26:29 PM
- How To Install Windows Server 2012 On VirtualBox
- How To Bypass Torrent Connection Blocking By Your ISP
- How To Install Actual Facebook App On Kindle Fire

XNA's Game class already provides a flexible engine for initializing and driving the game. What it doesn't offer is any built-in mechanism for managing the game objects that we want to display and manipulate inside the game.

The examples that we have been looking at have provided mechanisms for moving small numbers of sprites, but any real game needs to track significantly more objects than this. Instead of building all the objects into the game on an ad hoc basis, we will build the framework inside which the objects can be managed.

These objects will be used for all elements of the game that we need to draw: they will be the falling tiles in a game of Tetris; the ghosts, dots, and player in Pac Man; and all the spaceships and bullets in Space Invaders. Objects will know various things about themselves (such as where they are on the screen) and will allow us to manage a consistent and simple mechanism for moving and rendering efficiently. Providing a simple and flexible implementation for game objects is the primary area that we will address in the design and construction of the game framework.

We will take advantage of the .NET object orientation features in the design of the framework. We will create an abstract base class that supports a core set of functions that will be generally useful in any of our game objects.

All these classes will be placed into a separate Game Library project named GameFramework, which will allow us to reuse them between projects without having to reimplement them or share source files. Instead, we will just add a reference to the framework library, and its classes will be immediately available.

1. The GameObjectBase Class

The first class is the GameObjectBase class, an abstract class from which all our game object classes will ultimately be derived. The functionality within GameObjectBase is limited, but we can declare collections of game objects by using this class without initially needing to know any more details about which types of objects we are going to store.

The actual functionality within the class is very limited. In the class constructor, we take a Game object as a parameter. You might recall that the XNA games that we write all have their main class derived from the Game class, so storing it provides a mechanism that allows code within this class and any derived class to access the main Game object. The constructor is shown in Listing 1.

Example 1. The GameObjectBase class constructor
/// <summary>
    /// Constructor for the object
    /// </summary>
    /// <param name="game">A reference to the XNA Game class inside which the object
    /// resides</param>
    public GameObjectBase(Microsoft.Xna.Framework.Game game)
    {
        Game = game;
    }

					  

In addition to the constructor, we define a single method, Update, which will be used to update the state of the object. It accepts a GameTime object as a parameter so that timing information can be extracted, just like the Update method in the main Game class. The function does nothing more than increment a variable, UpdateCount, so that we can tell how many updates have taken place; its code can be seen in Listing 2. Our derived classes will override this function, however, so that they can perform the actual updates of their corresponding game objects.

Example 2. The Update function
/// <summary>
    /// Update the object state
    /// </summary>
    /// <param name="gameTime"></param>
    public virtual void Update(GameTime gameTime)
    {
        // Increment the UpdateCount
        UpdateCount += 1;
    }

2. The SpriteObject Class

Derived from GameObjectBase is the SpriteObject class (see Figure 1). This is a concrete class (not abstract) in which we will add all the basic functionality that we might want to use to position and draw our sprites. In its basic form, the class is capable of maintaining a sprite's position, scaling, rotation, and origin, a texture for it to render with, a color to tint with, a source rectangle for partial texture rendering (if required), and a layer depth to help define the order in which the sprites should be rendered.

Figure 1. The SpriteObject's position in the framework project

It doesn't contain any object logic, however: it knows nothing about how to move or change any of its properties. This logic will be added by deriving further classes from SpriteObject, as we will see shortly. Such derived classes are generally what we will use when we build our games, but if a simple static sprite is all that is required, instances of SpriteObject can be created and added to the game.

Various different constructor overloads are provided to allow the calling code to easily set some of the common properties of the class. The signatures for each of these are shown in Listing 3 (the bodies of the functions are omitted for brevity because they are simply setting the provided parameter values into the class properties).

Example 3. The available constructors for the SpriteObject class
public SpriteObject(Game game)

    public SpriteObject(Game game, Vector2 position)

    public SpriteObject(Game game, Vector2 position, Texture2D texture)

The class offers a lot of additional properties to allow us to control the position and appearance of the sprite, however, as follows:

  • SpriteTexture stores a reference to a texture that can be used to render the sprite. The default implementation of the Draw method (which we will discuss in a moment) will use this texture to draw the sprite, though this behavior can be overridden if required.

  • PositionX and PositionY store the sprite's position as float variables, whereas the Position property represents the same position as a Vector2. Any of them can be set or retrieved, though they update the same internal variables so setting PositionX or PositionY will have an immediate effect on the return value from Position, and vice versa. The reason they are stored as floats as well as a Vector2 is that Vector2 is a structure rather than a class, so when we read the Position property we are actually given a copy of the underlying structure. This copy's properties cannot be modified, and Visual Studio will give an error if, for example, you attempted to assign a value to Property.X. So instead we expose the individual coordinates for modification and interrogation, and the Vector2 structure for passing into functions that expect a value of this type.

  • OriginX, OriginY, and Origin store the sprite's origin coordinate, using a pair of floats and a Vector2 structure just as for the Position properties in the previous paragraph. The default origin is the coordinate (0, 0).

  • Angle stores the angle of rotation in radians, defaulting to 0.

  • ScaleX and ScaleY are float values that allow for uniform or non-uniform scaling to be applied to the sprite. In addition to this, the Scale property represents the same values as a Vector2 structure.

  • SourceRect is a Rectangle structure with which we can define a subregion of the sprite's texture that is to be rendered. If the structure is "empty" (its values are all zero), this feature will be ignored, and the whole texture will be rendered. Its default state is to be empty.

  • SpriteColor allows tinting and alpha levels to be applied to the sprite. It defaults to Color.White, with full intensity alpha.

  • LayerDepth stores a float value that will be used for setting the rendering order of the sprite if the appropriate mode is set when calling the SpriteBatch.

As you can see, the object allows virtually all the basic sprite state to be stored and maintained. Creating an instance of this class (or a class derived from it) allows a good deal of flexibility for displaying the sprite without needing any further variables to be defined. That greatly simplifies the repetitive code that we would otherwise need to write to store all this information.

In addition to storing the sprite state, we also add a virtual function called Draw. Just like the Draw method in the XNA main Game class, we expect a GameTime object as a parameter, but we also require a SpriteBatch object to be passed in. Because this class is dedicated entirely to drawing sprites, it makes sense to expect a SpriteBatch, and we need access to one so that we can call its Draw method to display our sprite to the screen.

The default behavior of the SpriteObject.Draw method is to draw the configured sprite to the screen. It can do this only if it has a valid SpriteTexture, so this is checked first. After that, one of two different calls is made to SpriteBatch.Draw, depending on whether a SourceRect has been specified. The code for the Draw function is shown in Listing 4.

Example 4. The SpriteObject class Draw function
public virtual void Draw(GameTime gameTime, SpriteBatch spriteBatch)
    {
        // Do we have a texture? If not then there is nothing to draw...
        if (SpriteTexture != null)
        {

// Has a source rectangle been set?
            if (SourceRect.IsEmpty)
            {
                // No, so draw the entire sprite texture
                spriteBatch.Draw(SpriteTexture, Position, null, SpriteColor, Angle,
                                        Origin, Scale, SpriteEffects.None, LayerDepth);
            }
            else
            {
                // Yes, so just draw the specified SourceRect
                spriteBatch.Draw(SpriteTexture, Position, SourceRect, SpriteColor, Angle,
                                        Origin, Scale, SpriteEffects.None, LayerDepth);
            }
        }
    }

					  

Other classes that derive from SpriteObject can, of course, override the Draw method and supplement or entirely replace the default functionality as needed.

A final property present within the class can be used to help determine the area on the screen in which the sprite is being drawn. BoundingBox calculates this by looking at the sprite's position, its origin, the texture size, and the current scale. These are factored into a Rectangle structure that can then be used for simple collision checks, for example. The code for this function is shown in Listing 5.

Example 5. Calculating the sprite's bounding box
public virtual Rectangle BoundingBox
    {
        get
        {
            Rectangle result;
            Vector2 spritesize;

            if (SourceRect.IsEmpty)
            {
                // The size is that of the whole texture
                spritesize = new Vector2(SpriteTexture.Width, SpriteTexture.Height);
            }
            else
            {
                // The size is that of the rectangle
                spritesize = new Vector2(SourceRect.Width, SourceRect.Height);
            }

            // Build a rectangle whose position and size matches that of the sprite
            // (taking scaling into account for the size)
            result = new Rectangle((int)PositionX, (int)PositionY,
                                (int)(spritesize.X * ScaleX), (int)(spritesize.Y * ScaleY));

            // Offset the sprite by the origin
            result.Offset((int)(-OriginX * ScaleX), (int)(-OriginY * ScaleY));

            // Return the finished rectangle
            return result;
        }
    }

					  

The code first determines the size of the texture being displayed, which is the width and height of the whole texture, or the size defined by the SourceRect property if it has been set. It then creates its Rectangle structure by using the sprite position for the Left and Top values and the calculated texture size for its Width and Height. The texture size is scaled as appropriate to ensure that the resulting rectangle matches the size of the texture displayed on the screen.

The rectangle is then offset by the origin position. The further the origin moves toward the right, the further the sprite itself moves to the left, and so we subtract the origin position from the rectangle's top-left corner. Once again, this is scaled as appropriate.

The finished rectangle is then returned.

This function works well for many sprites, but those that have been rotated will not produce the expected results. The code does not take rotation into account, and so rotated sprites—and particularly those that are not square in shape—will protrude outside of the bounding box. If your sprites need more sophisticated bounding box calculation, this will need to be implemented in your derived game object classes.

 
Others
 
- Windows Phone 7 : Getting Started with XNA - Other Graphics Options
- iPad : Your Calendar - Adding New Calendar Appointments, Events (part 2)
- iPad : Your Calendar - Adding New Calendar Appointments, Events (part 1)
- Java ME on Symbian OS : Handling Diversity - Using Adaptive Code and Flexible Design to Handle Diversity
- Java ME on Symbian OS : Handling Diversity - Detecting Diversity using Properties
- BlackBerry Bold 9700 and 9650 Series : Email Set Up - Sync Google Contacts Using Email Setup
- BlackBerry Bold 9700 and 9650 Series : Email Set Up - Maintaining Your Email Accounts
- iPhone Programming : Other Native Platforms - MonoTouch
- iPhone Programming : Other Native Platforms - PhoneGap
- Windows Phone 8 : Phone-Specific Controls (part 2) - Pivot Control
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
programming4us programming4us
 
Popular tags
 
Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS