Particle systems

16 Apr 2016

Since the last post I’ve been working in short bursts on implementing some simple particle system effects, which are now starting to take shape. That means time for a quick update.

Design decisions

The main purpose for adding particle effects was to add some simple visual feedback when the player or some other entity was hit. In the original game, a very simple explosion effect was used when the player or some enemy entity was shot, which would send a few single-pixel (remember, pixels on the Commodore 64 are pretty fat ;-) particles flying away from where the player or enemy used to be. A similar effect was used when the reactor was hit: the reactor itself would not explode but some particles would be emitted from the point of impact. At the very least, I wanted to be able to replicate these effects for 2k14: The Game. But I also set some additional requirements for myself:

  • Flexibility

    Instead of limiting the particle effects to a few hard-coded configurations, I want to be able to compose particle effects by parameterizing particle effects in a way that would allow combining different parameterizations to get new effects. For example, the initial position of particles and the rate at which they are emitted, should be independent of their movement, rendering, physics properties, etc. A hypothetical ‘snow flake particle’ could just as well be emitted from a ‘cloud emitter’, as from an ‘explosion emitter’. An ‘explosion emitter’ could just as well emit textured particles that don’t collide with anything and simply fade away, as particles with full Box2D physics, that bounce around the planet surface and kill any entities hit by them.

  • Scalability

    Even though we are at first only interested in very simple effects having less than a handful of particles, far less than 100, the implementation should leave room to scale the number of particles up dramatically. Let’s say up to 100K particles, to pick a nice round number. This would allow more advanced effects such as smoke, fire, fluid dynamics, etc. It’s highly doubtful 2k14: The Game will ever use these kinds of particle effects, the only reason I put it as a requirement is because it’s more fun to build particle systems that scale to these numbers ;-)

  • Optional physics integration

    To save myself some work initially, I’d like to be able to create particles with full Box2D physics, that are affected by gravity, collide with other entities and the planet surface, have mass, etc. Besides cheating my way out of implementing particle physics myself, this would also allow particle effects for gameplay purposes, e.g. non-lethal particles impacting the player ship.

    Box2D will most definitely not scale to 100K live particles on phone hardware, so Box2D physics should be an optional property of particles. If we ever wanted to add smoke or fire effects, or particles with very simple physics that don’t interact with other parts of the world, it should be possible to emit particles completely independent of the Box2D world.

  • Easy integration

    It should be possible to easily create particle effects from Lua scripts, in a fire-and-forget fashion, for example as a result of gameplay effects. The engine code should not encode any logic as to when or how a particle effect is created, that’s all up to the gameplay logic implemented in the Lua domain.

Implementation

The straightforward way to implement particle system support would be to create some classes directly mapping to the most obvious concepts involved in a particle system. We would have something like a particle class’ that would implement the minimum representation of an individual particle, and act as base class for specialized particles for various particle effects. The particle base class would have properties like particle position, velocity, etc. and abstract interfaces for assigning initial values to them, and to update the particle with a timestep. Particle subclasses would implement the abstract interface, and optionally add additional properties such as particle size, texture coordinates, etc. A simple particle system class would compose a large number of instances of the particle class, and offer methods to emit them in various ways, updating them, and managing particle lifetimes.

For our purpose, this approach would (at least initially) be a perfectly fine solution with no real drawbacks. For small numbers of particles the runtime overhead of dispatching multiple virtual function calls for each particle update negligible, and as long as the number of ways the particle system or the particles themselves can be parameterized (ie: if only a few ‘particle system configurations’ need to be supported), we can get by with a simple particle system class and two, three, maybe four derived particle classes. These two assumptions violate the ‘scalability’ and ‘flexibility’ constraints outlined above though: when updating 100K particles each frame, virtual function call overhead will start to matter, and scattering data for each individual particle all over heap-allocated object instances will absolutely kill CPU cache performance. In addition to that, using inheritance and specialization to represent different combinations of particle attributes and behaviors will quickly either lead to an unwieldy forest of subclasses, or to a smaller number of particle ‘god classes’ that can be initialized/parameterized in a million ways to implement every kind of particle effect we can come up with. All in all, a different solution is needed.

Particle system architecture

To satisfy all of my self-imposed design constraints, I’ve opted for a component-based design. I’ve written a little bit about component-entity systems in a previous post in the context of refactoring the way scripting capabilities were added to games, planets and entities, so for a little more background on what they are about you can read more about them there. Back when I wrote that post, I already decided to keep component-entity systems in mind as an architectural pattern for other parts of the code for which it could be a good fit. As it turns out, particle systems are a perfect example of a problem where a component-based design really is a much better choice than classic OOP inheritance/specialization.

In the following sections I will quickly go over the components involved in the particle system implementation I’ve integrated into 2k14: The Game, which will (hopefully) make clear why the component-based architecture makes a lot of sense for this problem, considering the requirements I’ve mentioned earlier.

I’ll be honest and state upfront that what I’ve implemented wasn’t actually all my own idea ;-). I found this excellent series of articles by Bartlomiej Filipek, who goes over all the moving parts involved in advanced and efficient particle systems, converging to a solution that’s very close to optimal for what I’ve had in mind for 2k14: The Game. Please read Bartlomiej’s series if you’re interested in particle systems, because he goes into a lot more detail than I will here!

Particle data

At the center of the particle system implementation sits the most obvious component: the data structure that holds particle data. Obvious in what it does (store particles), but not so obvious in how it does that.

With scalability to hundreds of thousands of live particles as a self-imposed requirement in mind, I went for a straightforward C-style structure-of-arrays (SoA). The idea is to make separate, flat arrays for every particle property, and group them into a structure. The counterpart to SoA would be an array-of-structures, which is basically exactly what the name suggests: the properties of a single particle are grouped into a structure, and the structure is repeated in an array for each particle. The SoA K14ParticleData structure looks like this:

typedef struct K14ParticleData
{
  unsigned int maxParticles;
  unsigned int currentParticles;
   
  GLKVector2 *positions;
  float *angles;
  GLKVector2 *velocities;
  float *angularVelocities;
  float *lifetimes;
  float *decayRates;
  float *sizes;
  GLKVector4 *colors;
  void **box2DBodies;
   
} K14ParticleData;

The main reason for choosing the SoA layout is to improve locality of reference. When updating particles, we typically want to update the same attribute for all particles from a tight loop, then move on to the next attribute. For example, first update all particle positions, then update all particle lifetimes, etc. Grouping particle attributes in a flat array by their types means that they are sequential in memory, and updating them one-by-one will maximize cache effectiveness: in an AoS layout the position of particle 1 would not be next to the position of particle 2, but N bytes separated from it, where N is the size of each particle structure. With enough particle attributes jumping through memory like this will greatly increase the chance of cache misses, which can kill performance.

Another advantage of the SoA layout is that in the future, we could make some particle attributes optional, or add new particle attributes without having to change the particle data structure. We could allocate a big chunk of memory, then setup pointers into it for each particle attribute used. Right now, the set of particle attributes is static, and determined by the layout of the K14ParticleData structure, Memory for each particle attribute array is separately malloc-ed and free-ed on construction/destruction, which by itself is not terribly bad, but means the arrays may not be consecutive in memory, and need to be memcpy-ed one-by-one when taking a snapshot of the particle system for rendering.

Particle generators

The second component of the particle system implementation are the particle generators. The name ‘particle generator’ may be a bit misleading, because a particle generator does not actually generate any particles, instead it initializes (a subset of) the initial attribute values for newly allocated particles. Examples of possible particle generators are a ‘circular area position generator’ (to assign positions to particles within a radius of some point) a ‘random color generator’ (to assign random colors), or a ‘Box2D generator’ (to create a Box2D body to each particle that can be used to add physics properties to it).

More complex generators are possible as well, which initialize multiple attributes at once. One example from 2k14: The Game is the ‘explosion generator’, which will assign particles linear and angular velocities that simulate an explosion parameters by a point (origin), a force value, and an initial velocity (for exploding moving objects).

All particle generators have to implement a single method defined by the K14ParticleGenerator protocol, which will be called when new particles have been emitted. For the K14ExplosionParticleGenerator, this function looks like this (note that this is a very simple proof-of-concept generator, the velocities that are calculated and assigned are not very realistic):

/**
 Generate particles.
 
 @param count number of particles to generate
 @param sequenceId sequence number of first particle to create (unused by this generator)
 @param particleData particle data
*/
-(void) generateParticles: (unsigned int) count
               sequenceId: (unsigned int) sequenceId
             particleData: (struct K14ParticleData *) particleData
{
  for (unsigned int i = particleData->currentParticles; i < (particleData->currentParticles + count); i++)
  {
    GLKVector2 dv = GLKVector2Subtract(particleData->positions[i], GLKVector2Make(_origin.x, _origin.y));
    GLKVector2 dv = GLKVector2Normalize(dv);

    GLKVector2 particle_force = GLKVector2MultiplyScalar(dv, randBetween(0.5f*_force, _force));
    GLKVector2 particle_velocity = { _initialVelocity.dx + particle_force.x, _initialVelocity.dy + particle_force.y };
    
    particleData->velocities[i] = particle_velocity;
    particleData->angularVelocities[i] = randBetween(-5.0f, 5.0f);
  }
}

The idea of having a very simple protocol that classes can implement to perform just the bare minimum, re-usable bit of logic is a good example of the single-responsibility principle, and allows mixing and matching various particle generators to create more complex particle effects.

Particle emitters

The particle emitters are probably the least exciting part of the particle system design, so I will quickly skip over it. The main task of a particle emitter is, unsurprisingly, to emit particles. Besides emitting particles, the emitter also acts as glue between the particle system, and the particle generators. The way this works is as follows: when creating a particle system, one or more emitters need to be passed. The emitters are created using a list of particle generators, an optional maximum number of particles to emit, and the rate at which particles are emitted (in particles per second). Whenever the particle system is stepped/updated, it calls an interface function (emit:dt:particleData) on all its emitters, each of which will allocate a number of particles and call the generateParticles method of its particle generators to initialize them. The number of particles to allocate is determined by the emission rate and the number of free slots in the K14ParticleData structure.

Right now, only a single particle emitter can be used for a particle system, but this is just an artificial limitation because I don’t have a use case for having multiple emitters yet. It’s not hard to come up with one though: for example a fireworks effect could have multiple emitters with different configurations of particle generators to make more interesting effects that activate at the same time. The K14ParticleEmitter class implements a simple particle emitter, defined by its emit method. The code for this method looks like this:

/**
 Emit particles.
 
 @param dt time since last particle was emitted
 @param particleData particle data
 @return number of particles emitted
*/
-(unsigned int) emit: (NSTimeInterval) dt particleData: (K14ParticleData *) particleData
{
  unsigned int count = 0;
  
  self.updateTime += dt;

  if (self.emitted < self.maxParticles)
  {
    if (self.updateTime > self.rate)
    {
      unsigned int free = (particleData->maxParticles - particleData->currentParticles);
      
      count = MIN(free, (self.rate > 0 ? (unsigned int) self.updateTime / self.rate : self.maxParticles));
      
      for (id<K14ParticleGenerator> generator in self.generators)
      {
        [generator generateParticles:count sequenceId:self.emitted particleData:particleData];
      }
      
      self.emitted += count;
      self.updateTime -= (count * self.rate);
    }
  }
  
  return count;
}

Particle updaters

Updating particles is implemented by particle updaters, which are very much like particle generators. Just like K14ParticleGenerator, K14ParticleUpdater is a protocol with a single method. For particle updaters the single protocol method to implement is updateParticles:dt:particleData.

Particle updaters can be combined in different ways to create different particle effects, similarly to the particle generators. Because I kind of cheaped out for the first iteration of my particle system by letting Box2D handle particle physics, currently only a single particle updater is implemented: the K14Box2DParticleUpdater. This updater simply copies the position, angle and velocities of the Box2D body backing each particle to the particle data structure, which means the particles will have full physics behavior including collisions, bouncing, etc. The update method for the K14Box2DParticleUpdater looks as follows:

/**
 Update particles. 
 
 This will copy the position, angle and velocities (linear, angular) of the backing
 Box2D body for each particle.

 @param dt update timestep
 @param particleData particle data
*/
-(void) updateParticles: (NSTimeInterval) dt particleData: (struct K14ParticleData *) particleData
{
  for (unsigned int i = 0; i < particleData->currentParticles; i++)
  {
    b2Body *body = (b2Body *) particleData->box2DBodies[i];
    
    b2Vec2 position = body->GetPosition();
    b2Vec2 velocity = body->GetLinearVelocity();
    
    particleData->positions[i] = GLKVector2Make(position.x, position.y);
    particleData->angles[i] = body->GetAngle();
    particleData->velocities[i] = GLKVector2Make(velocity.x, velocity.y);
    particleData->angularVelocities[i] = body->GetAngularVelocity();
  }
}

Simple enough, right? I do consider this cheating though, because having a heavy-weight object like a Box2D body backing each particle completely defeats the purpose of a particle system designed to handle hundreds of thousands of particles. This is just a temporary solution to get something to the screen though, it’s just a matter of adding particle generators and updaters that don’t involve Box2D to create a particle effect with truly light-weight particles.

Putting it all together

With the particle data, generators, emitters and updaters in place, there’s not much left for the main particle system class itself, K14ParticleSystem. Its main purposes are to bind together the particle data, emitter and updater, and to expose an API to update the particle system. The single method implemented by K14ParticleSystem is update:dt, which looks like this:

/**
 Update particle system.
 
 @param dt update interval in seconds
*/
-(void) step: (NSTimeInterval) dt
{
  // Update particle lifetimes,
  for (unsigned int i = 0; i < _particles->currentParticles; i++)
  {
    _particles->lifetimes[i] = MAX(0.0, _particles->lifetimes[i] - _particles->decayRates[i]*dt);
  }
  
  // Kill dead particles
  kill_particle(_particles);
  
  // Emit new particles
  if (_particles->currentParticles < _particles->maxParticles)
  {
    _particles->currentParticles += [self.emitter emit:dt particleData:_particles];
  }
  
  // Update particles
  for (id<K14ParticleUpdater> updater in _updaters)
  {
    [updater updateParticles:dt particleData:_particles];
  }
  
  // Flag particle system as 'done' if there are no live particles left, and no new particles can be emitted
  _done = ((_particles->currentParticles == 0) && (_emitter.emitted == _emitter.maxParticles));
}

The code above should be self-explanatory. First the lifetimes of all live particles are updated, and those that drop below zero are killed. Then, new particles are emitted, and finally all live particles are updated. If after this process no live particles are left and the emitter is exhausted, the particle system is ‘done’.

Killing particles is implemented by a C-style function kill_particles, which for each dead particle will simply copy the attributes of the last live particle in the particle data structure over the dead particle, and decrease the the number live particles. This ensures that all live particles are always consecutive (no gaps) in the first slots of the K14ParticleData structure, which makes allocating new particles as simple as bumping the live particle count. It also allows the particle generators and updaters to always operate on consecutive arrays without having to check for gaps/dead particles, which means we can write tighter particle update loops.

I implemented kill_particles as a plain C-style function for the simple reason that I wanted to keep K14ParticleData a C-structure (as opposed to an NSObject or a C++ class), but I also didn’t want to separate the ‘particle killing logic’ from the data structure as it depends on the layout and fields of the particle data structure. Now that I’m typing this it seems a little arbitrary, as it should be trivial to wrap the C-style particle data structure with an Objective-C class to allow proper initializers, destructors, copy semantics and possibly serialization. I may change this in the future.

Particle rendering

I’m going to leave a more in-depth treatment of the particle rendering for a later post, for two reasons: first of all because the way the particle rendering is currently implemented is quite inefficient, and I already know I want to change it. Second, because this post is already getting a little to long to my taste, and I’d really like to move on with the code ;-)

I’ll quickly summarize the particle renderer in it’s current state. On every step of the game loop, render commands are created for all active particle systems, which are initialized with (a copy of) the particle data for each system. When the renderer first encounters a render command for a new particle system (as determined by the render command id, which is unique for each particle system), it will setup a K14ParticleRenderState instance. The particle render state consists of 2 vertex buffer objects (VBO’s): one static (cannot be modified), one dynamic (updated every frame as long as the particle effect is active). The static buffer is initialized with vertices defining a unit square, 4 vertices for every particle, up to the maximum number of particles for the particle effect. The dynamic buffer is initialized with vertex attributes derived from the particles: position, angle, size, color, lifetime. Every attribute is repeated 4 times, once for every vertex of the unit triangle corresponding with the triangle. When it comes to rendering, only as many elements are used from these buffer as there are live particles in the particle system.

Rendering the VBO is done using a vertex shader that will read the particle position and angle from the vertex attribute array, setup a 2D rotate-translate matrix from it, and transform the corresponding unit square vertex by it. The remaining particle attributes are passed to the fragment shader as varying values. The fragment shader currently simply reads the color value and multiplies it by the lifetime value.

On every frame, the particle positions, angles, colors and lifetimes in the dynamic VBO are updated, but the unit triangle is unmodified. This way of rendering could be described as “poor man’s geometry instancing”. We basically want to push 2 triangles (1 rectangle) and a set of scalar attributes per particle, but because OpenGL ES 2 does not have built-in default support for instancing, we emulate it by duplicating the unit triangle for each particle, and every particle attribute 4 times (once for each vertex of the unit square).

Suffice to say I don’t like this because even though it’s more than fast enough for the simple effects I’m rendering now, it just feels gross to unnecessarily copy and upload the same information 4 times for each particle. Fortunately, there’s a better way, which I will try to implement and document in the near future.

Lua integration

By design, the particle system design allows a practically infinite variety of particle effects, by adding additional particle generators and updaters and combining them the possibilities are limitless. Exposing all this flexibility to the scripting layer is difficult though: we don’t have automatic binding, so Lua bindings for every K14ParticleGenerator and K14ParticleUpdater would have to be added manually if we wanted to create a one-to-one mapping of the particle system API to Lua. Some particle generators can be parameterized in many ways, requiring many different constructors, construction parameters, default values, etc, which means manual binding could quickly grow unwieldy.

Instead, I’ve chosen to funnel interesting particle effects through a much smaller Lua interface, which is defined by some preset particle system configurations. Instead of directly creating emitters, generators and updaters, Lua scripts pass in a ‘particle effect definition dictionary’ to a wrapped native function particleEffect, which will interpret the definition and perform the required setup work.

Currently, only a single Lua particle effect has been implemented, which I’ve called the ‘rectangle explosion’. This effect is parameterized using an explosion origin, force, impact vector, an angular spread, and a number of per-particle attributes such as min/max size, allowed colors, etc. The following code snippet illustrates how Lua scripts create particle effects, in this case an explosion effect that is triggered when the player is hit:

if event.type == Game.Event["PLAYER_HIT"] then

    -- Player hit. Create explosion effect, and post a delayed 'player killed' event

    -- ...

    local particle_system_parameters = {
      type=Game.ParticleSystemType["RECTANGLE_EXPLOSION"],
      duration=1.5,
      numParticles=25,
      origin=impact_point,
      radius=1.0,
      minSize=0.1,
      maxSize=0.2,
      force=5,
      angle=math.atan(impact_vector.y, impact_vector.x),
      spread=math.pi,
      initialVelocity={ x=0, y=0 },
    }

    planet:particleEffect("explode_player", particle_system_parameters)

    -- ...

The native wrapper function that implements the particleEffect function is relatively straightforward. I’ll post a small snippet from it that implements the actual K14ParticleSystem initialization here, just to show an example of how the native API for particle systems is used:

// Parse particle system table and crate particle system
K14ParticleSystem *particle_system = nil;

lua_getfield(L, -1, "type");
K14ParticleSystemType particle_system_type = (K14ParticleSystemType) lua_tointeger(L, -1);
lua_pop(L, 1);

//...

switch (particle_system_type)
{
  // ...
    
  case K14ParticleSystemTypeRectangleExplosion:
  {
    NSDictionary *explosion_fields =
    @{
      @"duration" : @(K14LuaPropertyTypeReal),
      @"numParticles" : @(K14LuaPropertyTypeInt),
      @"origin" : @(K14LuaPropertyTypeVec2),
      @"radius" : @(K14LuaPropertyTypeReal),
      @"minSize" : @(K14LuaPropertyTypeReal),
      @"maxSize" : @(K14LuaPropertyTypeReal),
      @"force" : @(K14LuaPropertyTypeReal),
      @"angle" : @(K14LuaPropertyTypeReal),
      @"spread" : @(K14LuaPropertyTypeReal),
      @"initialVelocity" : @(K14LuaPropertyTypeVec2),
    };
    
    NSDictionary *explosion_parameters = [scripting_component.vm stackDictionaryValueWithFields:explosion_fields];
    
    /// \todo use a hard-coded set of particle colors for now
    NSArray *explosion_colors =
    @[
      [K14Color colorWithUIColor:[UIColor redColor]],
      [K14Color colorWithUIColor:[UIColor yellowColor]],
      [K14Color colorWithUIColor:[UIColor orangeColor]],
      [K14Color colorWithUIColor:[UIColor darkGrayColor]],
      [K14Color colorWithUIColor:[UIColor lightGrayColor]],
      [K14Color colorWithUIColor:[UIColor whiteColor]],
    ];
    
    unsigned int num_particles = [explosion_parameters[@"numParticles"] intValue];
    CGPoint initial_velocity = [explosion_parameters[@"initialVelocity"] CGPointValue];
    
    NSArray *generators =
    @[
      [K14CircularAreaParticleGenerator generatorWithOrigin:[explosion_parameters[@"origin"] CGPointValue]
                                                     radius:[explosion_parameters[@"radius"] floatValue]
                                                      angle:[explosion_parameters[@"angle"] floatValue]
                                                     spread:[explosion_parameters[@"spread"] floatValue]],
     
      [K14SizeParticleGenerator generatorWithMinSize:[explosion_parameters[@"minSize"] floatValue]
                                             maxSize:[explosion_parameters[@"maxSize"] floatValue]],
     
      [K14ColorParticleGenerator generatorWithColors:explosion_colors],
     
      [K14ExplosionParticleGenerator generatorWithOrigin:[explosion_parameters[@"origin"] CGPointValue]
                                                   force:[explosion_parameters[@"force"] floatValue]
                                         initialVelocity:CGVectorMake(initial_velocity.x, initial_velocity.y)],
     
      [K14DecayRateParticleGenerator generatorWithMinLifetime:0.5f maxLifetime:[explosion_parameters[@"duration"] floatValue]],
      
      [K14Box2DParticleGenerator generatorWithWorld:scripting_component.planet.world density:1.0],
    ];
     

    K14ParticleEmitter *explosion_emitter = [[K14ParticleEmitter alloc] initWithGenerators:generators maxParticles:num_particles rate:0.0f];
    
    NSArray *updaters = @[ [K14Box2DParticleUpdater new] ];
    
    particle_system = [[K14ParticleSystem alloc] initWithEmitter:explosion_emitter updaters:updaters maxParticles:num_particles];
    
    break;
  }
  
  // ...
}

Video

Obviously, I also made a video of some particle effects I quickly hacked into the game when entities or the player are hit. These are purely for illustration purposes, some finetuning is needed to improve the appearance and behavior of the effect.

Some unrelated tidbits

In parallel with the particle system work, I did some ‘collateral cleanup’ that was either just bothering me, or was broken by stuff added to K14Game and K14Planet classes to support particle system effects. Most of it related to freezing/unfreezing Lua game state, and one-off/post-create initialization of Lua wrapper classes. Nothing terribly exciting so I’ll just summarize:

  • Delayed events

    To allow effects such as exploding entities when they are hit, a mechanism was needed to introduce some time between the hit, and the ultimate removal of the entity from the game state and any possible further events it may trigger (respawning, for instance). The concept of ‘delayed gameplay events’ was added to the event handling logic in the Lua Game class. Instead of always processing gameplay events directly when they are generated, they are now queued up with a timestamp, and handled when the elapsed game time reached this timestamp. This means the event queue can persist over multiple ‘ticks’ of the gameplay update loop, which introduces some new problems serializing Lua state, as explained in the next list item.

  • Lua wrapper class serialization

    When freezing/unfreezing a game, properties of Lua wrapper classes for the game, planet and entities are now serialized completely from Lua, using the Lua marshal module. Previously, every Lua wrapper class had to advertise the names and data types of all instance properties to include when serializing, using a ‘property table’ class variable. The native scripting interface classes code would use the property table to fetch the property values and convert them to native types (NSValue, NSString) that were included in the NSArchive as an NSDictionary. This was unnecessarily complex, and when I realized more complex/structured data types besides scalars and strings needed to be serialized (delayed gameplay events, for example) I decided to change it to something much simpler.

    Every Lua wrapper class can now implement optional freeze/unfreeze methods, the former of which should produce a string representing serialized instance data for the class, the latter of which should take a string, parse it, and assign instance variables from the deserialized data. The scripting interface class will call these methods and simply store/retrieve the serialized string. Since we’re only serializing POD types (numbers, strings) and tables, no object references or Lua userdata, serialization is trivial: just create a table of name-property pairs and serialize/deserialize using Lua marshal.

  • Wrapper class initialization

    One-off/post-create initialization of Lua wrapper classes used to be handled by the native scripting interface classes, by means of explicit calls to the init method of each wrapped instance. This had been bugging me for a long time already, because initialization order dependencies prevented doing these init calls automatically on construction of the K14LuaComponent scripting interface base class. Instead, post-create initialization calls were hard-coded at various ‘strategic’ (read: more or less arbitrary) points.

    To make a long story somewhat shorter, all one-off/post-create Lua wrapper class initialization is now purely done from Lua, no native code is involved. The trigger is the Game:started() callback which is always called exactly once for a game (restoring a game from a snapshot will not trigger this callback). This removes the need for explicit initialization calls from places that should not have to know about the scripting layer at all, such as the view controller that used to kick off the initialization process from the viewDidAppear callback.

Next steps

At the top of my todo list is improving the particle system rendering, to eliminate the wasteful copying/duplication of particle coordinates and attributes. After that, I’d like to use the particle system API to create some more visual effects, to stress the API and to liven up the game a little. I may also experiment with particle systems that are not backed by Box2D, with significantly more particles, and more interesting rendering, for example smoke, fire, textures, etc.

Development scoreboard

Total development time since the last update increased by about 25 hours, a not-insignificant part of which was spent on refactoring the wrapper class initialization and serialization code. I wasted quite a bit of time using a different Lua serialization module which seemingly randomly corrupted serialized data, for example. Total development is now about 309 hours. SLOC count is at 8964, an increase of 1279. Lua SLOC count increased by 116 to a total of 889.