Okay, let's chat a bit about class design and the pattern I will be using in Spaceballs. I will be using an in-memory, message-driven architecture. Messages will be the nervous system of the game engine. Nearly all communication will take place through messages. The "brain" of this nervous system is the MessageProcessor.
The MessageProcessor is responsible for broadcasting a message to all listeners that are interested in listening to that type of message. But how does the MessageProcessor know which listeners to send a message to? A listener is registered with the MessageProcessor for a specific type of message. Currently, this act of registration is done by the MessageConfigurator. Eventually, the MessageConfigurator could be driven by an external XML file. However, in the mean time, this is done in code within the MessageConfigurator.
The goal to this system is that a Message would have numerous listeners (IMessageHandler) associated with it. For example, if we are talking about a PlayerDamagedMessage, we would have our GameLogic listening to it as well as our PlayerView. The GameLogic would take the PlayerDamagedMessage and update the player's health and send out any more messages necessary. The PlayerView on the other hand might play a damage sound, add some particle effects around the player, and update the Health meter to reflect the new health level of the player.
Let's look at the relationships between these classes.
As you can see from this picture, the SystemController handles the three main messages we have defined that are at the core of our game loop. We will see later what this communication looks like in action.
Let's talk about the other area that drives this system, Services:
Hm, there that *Configurator thing again. I have to admit something: I "stole" the Configurator idea from open source projects out there like log4net. This isolates the act of configuring an object to one class. This way, the object can be configured using different sources, like code or XML. The ServiceConfigurator above configures the ServiceLocator with all the various service types and instances that the engine will be using.
All services registered with the ServiceLocator are driven off of interfaces. To configure the two services above, the ServiceConfigurator might look like the following:
Then a message handler can use the graphics service like so:
IGraphicsService svc = (IGraphicsService)ServiceLocator.GetService(typeof(IGraphicsService));
One of the advantages of having message handlers retrieve services using this pattern is that unit testing becomes easier. I could just as easily register a mock service that implements IGraphicsService with the ServiceLocator. Then I could do assertions on the mock asserting that the message handler is behaving as expected under certain situations. Basically, this is a round about way of doing dependency injection (rather than using Constructor injection of Property injection).
As you can see, there are currently two services in the game. This number will most likely increase throughout the course of development on Spaceballs (i.e. the introduction of various services: GameObjectRepositoryService, ScriptingService, etc.). First, we have our IGraphicsService. This service is essentially a Facade around the graphics system. You will find methods like BeginScene, EndScene, WindowCaption, etc. in this interface. SdlGraphicsService is an implementation of IGraphicsService that is an SDL-specific implementation of IGraphicsService.
The next service is the IStateService. This is the main component in the state machine that ultimately controls the flow of the game. You will find methods like OnUpdate(timeElapsed), ChangeState(IState), etc. in this interface. Let's see some of the core states that are stubbed out in Spaceballs:
I think they are pretty self explanatory. I may go into a breakdown of a flow between all the states in a later update.
Well, that's basically the gist of our services/state layer as it exists today. Nothing too complicated here. Now let's see how messages and services work together, shall we?
To use this architecture to drive our game loop, there are essentially three core messages we will be dealing with (that were outlined above): PreProcessFrameMessage, ProcessFrameMessage, and PostProcessFrameMessage.
So, that's the core of our engine. Although it may seem like overkill at first, it will help us manage the dependencies in the future and enable us to easily add new features.
Do I have any updates on the use of Visual C# Express. Not really. In this case, no news is good news. The IDE has not gotten in my way one time which makes me a happy camper. Of course I miss the Class Designer but you have to have _some_ features that are limited to Visual Studio to encourage people to upgrade :).
Until next time, I'm off to continue implementing my various services (starting with the PlayState). Catch ya' on the flip side :).