Sprites II

16 Mar 2015

A business trip followed by a short vacation means few updates since the last post. I did spend some time on the game before my trip and on the flight back home, and now that the jet-lag has worn off a little I’m trying to pick up development a little.

Entity sprites, revisited

The most interesting bits for this post are related to entity sprite rendering. The initial idea was to render each entity using a single rectangular sprite, where each entity class provided one or more sprite frame images to visualize its various states. This design constraint was not really the result of a deliberate thought process, I just built it this way because it most closely matched the fixed-size sprites the original Commodore 64 version was limited to. For the modernized iOS version it’s a little naive and unnecessarily restrictive, though.

The problem using single sprites with the same size, position and rotation as the entity itself, is that it’s not easily possible to use sprites larger than entity bounding box, and it is not possible to visualize entity state that is independent of the the entity position or rotation. Additionally, any sprite rendering effect I would like to implement in the future would be indiscriminately applied to every part of the single entity sprite. As an example, if I wanted to render a circular shield around the ship when the tractor beam is active, it would not be possible to draw the shield as a circle larger than the ship itself, and it would not be possible to dynamically change the color of the shield without changing the color of the ship. At least not without ugly hacks like encoding cues for the renderer inside the entity texture itself, or oversizing entity sprites and rendering them larger than the entity bounding box to have some extra space to play with.

To make entity sprite rendering more flexible, I changed the single string-type spriteFrame property of the K14Entity class and its derived classes, to a new property sprites, an array of instances of a new class K14Sprite. The K14Sprite class encodes a sprite frame name, and a position, size and rotation to allow drawing sprites independent of the entity bounding box. In addition to these properties, K14Sprite instances can encode rendering effects or cues. At this time, the only rendering effect I’ve added is a tint color, which is used by the entity fragment shader to change the color of the sprite by multiplying it with the (greyscale) sprite texels.

The K14Ship entity is currently the only entity that is using multiple sprites, depending on whether the tractor beam (which activates the shield when nothing to tractor is in range) is activated. The following code snippet shows the implementation of the K14Ship sprites property:

-(NSArray *) sprites
  K14Sprite *default_sprite = [[K14Sprite alloc] initWithSpriteFrame:K14_ENTITY_DEFAULT_SPRITE_FRAME
  NSMutableArray *sprites = [NSMutableArray arrayWithArray:@[ default_sprite ]];
  if (self.tractorBeamActive)
    if (!self.orbAttached && (self.tractoredEntity != nil))
      K14Sprite *tractor_sprite = [[K14Sprite alloc] initWithSpriteFrame:K14_SHIP_TRACTOR_SPRITE_FRAME
                                                                    tint:[K14Color colorWithRed:1.0f green:1.0f blue:0.0f alpha:1.0f]
                                                                position:CGPointMake(self.position.x, self.position.y + self.size.height)
      [sprites addObject:tractor_sprite];
      K14Sprite *shield_sprite = [[K14Sprite alloc] initWithSpriteFrame:K14_SHIP_SHIELD_SPRITE_FRAME
                                                                   tint:[K14Color colorWithRed:0.0f green:1.0f blue:0.0f alpha:1.0f]
      [sprites addObject:shield_sprite];

  return sprites;


One major gameplay feature that was still missing was shooting, so I added it on the flight back after my vacation. There really isn’t all that much interesting to say about it, since all the bits and pieces were already there, just not tied to any touch handling, and not integrated into the action replay system. Implementing the feature was just a matter of adding an additional touch area and triggering a ‘shoot’ command event when it is activated. This exposed a few bugs in the way the K14Inputs class handled queued command events, and in the action replay command event classes, which were all easily resolved.

Assorted fixes and improvements

Besides the new features I mentioned above, I also spent some time tying up some loose ends and implementing some behind-the-scenes fixes. Without going into too much detail, here’s a summary:

  • Fixed a bunch of memory leaks, reduced memory usage

    Using the XCode Instruments application I was able to hunt down a few memory leaks, mostly related to OpenGL state that was not properly released after closing the game screen. Things like the CGBitmapContext used to initialize the sprite atlas texture, and the OpenGL texture objects themselves. Additionally, I reduced total memory consumption by over a factor of 2, by releasing the sprite atlas texture image after uploading it to the GPU, and by allocating only the number of atlas sprite frames required for each entity, instead of always allocating a 4x4 atlas. The game still uses about 30 MB of RAM for a typical planet, which I find a little on the high side for what you get, especially considering the original Commodore 64 version had to get by with 64 kilobytes ;-). I’ll have to revisit the memory footprint later.

  • Fix planet teardown

    While trying to track down the memory leaks mentioned above, I ran into a problem with the way I implemented entity destruction. Whenever the reference count of a K14Entity dropped to zero, its backing Box2D body (and associated fixtures, joints, etc) was automatically deleted by the K14Entity destructor This lead to race conditions when deallocating a K14Planet instance, when Box2D state was deallocated before K14Entity state that referred to it (either directly as a backing Box2D body, or indirectly as e.g. a tractored entity). Strangely this race condition did not result in crashes before, probably shuffling around something in some dealloc method changed the destruction order and triggered the problem.

    The solution is straightforward and can act as a template every time you have a data structure of interconnected objects that may have mutual references to each other or to backing state, some part of which has to be deleted as a result of an update operation. Instead of immediately dereferencing objects whenever they are considered ‘dead’ and tearing down backing state from their destructors, mark the objects as ‘stale’, dereference them atomically by sweeping the ‘stale set’ after the update operation, and deallocate backing state explicitly afterwards. For the case mentioned above this meant setting a ‘dead’ flag for entities flagged for removal and deactivating their Box2D body, then sweeping the entity set after each update operation, removing all references to dead entities, and destroying their backing Box2D body explicitly.

  • Change planet initialization sequence

    Instead of automatically creating a K14Ship and K14Orb entity from inside the K14Planet constructor, client code is now responsible to create these explicitly. I’m not even sure anymore what problem this solved, but at any rate it makes planet creation more predictable when the position of the ship and orb depends on some other aspect of the planet that may not be known from inside its constructor (e.g. if the orb needs to be positioned relative to some other entity such as its pedestal, more on that later ;-)

  • Split the orb into two entities

    Instead of representing the orb and pedestal as a single entity which was lifted in its entirety when tractoring it, there are now separate entities for the orb and the pedestal. When tractoring the orb, the pedestal is left behind on the planet surface like it should.

  • Fixed unit tests

    All unit tests using K14Planet or K14Entity were broken after earlier changes to the way planets and entities are created and initialized. Unit tests using a hand-crafted action replay to simulate and test gameplay situations were also broken as a result of changes to the action replay classes. These were all relatively easy to fix.

  • Minor gameplay tweaks

    Changed some gameplay constants related to ship handling, changed the size of some entities.


As usual, here’s a video showing some recorded gameplay to illustrate the visible changes I’ve talked about in the paragraphs above. As you can see, the game is slowly starting to become playable :-).

Fun fact: this replay was recorded on an iPhone 6, then replayed in the iOS simulator, set to simulate an iPhone 5. Even though the replay is compiled to x86 code running in a simulator, which is set to simulate a different piece of hardware, the replay is a perfect reproduction of the iPhone 6 gameplay. This illustrates two things, 1: the action replay functionality appears to be pretty solid, and 2: the iPhone simulator is pretty awesome ;-)

Next steps

One thing I still want to implement before moving ahead with new features is smarter sprite texture atlas packing. Right now, each entity has a sprite atlas created as a grid of equal-sized square sprite frames, which means the sprite images are scaled and stretched when creating the atlas to make them fit. When rendering an entity, the sprite is resized again, to match the aspect ratio and size of the entity. All this scaling and stretching results in image quality loss, so I’d like to avoid it by allowing non-rectangular, non-uniform sprite images in the same texture atlas, and directly storing the source images for each sprite frame in the atlas without scaling or stretching them.

Besides the texture atlas packing there’s a few minor things I’d like to fix, such as projectiles flying through the planet surface and such. I’ll start thinking about new features and improvements after these are fixed.

Development scoreboard

All of the above added about 7 hours of development time, which brings the total development time to ~147 hours. SLOC count increased by 127 to 1749.