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.