March 10, 2014

Systems and Update Order

When creating a game loop, the order of operations is important. In many engines, it is easy to specify this order because the engine knows about all of its systems. In this engine, however, the engine core knows nothing of its systems. This keeps the engine extremely flexible and modular, but introduces difficulty in determining the order in which systems receive their turn in the update cycle.

Consider a scene with four systems: player input, physics, collisions, and rendering. The ideal order is as listed; the engine receives player input and updates player velocity, the physics system moves everything, the collision system resolves collisions, then everything is sent to the renderer. If the order is changed, the game will behave differently. If the renderer goes first, for example, then the player will see the previous frame and input will appear to him to be a frame behind. If collisions are resolved before physics updates positions, then the player will see objects in invalid positions before the next frame corrects their locations.

Resolving this problem is simple. Each system is given a new property: updateOrder. When a scene registers a new system, it is inserted into an array of systems and placed in the appropriate location based on its update order. During the game loop, this array is iterated through in order. If two systems have identical update orders, then they are updated in the order in which they were registered with the scene.

Here’s how I implemented update order. First, I added the updateOrder property to the system class and created an enum for update orders. Using an enum will make it easier later if it is determined that additional order is required.

// LGSystem.h
enum
{
	LGUpdateOrderFirst,
	LGUpdateOrderSecond,
	LGUpdateOrderThird,
	LGUpdateOrderLast
};

@interface LGSystem : NSObject
@property (nonatomic, assign) int updateOrder;
@end

When the engine is complete, the enum can be refactored so that it is clear, for example, that LGUpdateOrderLast is where rendering occurs. The refactoring might look like this:

enum
{
	LGUpdateOrderBeforeMovement,
	LGUpdateOrderMovement,
	LGUpdateOrderBeforeRender,
	LGUpdateOrderRender
};

Next, in the initialize method of each system I added a line to set its update order. If omitted, the value defaults to LGUpdateOrderMiddle. Finally, I modified the addSystem method of the scene class to insert new systems in the appropriate location in the systems array. It is worth noting that this solution requires that the updateOrder is set prior to adding it to the scene, which is why the initialize method is an ideal place. Because systems cannot be added to the scene after the first entity is added, this constraint does not appear to present any immediate concerns.

// LGScene.m
- (void)addSystem:(LGSystem *)system
{
	for(int i = 0; i < [systems count]; i++)
	{
		if([[systems objectAtIndex:i] updateOrder] > [system updateOrder])
		{
			[systems insertObject:system atIndex:i];
			return;
		}
	}
	
	// If we reach this, then it hasn't yet been inserted; add it to the end
	[systems addObject:system];
}

Posted by Luke Godfrey

Luke Godfrey is a graduate student at the University of Arkansas, pursuing a PhD in Computer Science. He has created applications on a number of platforms, and is especially interested in mobile and web development.

View more posts from this author

Leave a Reply

Your email address will not be published. Required fields are marked *