Preliminary data model

19 Jan 2014

Just a quick update this time, as there hasn’t been much visible progress this week. I didn’t write any actual code, but I did throw together a class diagram illustrating a very preliminary data model design. By data model, I mean those parts of the game modeling the game world, its inhabitants, and the way they represent and handle their interactions. You could see it as the bottom layer of the application, representing stuff unrelated to rendering or actual game logic:

Here’s the class diagram:

The diagram is a little wide and unwieldy for a web page, as usual with UML class diagrams. If only there were some kind of drop-in JavaScript component that adds proper zooming and panning to any SVG image, one that actually works. I’ve tried two but they both only made things worse… If I’m going to post more class diagrams I may have to revisit this. In the mean time, click tje diagram for a bigger version.

A few notes to go with the design

Let’s start by saying that the data model class diagram is clearly not intended as a blueprint or a specification. Some classes are only partially defined, or contain references to other classes that aren’t defined at all, because they are not really relevant for the purpose of illustrating the data model. I basically made a list of things I could come up with the game engine should be able to represent or handle, and tried to accommodate them in a simple class hierarchy. I will try to keep updating the diagram as the implementation progresses and the data model starts to evolve and converge to its final form, but for now don’t make more out of it than a very rough initial attempt at an architectural overview.

I chose to prefix all class names with ‘K14’, to avoid name clashes with external frameworks. In Objective-C land there’s no such thing as proper namespaces, so everyone prefixes class names with some (hopefully) unique enough characters, which kind of sucks, but that’s just how it is. I would have liked to prefix all class names with ‘2k14’, but class names can’t start with numbers, so I ended up with ‘K14’ instead.

The main class representing the game world is K14World, which can be seen as a representation of the game level data, and the umbrella under which all dynamic game entities live. The K14World class sets up the Box2D world using game level data, and configures the Box2D simulation. The class provides a step method that takes a time delta, which will be the main interface to advance the game logic and the Box2D simulation and will be driven by the game main loop. The step method is also the place where any global game logic will be implemented, for example reacting to inputs, handling game win/lose conditions, keeping track of player score, etc. The class will provide methods to add, remove and retrieve game entities, and to perform world queries such as finding entities visible from some location and viewing direction via the peek method. A K14World instance is created with a list of line-segments defining the level geometry, which may sound overly simplistic, but should be expressive enough to define the geometry of the levels from the original game. Besides the planet color the levels are basically no more than a rectangular area with some polygons cut out where there should be ‘air’ (or would that be vacuum?). In Box2D the surface-to-air transitions of the level can be conveniently specified using line segments, so it seems natural to just stick with them and not over-complicate things by dreaming up some elaborate polygonal data structure or something. For rendering we will probably have to do this in some way or another after all, but I’m not thinking about rendering yet. One last thing to mention about the K14World class is that eventually, almost its entire interface should be scriptable, or in other words: it should be possible to create world data for levels of the game completely using scripts.

The other main class in the diagram is K14Entity which will represent all dynamic entities in the game: actors (the ship controlled by the player, enemies, the reactor placed in every level), projectiles (bullets) and stationary objects (fuel pods, the orb, …). Every game entity is backed by a Box2D body and one or more fixtures, the latter of which are added to the entity programmatically using the addFixture method, which also takes the shape and size of the fixture. How the shapes will be defined isn’t really all that interesting for now, but it will probably come down to either a box, a circle or a convex polygon. Initially, most (all?) entities of the game can probably be represented as a Box2D body with a single fixture, but I can imagine multiple fixtures are needed to create more complex actors, for example to model actors with a non-uniform density distribution (e.g. an elongated ship with 2 heavy bits at opposing ends and 2 individually controlled thrusters). Most of the methods and attributes listed with the K14Entity class are directly related to Box2D concepts. There’s basic body orientation, velocity, applying impulse/force to the entity, and a convenience function peek, which is similar to the identically named method in K14World, but queries a configurable field-of-view for the entity.

Just like K14World, the K14Entity class has a step method, which will be called from the step function of the world class. The step method for actors is where entity-specific game logic will be programmed, for example applying force to the ship when thrust is engaged, joining the orb with the ship when the tractor beam is activated close enough, deciding when a turret should fire, reacting to collisions with the world itself or other actors or projectiles, etc. Entities have a collision callback method onCollision that will be called when Box2D signals a collision, and the class will have to provide some kind of way to join entities together, the specifics of which are not really clear to me yet. For now I’m going with some kind of class representing various types of joints (K14Joint), which can be used to join an entity to one or more other entities. For the ship and the orb, only a single, static joint of a single type (distance joint) would be needed, but again, I can imagine more interesting scenario’s that would require multiple, dynamic joints (a ship that can lift multiple orbs at once for example, or use the tractor beam as a crane).

Each entity will have back-references to the K14World instance, and can use it to signal various events, spawning new entities (projectiles, for example) or removing them (destroying a turret, etc). The exact distribution of game logic between entities and the world class is a still a bit muddy and will have to be worked out further. For now, I made the decision to put something like player fuel with the world class via the K14Player class for example, because I can imagine a scenario where the fuel level would be affected by some global world condition not known to the ship entity, but this could change if I get the idea the game logic gets too scattered around and awkward.

One last thing to mention about the entity classes is the K14ScriptableEntity class. Eventually, I would like to be able to fully script almost any aspect of every entity, instead of having a few compiled-in entity base classes that scripts can only manipulate within the limits of the native base class interface. This would require an entity class that allows adding arbitrary attributes to entities, which is what the ‘custom property’ methods on K14ScriptableEntity are supposed to model. I haven’t given this much thought yet, but I keep it in the back of my mind so as to not work myself into a corner. To get up and running quick, I will start with static, compiled in entity classes though.

The coming week I’ll probably just go ahead and start hammering in the classes in the diagram, and see where I end up. If there’s anything I’ve learned about software development over the years, it’s that it’s never a good idea to over-specify early on in the development process. Big corporations may still love the waterfall model, writing requirements until you drop before you even have a single line of working code, but in the end, it always turns you can’t make the world (the customer, the framework, the hardware, or the problem itself) conform to your requirements, just because it took you so much time to write them down. In my opinion, iterating quickly, getting something up and running quickly, and from there on trying to keep a working system all the time, always trumps the rigorous long grind of trying to completely specify a system the details of which you almost certainly can’t fully appreciate yet.