I have written before about my early experiences with text adventure games and about how I built a GW-BASIC translator to convert one such example game to C#. It was a fun challenge but was admittedly more of a compiler project than a game project. This time, I want to build a .NET Core adventure game library from the ground up.
I have no specific reason for doing this other than “personal enrichment.” What I hope in the end is to have a relatively maintainable codebase using object-oriented principles. I feel that the text adventure format provides a reasonable playground for exploration of design ideas with relatively few yak shaving detours.
These blog posts will document the process, based on notes that I’m taking on each day that I make changes. I do not have a strict schedule or deadline. On some days I might spend an hour building features; on other days, I might spend five minutes. Typically I’ll start with an observation about the current state of the program which spurs a set of improvements or additions.
Enough exposition — let’s move on to the content.
Day 1
I’m starting from a blank slate. I know I’ll need at least four things to start with:
- a library project (“Core”)
- a test project for the library (“Core.Test”)
- a console app using the library (“App”)
- a test project for the app (“App.Test”)
To get started on the right foot, I enable StyleCop Analyzers for each project via an MSBuild Directory.Build.props file. In that props file I also customize the output path so that all projects use a common path — I like a single bin
folder! I verify everything works by adding one simple xUnit.net test to each project. It’s all green. Commit.
So that was all just my “tracer bullet” skeleton if you will. I need to start making this fit the mold that I’ll eventually have with a text-oriented adventure game. There is a concept of an adventure game walkthrough which tells you the commands to use to solve a game (example for King’s Quest III). I’ll borrow this concept and incorporate it into a golden master-style test. I need to change the basic App.Test to read an expected input file walkthrough.in
and produce an actual output file walkthrough.actual.out
and finally compare it to the expected output file walkthrough.out
. The app is so simple at this early stage that this is not hard to do. For testability, I need to design my Game
class to work with TextReader
and TextWriter
rather than depending directly on Console
streams. This enables the walkthrough approach to work as a unit test (you could call it “headless testing”). Commit.
Just a little while back I wrote about a message bus. I figure this will be a great core abstraction for building my adventure game. Thinking ahead a bit, I know I’m going to need components that will have some degree of interaction (e.g. taking an Item
from a Room
into the player’s Inventory
) but I still want loose coupling. The message bus will allow me to use an event-driven approach based on shared messages between classes, as opposed to having the classes all know about each other directly which could very easily become messy. At least, that is my current thinking — I suppose we’ll see later if I’m right. I build my MessageBus
in true TDD style, one test at a time. The first commit brings it into existence with its first test. I continue until the last commit for now which cleans up the subscriber Dispose
pattern (unlike in my blog post, unsubscribe is supported by this message bus via Dispose
).
The final order of the day is to extract a TextConsole
object from the game and into the library. I start with the first commit to form the basic shape and end with the final commit which deletes the custom console logic in the game in favor of the new library abstraction. Note the use of the OutputMessage
via the MessageBus
to which the TextConsole
subscribes. This is an example of the loose coupling I am after; any component, no matter where it is in the system, can display text output by sending a message. (This message-passing approach happens to be similar in spirit to what Sierra did with SCI, their adventure game engine.)
Overall, it was a rather productive day. The game does very little but it has a solid foundation for extension. We’ll resume with day 2 and beyond next time.
Pingback: Building an adventure game: part 2 – WriteAsync .NET