Over the last 2 weeks I logged quite a bit of development time, about 12 hours in total, which went into many small features and behind-the-scenes improvements related to the core functionality of the game engine. I really want to work up to the point where all core gameplay features are either implemented or have all their supporting infrastructure in place, so I can move on to one of the things I enjoy most about game programming: the graphics ;-)
The list of minor features and improvements is pretty long, so I won’t bore potential readers (god knows if there are any ;-) with all the details. Instead, I’ll summarize my checkins over the last few weeks along with some short commentary, in the order I implemented them:
Implement ‘command inputs’ and use them for shooting
Previously, the input handler class only supported on/off toggle inputs, for thrust and to enable the tractor beam. To support event-based inputs such as shooting, I added a ‘command input’ class, which represents a discrete event triggered by an input action. Command inputs are queued up in the
K14Inputsclass and can be peeked by the game logic update methods. At the end of each game update, the command input queue is emptied. The
K14Shipentity now uses this to launch a projectile for every command input labeled as a ‘shoot’ action.
Fix projectile self-collision using a Box2D contact filter
When an entity launched a projectile, the projectile sometimes spawned inside the bounding rectangle of the entity, causing a self-collision. Especially when the shooting entity was moving (like the player ship), this resulted in projectiles killing the originating entity. The solution was to use a Box2D collision filter class, which gets called before collisions are propagated, and can reject them based on some condition. In this case, the condition was to filter out all collisions between a projectile and its originating entity.
Fix projectile physics when firing from a moving entity
When shooting while flying fast, the player ship would ‘overtake’ the projectile or otherwise launch it in a direction completely different from where the ship was facing. The fix was to add the ship velocity to the initial projectile velocity after launching it
Add classes and API support to represent, create and break joints
Instead of direct Box2D calls from the
K14Ship stepmethod to create a joint between the ship and the orb when it is lifted, there is now a class named
K14Jointwith a specialized subclass
K14DistanceJoint, that represent a joint between 2 entities. The only joint type currently supported is a distance joint, which is created using a method
K14Entity joinToEntityUsingDistanceJoint, which takes the entity to join, and the maximum joint length. Whenever the
K14Jointinstance is destroyed (e.g. when the player respawns), the backing Box2D joint is automatically removed from the physics simulation. Additionally, if an entity has a joined entity (accessible through a new property
joinedEntity), both entities will be automatically wrapped around at the same time when either one crosses the wraparound point.
Implement ‘dead entities’
When an entity collides with a projectile, or when the player ship hits another entity or the planet surface, it is now marked as ‘dead’, and will be removed from the game world.
Reactor game logic
K14Reactorentity now has a hit count, and if it exceeds a preset maximum, it will start a countdown timer. Eventually, this timer should trigger a planet meltdown that kills the player, but I didn’t implement this behavior yet.
Previously, the planet setup code had to explicitly create the entities representing the player ship and the orb, and position them somewhere on the planet. I didn’t like how this allowed creating an incomplete planet, and how much of the game logic only worked under the implicit assumption that exactly one
K14Orbentity were present. Having to create the ship and orb through
K14Planet createEntityalso resulted in a chicken-and-egg problem when I tried to add a respawn location property for the orb: it wasn’t possible to know beforehand the exact location and angle of the orb entity because it is usually placed at a calculated location along a surface edge, which meant I had to create the
K14Orbinstance, position it at its respawn location, get the resulting coordinates, and store them with the parent
K14Gameresponsible for respawning the player and orb :-/. The solution I chose is to implicitly create the
K14Orbentities on planet construction, and capture their location when starting the game (see below). The
K14Planetclass now has direct references to the ship and the orb, which means it is not necessary to look them up by iterating the list of planet entities anymore when they are required inside a
stepmethod, for instance.
Game loop control
After creating a new game, the game loop now has to be started explicitly using a new selector
K14Game start. Only after the game is started, view controller callbacks result in game updates. In the future, I will also add
finishselectors and corresponding state enumeration values to
Respawn the player and the orb when the player ship crashed or was hit
Adding up all of the above, implementing player and orb respawning was pretty straightforward. Whenever the player is hit or crashes, or when the orb collides with something while it is being lifted, both entities are reset to their respawn locations, as captured when the game was started. Additionally their velocities (linear and angular) are set to zero and the joint between the ship and the orb is destroyed. There are no win/lose conditions yet, and the player life count is not used yet, but that’s mainly because it’s not that interesting to implement yet.
In addition to the things on the list above, I did some collateral cleanup and refactoring. Most of it barely warrants mention, such as Objective-C style fixes to satisfy my OCD. There’s a few things that deserve a few words though.
For instance, one thing I ran into after I implemented dead entities, was that
after removing them from the game world, their K14Entity instance wasn’t
properly deallocated, which in turn resulted in their backing Box2D body
sticking around to haunt the planet. These entities were not rendered and did
not receive any game logic updates anymore, because they were not officially
part of the
K14Planet set of ‘live entities’. Their Box2D body was still
alive and kicking though, and being updated by the physics engine. The reason
for this was that the data model contained some ARC cycles, strong references
K14Entity and back for example, or from
K14Joint, and from there back to
K14Entity. These cycles prevented dead
K14Entity instances from being deallocated, and with the Box2D body cleanup
K14Entity dealloc selector, they never left the physics simulation.
The fix was to change all ‘back references’ in the data model to weak references
that don’t add to ARC reference counts. Visualizing the data model as a tree
K14Game at the root, only references that go ‘down’ in the data model are
now strong references. All references that go ‘up’ were changed to weak references.
Another refactoring step was to deprecate the
classes that sat between
K14Entity and concrete entity subclasses. This
intermediate level of abstraction did not have any significant benefits, and
only added unnecessary complexity, requiring protected scoping of
properties that needed to be modified by
K14Actor for example, or downcasting
K14Actor. I consider these kinds of things ‘code smells’,
indicating an incorrect level of abstraction, so I removed them. Instead,
now has a simple enumeration type
K14EntityType to indicate the kind of entity.
This enumeration has values such as
K14EntityTypeEnemyProjectile, etc, allowing much finer
grained entity type-specific checks and operations. Right now, these are only
used by the
K14Planet method to organize live entities in disjunct dictionaries,
but they will undoubtedly prove useful for other purposes later, for example to
implement certain entity game logic.
Updated data model
All the changes I talked about in the previous sections had their effect on the data model, so I thought this would be a good time to post an updated version. Interestingly, even though many small features were added, the data model itself got simpler, which I obviously don’t regret at all ;-)
To conclude this post, here’s the obligatory video showing off the new features added to the game engine. This video shows almost all features of the game engine: player ship controls, shooting, dead turrets, the reactor taking hits, fuel pickup, lifting the orb, and crashing and respawning:
With most of the core engine features in place and seemingly working well, I’m going to work on the game graphics a bit. I’ll start with planet rendering first, then rendering game entities using sprites.
Total development went up by ~12 hours, to a total of ~44 hours. SLOC count went up to 889. Probably half of the added lines of code were a direct result of the Objective-C style fixes I did, and the dynamic properties added to get access to Box2D state I hadn’t needed before.