Grammars
Object-Oriented Programming
UML diagrams (at least, what they are)
A design pattern is a way of solving a commonly occurring problem that occurs in programming. Many design patterns exist to solve a wide variety of problems. One of the first books written on the topic was written by the famous “Gang of Four” who took several of these common solutions and published them in one source. Their book can be a difficult read, especially when lots of UML diagrams complicate what should be a relatively straightforward solution. One of these patterns found its way into this game project.
In this sidescroller game, each level has a series of objectives or tasks that have to be completed in order for the level to be considered completely done. I contemplated for a long time how to solve this problem. The core design question was this: How do I allow for multiple objectives? Traditional sidescrollers simply cared about crossing the exit. (i.e. The old-school Super Mario Bros. or Kirby's Dream Land) How could I allow for quests like Super Mario 64, where one of the objectives was to “collect the eight red coins?”
I first listed a few of the objectives I wanted to program for:
- Allowing for someone to collect a certain number of items
- Touching/crossing/passing by a particular object
- Completing a level/room in a given amount of time.
I then considered how I could generalize all of these into something I could model using C#. I came up with a grammar to describe each of these actions. For instance, if I wanted to have a player collect five coins in a level with seven, I created a string “collect 5 from (c1, c2, c3, c4, c5, c6, c7)” with c1...c7 being the identifiers of each of the coins in the map file. Similar grammars were made for the other items (or they are being created now).
I then realized that this problem became easier to solve if I combined this grammar with an adapted version the Visitor design pattern from the Gang of Four. Essentially, the Visitor pattern allows for me to take all of a certain set of objects and “visit” them: Look at their properties, run an algorithm on them, and then take the summarized data the class gathered and do something useful with it. This helps with the complexity of the program, putting algorithms on similar sets of data in their own class, with a common interface.
I implemented this idea by creating a concrete implementation of the following interface for each type of objective:
public interface IObjectiveVisitor {
// At the end of running the visitor, has the objective
// condition been met?
// (e.g. Have we collected 5 coins out of 7?)
bool IsObjectiveMet();
// Reset the internal variables
void Reset();
// Set the condition we are checking for.
void SetCondition(string condition);
// Visit each of the objects and analyze things pertaining
// to the condition
void Visit(GameObject obj);
}
In the traditional “textbook” implementation of a Visitor, this would also include an “accept” method on each subclass of GameObject, telling the game object which objective is being checked for. In this project, I chose to have an "Objective Manager" that handles all of the objectives and which are completed, eliminating this need. Thus, this is an adapted version of the Visitor pattern.
This solution runs quite well. Once an action is performed on a given object, all of the objective visitors are run on all of the game objects to see if any new objectives have been completed. I may give more details in a later post.
0 comments:
Post a Comment