Monday, August 17, 2009

Game Architecture: Contexts

I have had my dabble in game development. But ever since I left the game industry back in 2006, I have been spending more of my time on my full time career, which is J2EE related development. And even till then, my main experience were in J2ME and some symbian.

And now that I am doing some hobbyist game development, I had actually looked around to find out more about game architecture, which had been a sparsely undocumented and unchartered waters. The best books I had seen were the following:

1. Game Architecture and Design: Learn the Best Practices for Game Design and Programming http://www.amazon.com/Game-Architecture-Design-Practices-Programming/dp/1576104257
2. 3D Game Engine Design : A Practical Approach to Real-Time Computer Graphics (The Morgan Kaufmann Series in Interactive 3D Technology) http://www.amazon.com/Game-Engine-Design-Interactive-Technology/dp/1558605932

And recently, I stumbled upon GameArchitect(http://gamearchitect.net/), which provide many interesting ideas. There was one which in particularly interest me, which was An Anatomy of Despair: Managers and Contexts (http://gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/), which was singleton vs grouping them into a single context class.

Coming from an enterprise development background, I long knew the evil of singleton. And the idea of an environment context object sounds very attractive. I could simply pass a single object around, and all my objects would have access to all system services, from file management to logging manager to profiler to system timer and so on!

Surely there is no drawback.

Or is there?

After using it for a while, it became an extremely bad idea.

It complicates unit testing for one. There are some part of the game that I want to test, eg. script parsing. In order to test them, under the normal java/c# approach, I would mock the context. But being in C++, mocking is definitely more tedious (no I did not try out google mock).

In fact, for some of them, the services are not meant to be interfaces. They are concrete implementations. For example, a profiler service. It's not exactly a costly instantiation, but still, unnecessary. In the world of unit testing, I should not be creating services that a particular test does not use. They instead become unnecessary baggage to be carried around.

Of course, that brings up the second problem. Unnecessary baggage. It is very tempting to give the context to every object/component in the system. Even if all they need are the logging manager, and maybe profiler. Not all of them need the object repository. Not all of them need the script interpreter. But they still carry along the unnecessary baggage. This just irks me. A class has unnecessary knowledge of another interface.

For now, I'm dropping the idea of contexts. That does not mean that I am reverting to singletons. Right now I'm simply going to pass whatever is needed. Nothing more, nothing less. The dependencies are clearer in the class declaration.

As for global services like logging and profiler? These are more of debugging tools. For a game, well... I don't see them to be fully enabled. It will probably be wrapped by MACRO. And since a class might not use them, it makes more sense for them to be C style functions or singletons. Such dependency should not be visible as a constructor argument or setter method.

Of course, if this is an enterprise application, I probably will have them passed as an argument... they would still be in used during production.

blog comments powered by Disqus