Wednesday, January 5, 2011

Breaking everything!

This is partially another progress update, but I might go into a bit more detail this time. (Turns out after writing it is mostly prose, not so much an update. :p )

First off, I spent the week before christmas on a big big change to a fairly low level in the codebase. It took the better part of 3 days (with probably 5 to 6 hours of work each day) to just get the system compiling again, let alone fully functional! We're talking a low level change here. The full system isn't fully functional again yet, even though the core is working correctly, so you know it's a pretty fundamental change.

Here's the skinny. We've got a good 4 sizable and separate layers of the server side implementation. The lowest level manages definitions of game data (the game database). The next implements that data (the zone implementation). There is another layer to construct that data from a more user-friendly format (stored in google documents, actually). Lastly there are various communication layers built on top of the zone implementation that provide specific functionality that can be consumed by the lower layers.

Most of the layers have very few concerns. (As an aside, it's odd to many people to anthropomorphize code, assigning it thoughts or concerns. To me it's important to constrain sections of code by their concerns, so that no section internalizes too much complexity. It makes a large system possible and maintainable when done correctly, even if it sounds silly and doesn't belong in master's theses).

Ahem; Most of the layers have very few concerns. The bottom 2 don't even know what kind of game we're running! In fact, the server and communications modules don't know either, so half of the 3rd major layer doesn't even know it is running an RTS. The obvious boon for this is that we have a game-agnostic engine. We could use this to make another game! But, er, why? Are we actually going to do that? I can't finish 1 game let alone multiple. No, the real benefit is that the code is simplified when it uses fewer assumptions. The foundation of this engine is simplicity; an entity in the game consists of only 3 concepts; data, states, and actions. Everything can be broken down to these 3, which keeps the zone implementation and game database layers very focused.

Now all that's all good, but it makes elementary interaction with the system complicated. For example, imagine some very very high level code that operates the formation system. This system is extremely dependant on knowledge of the skirmish game. But, since it operates on the skirmish-agnostic data, it has to contain a great deal of additional code to translate the low level data into the higher level data. That makes things crazy hard to work with at the top level!

Relatedly, it turns out that building a scenario is extremely complicated. The nature of spawning enemies, teams, groups, formations, scenario organization, respawns, etc etc, is very complicated! The editor for the zone implementation, since it doesn't 'know' anything about a skirmish, is incapable of making these operations clear. Of course, we could just break our rule and make it aware, but that just makes that one layer extremely complicated, since it has to now know the full details about both layers.

Anyone who's played the game within the last 2 years knows we only have one level. Perhaps that's clear why now. Even I really, really didn't want to have to make another.

So it's time to fix that. I'm building a scenario editor. This is a higher level layer that represents data specifically in skirmish-level constructs, and is then compiled into a zone implementation. The compilation process is the only small bit that needs to understand both layers, and is segregated away from the data manipulation. It loses some power in the process, but it gains orders of magnitude in simplicity and usability.

But just building the same kinds of scenarios is boring. There are a pile of necessary, low level features we've been lacking for too long, and its time to figure out how to include them. The first obvious one is to have terrain and obstacles, rather than an infinite empty plane. This, is what took those 4 days.

So. Originally the world system was built without movement constraints. There was no collision, no intersection prevention, no pathing. Everything could move anywhere. But, when we started implementing the RTS mechanics, we found that horribly lacking, and needed to control space better. We opted for a hex-grid based system, which works well for RTS games. The zone implementation was given a higher layer for managing separate spaces. Some objects used the free mover system (non-game entities, spawners, some ground effects), and units used a hex system.

But this then colluded the hex system away from the core assumptions of the zone implementation. The hex system couldn't be assumed, so a great deal of code had to be written to translate between those layers. Complicated! Hacky! It also means that any collisions have to be done on all layers, and between layers, which is just way complicated. Adding navigation, obstacles, and heights to the combination of systems would be largely impossible.

So I settled on implementing the hex map as a core assumption of the engine. It would no longer be implemented 'on top of' the free mover system. Position of entities would no longer be given in 3d coordinates, it would be strictly in hex-space. Everything from proximity detection, range checks, and basic movement would be done directly on the hex-graph. This meant, well, rewriting everything.

But it means that the graph is the de-facto declaration of the world space. Removing cells from the map or adding new ones controls space. So, if I want a bridge, instead of doing some intersection test or complicated movement logic, I just remove the cells around teh bridge, and everything works. The graph is the world.

But yes, changing over the system was difficult. First, the old scenario no longer even makes sense. It doesn't have a grid, and all the positions are in the old system. The old zone is dead! A new way of building zones needs to be built. So then, obviously the next step is the scenario editor. It's started now, but can't do most of what is necessary. I can create a flat network of hex for the map, but that's just to get us back where we were. I cannot place units yet, nor setup start positions. I can setup the scenario type, btu that's about it.

So we have no scenario. :p But we have a kickass new nav and motion engine! That we can't see.

Back to work with me.

That was kind of rambly, wasn't it...


  1. Of course, you need to break everything every now and then. Otherwise you wouldn't know what needed to be fixed.

  2. Nifty. It'll be interesting to see how things play once the game is functional again.

    I'm looking forward to a town skirmish now!