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
K14Inputs
class and can be peeked by the game logic update methods. At the end of each game update, the command input queue is emptied. TheK14Ship
entity 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 step
method to create a joint between the ship and the orb when it is lifted, there is now a class namedK14Joint
with a specialized subclassK14DistanceJoint
, that represent a joint between 2 entities. The only joint type currently supported is a distance joint, which is created using a methodK14Entity joinToEntityUsingDistanceJoint
, which takes the entity to join, and the maximum joint length. Whenever theK14Joint
instance 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 propertyjoinedEntity
), 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
The
K14Reactor
entity 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. -
Refactor
K14Ship
andK14Orb
creationPreviously, 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
K14Ship
and oneK14Orb
entity were present. Having to create the ship and orb throughK14Planet createEntity
also 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 theK14Orb
instance, position it at its respawn location, get the resulting coordinates, and store them with the parentK14Game
responsible for respawning the player and orb :-/. The solution I chose is to implicitly create theK14Ship
andK14Orb
entities on planet construction, and capture their location when starting the game (see below). TheK14Planet
class 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 astep
method, 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 addpause
,resume
andfinish
selectors and corresponding state enumeration values toK14Game
. -
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
from K14Planet
to K14Entity
and back for example, or from K14Entity
to
K14Joint
, and from there back to K14Entity
. These cycles prevented dead
K14Entity
instances from being deallocated, and with the Box2D body cleanup
in the 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
with 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 K14Actor
and K14StaticEntity
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 K14Entity
properties that needed to be modified by K14Actor
for example, or downcasting
from K14Entity
to K14Actor
. I consider these kinds of things ‘code smells’,
indicating an incorrect level of abstraction, so I removed them. Instead, K14Entity
now has a simple enumeration type K14EntityType
to indicate the kind of entity.
This enumeration has values such as K14EntityTypePlayer
, K14EntityTypeEnemy
,
K14EntityTypeStatic
, 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 ;-)
Video
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:
Next steps
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.
Development scoreboard
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.