2014-01-27

Simple Solutions

Unlike the in depth investigation and learning required to understand premultiplied alpha, as discussed in my previous post, today's topic is simple and satisfying.

Collections of collections

I've structured the graphical elements of my game into a number of logical layers (not display layers). I won't go into all of them, but to give you an idea, here are some of the key parts, from the most fundamental to the most complex:

  1. The most basic layer is a Texture, which is an abstract class that represents a picture that is loaded from a file.
  2. Above that is an OpenGLES20Texture, which is an implementation of Texture specifically for OpenGL ES 2.0.
  3. Above that is a Graphic, which includes information about the Texture, plus information about its orientation, size, and color.
  4. The next layer up is Widget, which is an abstract class that has a 2D position, an abstract Animate method, and an abstract Render method.
  5. One layer above that is Symbol, which implements Widget and provides a number of concrete features, including references to one or more Graphic instances. I use this class for all of the interactive controls in the game, like the checkmark to start a game, the question mark icon for starting the tutorial, etc.
  6. Another layer above Widget are all of the game UI elements, like Ship, which is the white ball that moves around and bounces off of walls. This also implements Widget and contains the logic to make the ship do what it is supposed to. I have similar classes for the other game UI elements like Wall, Diagonal, etc.

Given the above structure, this means that all graphical elements that I need to render inherit from Widget. In the various game screens I keep track of these in collections that inherit from the following:

public interface IWidgetEnumeration
{
    /// <summary>Returns the <see cref="Widget"/>s in the collection.</summary>
    IEnumerable<Widget> Widgets { get; }
}

For example, the game screen parent class collects all of these multiple collections via the following:

protected sealed override IEnumerable<IWidgetEnumeration> WidgetsToRender
{
    get
    {
        foreach(IWidgetEnumeration collection in LowerWidgetsToRender)
        {
            yield return collection;
        }
        yield return WallsHorizontal;
        yield return WallsVertical;
        yield return Goals;
        yield return Diagonals;
        yield return Poles;
        yield return _diagonalMarkers;
        yield return Ships;
        yield return Collisions;
        foreach(IWidgetEnumeration collection in UpperWidgetsToRender)
        {
            yield return collection;
        }
        yield return _pauseSymbols;
    }
}

What's a little interesting here is that this accessor is an IEnumerable<IWidgetEnumeration>, or an enumeration of collections. This allows subclasses to override LowerWidgetsToRender and UpperWidgetsToRender to add additional widgets as necessary. What's been slightly annoying to me for a while, and what finally gets to the point of this blog entry, is that there have been a number of instances when I needed to only add a single new graphical element in a sub-class. But, since I need to return an IWidgetEnumeration I kept needing to create a collection to contain that single graphical element. This made the override of LowerWidgetsToRender look something like this:

protected override IEnumerable<IWidgetEnumeration> LowerWidgetsToRender
{
    get { yield return _lowerWidgets; }
}

Where _lowerWidgets is an IWidgetEnumeration that contains just one Widget. I couldn't just return the Widget directly, because I must return an IWidgetEnumeration. This seems inefficient to create this collection just to contain one element. But wait, IWidgetEnumeration is an interface. What's to stop me from implementing that directly on Widget so I can just return that directly? Well, that's exactly what I did. I made Widget implement IWidgetEnumeration and added the following simple bit of code to it.

public IEnumerable<Widget> Widgets
{
    get { yield return this; }
}

This allowed me to change the above LowerWidgetsToRender accessor into the following, where _tutorialCounter is the single Widget that was inside the previous collection.

protected override IEnumerable<IWidgetEnumeration> LowerWidgetsToRender
{
    get { yield return _tutorialCounter; }
}

I don't think this refactor improves the performance of the code in any measurable way, but it does make things a bit more straight forward and easier to understand. It's obviously not particularly clever or exciting, but I was happy when I thought of the solution and do feel the code is better because of it.

Next: A Tutorial; Step-by-step.

No comments:

Post a Comment