Monday 26 March 2012

Fixing input (event) processing

My first crack at interactive input processing was somewhat muddled: It was trying to be a keyboard state representation and an event processor all at once, with a rather non-idiomatic C-like implementation.  I'd said right at the start of the post that I probably wanted to handle events with callbacks, and sure enough that's how I fixed things.  Rather than stuff everything into a single Input class, I've separated the event loop wrapper from the keyboard representation and tied them together through callbacks.

Sunday 25 March 2012

A first crack at input processing

If you've been following along for the first couple days, you may have noticed some pretty confused use of const in my first few commits.  This has been rectified.

Anyway, one obvious next step, if I want to write an interactive program, is to start handling input in an interactive way.  It turns out that what "handle interactive input" means, for a game like Tungsten at least, depends on what part of the chain you happen to be looking at:
  1. Take events from the toolkit ("SDL_keyboard_event") and translate them into genericized events ("Up arrow pressed") that Tungsten client code can use;
  2. Take generic Tungsten events ("Up arrow pressed") from the above component and translate them into game-specific actions ("Player 1 is hitting Thrust Forward"); and
  3. Take game-specific actions ("Player 1 is hitting Thrust Forward") and translate them into actual game behaviour ("player1->ship()->thrustForward();" or however I decide to implement it that doesn't violate the Law of Demeter).
This sure looks like it's going to take at least three subsystems to manage, none of which I've explored in enough detail to implement or even fully design yet.  Probably I want to handle events via callbacks, and those callbacks are likely to be function objects rather than bare function pointers.  But we need a place to start, and it's easy enough to find one: Translating from toolkit-specific input descriptions ("SDL_keyboard_event", "SDL_SCANCODE_UP") to generic Tungsten input descriptions ("KEY_UP").  We'll start with keyboard input, since I know I want to handle that and it's a pretty static and easy-to-grasp problem; I'm not convinced that Tungsten should care about mouse input at all, and gamepad input opens up a whole 'nother can of worms.

Friday 23 March 2012

OpenGL context and window management

Before I can do any actual 3D graphics work, I need a framebuffer and an OpenGL context.  As mentioned previously, this is a toolkit- and/or platform-specific issue, and it carries with it a bunch of implementation issues around object life-cycle management.

First of all, we have to make sure that the program is in a state where it knows enough about its environment to actually create a window and get a handle to an OpenGL context.  Second, we have to make sure that we aren't inadvertently creating extra windows -- or, worse, aliasing the existing window and clobbering it when a passed-by-value GlContext gets destroyed.  And finally, this is exactly the sort of platform-specific crap I want to keep out of Tungsten's application code and quarantine as much as possible.

Fortunately, we've taken care of much of that with the AppFramework factory class (and its SdlAppFramework implementation).  But setting up and creating an OpenGL window involves a lot more state than anything else I've written about, so it merits some extra discussion.

Choosing a dialect of C++

The C++ language, which inevitably includes not just its own standard library but common additional libraries like Boost and CGAL, is huge.  It supports a wide variety of often partially incompatible programming styles, everything from "C with a working const keyword" to template metaprogramming to exception-based control flow.  Since one of my goals with Tungsten is to learn "modern idiomatic C++", I need to narrow down my focus and pick one style to focus on.

I've chosen the dialect of C++ described in the Google C++ Style Guide.

Coming back to C++ with about a decade of experience as a (sometimes elitist) C programmer, I see plenty of potential for confusion with a lot of the "automagical" features in C++, such as implicit type promotion, default implementations for copy constructors (and their not-always-obvious invocation in pass-by-value function calls), and global/static initialization order ambiguity.  Google's style guide describes a set of conventions for writing C++ code that seems to avoid or at least mitigate many of the potentially subtle bugs inherent in those features.  Like warning signs on power tools, I have to assume that Google's C++ style guide was written in blood.

Beyond the GCSG is Marshall Cline's C++ FAQ Lite.  I remember reading that site back in the days before cable/DSL.  It's more prescriptive than I remember, and when it conflicts with the GCSG I'm probably going to go with Google's wisdom, but it covers a lot of ground.

Finally, the C2 Wiki is probably the smartest collection of programming wisdom on the 'net, so I'll try to refer to their C++ Category as much as possible without getting overwhelmed by options.

Toolkits, libraries, and frameworks

So if I'm going to write a game using OpenGL, the first question is what tools I'm going to use to do it.  I have plenty of prior experience with SDL, and SDL 2.0 looks to be at least plausibly stable enough to rely on for development, but there are plenty of other options available.  My first choice when I started thinking about the project was PEZ, a truly minimalist interface to OpenGL, but it doesn't have any support for real-time keyboard input.  (It doesn't preclude it, either.)  There's always freeglut if I want to stay inside the OpenGL bubble, too.  On the other side of the spectrum, I might end up looking to use a more featureful game engine like Irrlicht or Ogre3D.

For now, I'm sticking to SDL 2.0, but I want to be able to change toolkits later on without having to rip the guts out of my code.  I also want to be able to react to changes in the SDL 2.0 spec -- it's still in development, remember -- without having to rip the guts out of my code.  So I'm abstracting all the toolkit-specific stuff that I can behind a framework of abstract interfaces that I can implement on a toolkit-specific basis.

Tungsten!

Okay, let's get this show on the road.

Tungsten is a simple "Deathmatch Asteroids" game project.  I've written a couple games like this in the past, and now that I've racked up a decade or so of software development experience it seems like the right time to write another one.  If you've played Star Control, you know exactly where I'm going with this.

Tungsten is also a learning project for me.  I've spent the past eight or nine years using C (specifically C99) as my language of choice for any heavy lifting I've had to do, with a few forays into Ruby and Haskell.  I started my programming career (think high school) as an enthusiastic and naive C++ programmer, but over the years I've come to appreciate the stark simplicity of the C language (particularly after wading through hundreds of thousands of lines of student code as a teaching assistant).  Modern C++, particularly the emergent C++11 dialect, is vastly different from what I remember (pointers and arrays are deprecated?  Nobody ever really calls delete any more?), and I'm trying to approach it as a completely new language and leave as many of my C habits behind as possible.  This also goes for OpenGL 3.x/4.x -- it's a very different style of graphics API from the immediate-mode interface I learned  as an undergrad.

Finally, Tungsten is a part of my resumé.  I want to be able to point people to this project and say "Look!  This is how I design a program; this is how I write code; this is how I go about learning a new language and API".  This is where I get to describe my thought processes in detail and walk through the design decisions and tradeoffs I've made.  Maybe we can chat about it in the comments.

On with the show!