IT tutorials
 
Mobile
 

Windows Phone 7 : Designing the Game Framework (part 3) - The GameHost Class

2/25/2013 6:28:39 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

4. The GameHost Class

The next framework class is the GameHost class. This class holds collections of various objects that we will want to use in our games, specifically Dictionary objects containing textures and fonts, and a List of the actual game objects. The game objects are stored in a list containing objects of type GameObjectBase, which allows us to store within it the derived SpriteObject and TextObject game objects that we have so far discussed, as well as any game-specific classes that derive from any one of these.

The class also contains some simple methods that we can call to save having to write boilerplate functions in the main project's Game class.

The GameHost position within the framework is shown in Figure 3. It derives from the Microsoft.XNA.Framework.Game class (from which all actual game projects must derive a single class). This means that in our game projects we can derive the main game class from GameFramework.GameHost instead of from Microsoft.XNA.Framework.Game. As a result, we get all the functionality of the XNA Game class and all the functionality added into GameHost.

Figure 3. The GameHost class position in the framework project

The object collections are accessed by the following properties:

  • Textures stores a dictionary of Texture2D objects. Any stored texture can be easily retrieved via its named dictionary key.

  • Fonts performs the same function for SpriteFont objects.

  • GameObjects is the list into which all the game's active objects will be placed. This is defined as a generic collection of GameObjectBase objects.

In many cases, we can add game objects to the class and then simply allow them to carry out their own tasks until one encounters a circumstance that requires us to interact with it. For example, in an Asteroids game we can simply set each asteroid object to move around the screen and then pretty much forget about them until one happens to collide with a bullet or the player's ship. When we detect that this has happened, we will process the asteroid as required. There is no reason to track the asteroids other than by having them present in the GameObjects list.

For other objects, however, we might need to be more proactive. The player's spaceship, for example, will need direct modification from the game so that it can respond to player input. To keep track of this, a separate reference to the game object will need to be stored in the game itself. The same applies for text objects whose values need to be updated (for the player score, for example).

Once the game is initialized and running, two additional methods are available to simplify the code in the main game class:

  • UpdateAll loops through all the items in the GameObjects list and calls the Update method on each one. This function can be called from the main game class's Update method to keep everything moving forward.

  • DrawSprites identifies all SpriteObject (and derived) objects in the GameObjects list and calls the Draw method of each. It requires an initialized SpriteBatch object to be passed in, and it is the calling code's responsibility to call its Begin and End methods. Keeping the call to Begin in the game class means that it can be given whatever parameters are appropriate for the sprite batch operation. A second overload draws only the sprites with a specified texture.

  • DrawText identifies all TextObject (and derived) objects in the GameObjects list and calls the Draw method for them, just as DrawSprites does for SpriteObject game objects.

DrawSprites and DrawText are essentially provided simply for convenience and need not be used if additional functionality is required. If several sprite batches are needed with different parameters, for example, and the DrawSprites overload that separates the sprites by texture is not sufficiently flexible, the game class can simply implement its own custom version of this function.

The code within UpdateAll is more complex than a simple for each loop and it is worth some further exploration, so let's take a look at what it is doing. The reason for its complexity is that, when .NET is iterating through a collection, GameObjects in this case, the collection is not allowed to be modified in any way. Attempting to add an object to, or remove an object from, the collection will result in an immediate exception because, if the order of the object is changed in any way, .NET cannot ensure that it hasn't skipped over or double-processed any of the objects.

A simple way in which we could have worked around this would be to call the collection's ToArray method and iterate over this instead of the collection, as shown in Listing 9.

Example 9. A simple but flawed approach to iterating over the objects while still allowing collection modifications
foreach (GameObjectBase obj in GameObjects.ToArray())
        {
            obj.Update(gameTime);
        }

This appears to work very well and meets our objectives: all the objects are updated and each can perform any modification to the collection that it wants. It has a flaw, however: every time the loop is prepared, it creates another array of object pointers and as a result consumes a little extra memory. This might not sound important, but the method is being called 30 times per second and might be processing hundreds of objects. This quickly runs into noticeable amounts of memory being used: 30 updates per second with 100 objects requiring 4 bytes per object pointer results in 12,000 bytes allocated per second.

When memory is being allocated this quickly on a device running .NET Compact Framework (CF), the garbage collection ends up triggering on a frequent basis. Each time this happens, the application briefly pauses while the process executes. This is not likely to be noticed in an e-mail or diary application, but in a fast-running game it will really attract the user's attention in an unpleasant way.

We therefore want to try to minimize the amount of memory that is allocated within the game loop. To solve this within the GameHost, a private array of GameObjects is declared at class level, as shown in Listing 10. We will use this array over and over again, minimizing memory allocation as far as possible.

Example 10. The class-level _objectArray variable
public class GameHost : Microsoft.Xna.Framework.Game
{

    //--------------------------------------------------------------------------------
    // Class variables

    private GameObjectBase[] _objectArray;

    [...]

					  

This array initially starts off uninitialized. The UpdateAll function first checks for this state; when found, it creates the array with enough space to hold all the current known objects in the GameObjects collection, plus space for 20 percent more. Allowing a little extra like this means that we don't need to reallocate the array every time a new object is added. If there are fewer than 20 objects in the game, it allocates a minimum of 20 to prevent reallocation each time a new object is added with low object numbers (adding 20 percent to 4 objects results in still only 4 objects after rounding).

If UpdateAll instead finds that the array has been previously created, it checks its capacity against the current game object count. If the object count fits within the array, it leaves the array alone. On the other hand, if the object count is now too large for the array, it reallocates the array by creating a new object collection, once again providing space for 20 percent more objects than are currently present.

This ensures that the array starts at a reasonable size and increases in stages rather than one object at a time. The old arrays that are discarded will be garbage-collected in time, but will be small in number and so won't waste large amounts of memory.

The opening section of UpdateAll that performs the steps discussed so far can be seen in Listing 11.

Example 11. The beginning of the UpdateAll function, allocating space in the _objectArray variable
public virtual void UpdateAll(GameTime gameTime)
    {
        int i;
        int objectCount;

        // First build our array of objects.
        // We will iterate across this rather than across the actual GameObjects
        // collection so that the collection can be modified by the game objects'
        // Update code.
        // First of all, do we have an array?
        if (_objectArray == null)
        {
            // No, so allocate it.
            // Allocate 20% more objects than we currently have, or 20 objects,
            // whichever is more
            _objectArray = new GameObjectBase[
                                        (int)MathHelper.Max(20, GameObjects.Count * 1.2f) ];
        }
        else if (GameObjects.Count > _objectArray.Length)
        {
            // The number of game objects has exceeded the array size.
            // Reallocate the array, adding 20% free space for further expansion.
            _objectArray = new GameObjectBase[(int)(GameObjects.Count * 1.2f)];
        }

					  

With the array created at an appropriate size, UpdateAll now populates it. This takes place for every update because we cannot tell at a high level whether the objects have been manipulated (checking the object count would not be sufficient as objects might have been removed and inserted in equal numbers). This might seem wasteful, but is really just assigning object pointers and so is extremely quick to execute.

There is another important thing that we need to do with the array, and that is to release any references we have to objects that have been removed from the GameObjects collection. Without this, our array could potentially keep them alive for extended periods within the elements past the end of our main object loop, even though nothing is actually using them any more. Releasing them ensures that their memory can be freed the next time garbage collection runs.

Removing these references is simply a matter of setting the array elements to null for all objects other than those in the GameObjects collection. As we are looping through the array to tell each object to update, it is easy to just continue looping to the end of the array setting the elements that we are not using to null. This will release all the expired object references.

The array population section of UpdateAll is shown in Listing 12.

Example 12. Copying the current game object references into _objectArray and removing expired object references
// Store the current object count for performance
        objectCount = GameObjects.Count;

        // Transfer the object references into the array
        for (i = 0; i < _objectArray.Length; i++)
        {
            // Is there an active object at this position in the GameObjects collection?
            if (i < objectCount)
            {
                // Yes, so copy it to the array
                _objectArray[i] = GameObjects[i];
            }
            else
            {
                // No, so clear any reference stored at this index position
                _objectArray[i] = null;
            }
        }

					  

With the array populated, we can now iterate through the array and tell each object to update. The GameObjects collection is not being iterated, and so the objects are free to manipulate it in any way they want without exceptions occurring.

We know how many objects we actually have to process because we already queried the GameObjects.Count property and stored it in the objectCount variable. Remember that we can't rely on the array length for this because we are allocating space for additional objects. The stored object count value has an additional use in the update loop: because the GameObjects collection might be modified during the loop (which would affect the result returned by the Count property), we cannot rely on the value being stable while the updates are processing.

Once we know how many objects to process, it is then just a matter of calling the Update method for each. The remainder of the UpdateAll function is shown in Listing 13.

Example 13. Updating the game objects
// Loop for each element within the array
        for (i = 0; i < objectCount; i++)
        {
            // Update the object at this array position
            _objectArray[i].Update(gameTime);
        }
    }

When the number of game objects is static, the array will persist without any reallocations from one update to the next. Each time the number of objects is allocated past the current maximum, a reallocation will occur, but the frequency of these reallocations will be limited by the addition of the 20 percent buffer for additional objects. Once the game's actual maximum is reached, no further allocations will take place at all. The only overhead for all this is two simple loops through the array, one to copy references from the GameObjects collection, and the other to draw the objects and remove expired object references. In return, we gain complete flexibility to manipulate the set of objects from within each object's Update code.

5. The GameHelper Class

Finally there is the GameHelper class, a place into which generally useful functions can be added (similar to XNA's own MathHelper class). The class is declared as static, so it cannot be instantiated.

For the moment, the class just contains a number of functions relating to random numbers. It hosts an instance of a Random object and exposes several overloads of a function called RandomNext, each of which returns a random number. There are two reasons for having these functions:

  • They provide our game code with immediate access to random numbers (a common requirement for game development) without needing to instantiate its own Random instance.

  • They add some useful overloads that return random float values, either between zero and a specified upper limit, or between an arbitrary lower and upper limit. They can be extremely useful when randomizing game objects because they use float variables for nearly all their properties.

 
Others
 
- Windows Phone 7 : Designing the Game Framework (part 2) - The TextObject Class
- Windows Phone 7 : Designing the Game Framework (part 1) - The GameObjectBase Class, The SpriteObject Class
- 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
 
25 Inspiring Game of Thrones Quotes
 
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