An API for planets

15 Feb 2014

As mentioned in the previous post, the primary goal I set for the past week was to implement a minimal API on top of the current set of planet/entity skeleton classes, that would allow creating and populating ‘planet 0’ of the original game. Starting from the set of skeleton classes I already added to the project, this turned out to be a pretty minor effort. So without further ado, here’s the code snippet that sets up ‘planet 0’.

How to setup ‘planet 0’

NSArray *surface = @[
   [NSValue valueWithCGPoint:CGPointMake(-22.0, 5.0)],
   [NSValue valueWithCGPoint:CGPointMake(-16.0, 5.0)],
   [NSValue valueWithCGPoint:CGPointMake(-12.0, 2.0)],
   [NSValue valueWithCGPoint:CGPointMake(-6.0, 2.0)],
   [NSValue valueWithCGPoint:CGPointMake(-3.0, 0.0)],
   [NSValue valueWithCGPoint:CGPointMake(3.0, 0.0)],
   [NSValue valueWithCGPoint:CGPointMake(3.0, 3.5)],
   [NSValue valueWithCGPoint:CGPointMake(7.0, 3.5)],
   [NSValue valueWithCGPoint:CGPointMake(9.0, 5.0)],
   [NSValue valueWithCGPoint:CGPointMake(15, 5.0)]
];

K14Planet *planet = [[K14Planet alloc] initWithSurface:surface gravity:CGVectorMake(0.0, -9.81)];

K14Entity *ship = [planet createEntityWithClassName:@"K14Ship"];
ship.position = CGPointMake(-9.0f, 10.0f);

K14Entity *fuel_pod = [planet createEntityWithClassName:@"K14FuelPod"];
[fuel_pod glueToSurfaceEdgeWithIndex:2 atFraction:0.5f];

K14Entity *reactor = [planet createEntityWithClassName:@"K14Reactor"];
[reactor glueToSurfaceEdgeWithIndex:6 atFraction:0.25f];

K14Entity *turret = [planet createEntityWithClassName:@"K14Turret"];
[turret glueToSurfaceEdgeWithIndex:3 atFraction:0.5f];

This piece of code is lifted straight from the application delegate, I just stuffed it in there to have something to test the game engine during development. I did also add an XCTest unit test including some assertions on the created entities, but since I’m almost at the point where I’d like to have a simple render class to visualize planets, I wanted to have an initialized K14Planet instance inside the actual application. For now the application delegate will have to do as a stub for planet setup.

As you can see, planet and entity creation closely follows the API documented in the ‘Skeletons’ post, so I wasn’t too far off with the ‘design upfront’ this time ;-). To save myself the hassle of calculating the exact x,y coordinates of entities after they are created, I’ve added a method glueToSurfaceEdgeWithIndex to K14Entity, which positions the entity in such a way that it sits directly on an edge of the planet surface. This method basically calculates a point along a surface edge at the specified fraction between its endpoints, then gets the center point of the entity bounding box, and locates it at exactly the right height and angle, such that the bottom of the bounding box aligns with the edge. Nothing magical, just some simple vector calculations. I did find out that that Box2D has no easy way to calculate the bounding box of a body, so to get a bounding volume for an entity, you have to iterate all of its fixture shapes and combine their bounding boxes. This logic is hidden behind a dynamic property K14Entity.bbox.

Initialization code for an entity (in this case the reactor) is about what you would expect: create a Box2D body of the right type, and add fixtures to it. All current entities are defined to have just a single box-shaped fixture, which should be sufficient for now. The following snippet shows the K14Reactor initializer, which isn’t called directly, but from the K14Planet.createEntity method.

-(id) initWithPlanet: (K14Planet *) planet id: (int) id className: (NSString *) className
{
  // Create Box2D body
  b2BodyDef bd;
  bd.type = b2_staticBody;
  
  b2Body *body = planet.world->CreateBody(&bd);
  
  // Initialize superclass using Box2D body
  self = [super initWithPlanet:planet id:id className:className body:body];
  
  if (self)
  {
    // Add the single fixture for the reactor entity
    [self createFixtureWithName:@"reactor" shape:[[K14BoxShape alloc] initWithSize:CGSizeMake(1.5, 1.5)] density:1.0];
  }
  
  return self;
}

Obviously additional entity setup will have to be added in the future, for example implementing aspects of the visual representation of entities. Right now, entities are completely static, as there are no game logic hooks yet to implement entity behavior. After creating and positioning an entity you can get and set things like its absolute position in world coordinates, its angle, velocity or angular velocity, all of which are implemented as dynamic properties that wrap to calls on the underlying Box2D body, and that’s about it.

Next steps

The next step will be to add (how appropriate ;-) step methods for physics and game logic to K14Planet and K14Entity, and implement a first attempt at a game loop that calls them. This will likely include a controller class to drive the game loop, with an associated view that gets updated periodically.

I’ve already been thinking about how the game loop will likely be implemented eventually, and it will probably turn out a little more complex than the typical single-threaded update-render-swap framebuffer loop. Preferably, I’d like to run the physics simulation on a background thread, at a lower framerate than the screen refresh. I’ll leave the details for a later post.

Development scoreboard

I estimate the raw coding time to implement the planet and entity setup at about 2 hours. Combined with the time spent setting up the XCode project and creating the skeleton classes, that makes a total of about 6 hours up until now. From the next post onward, I will also include SLOC (source lines of code, excluding comments and whitespace) counts in the development scoreboard section.