Gamasutra is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.

Gamasutra: The Art & Business of Making Gamesspacer
Procedural Content Generation: Thinking With Modules
View All     RSS
September 19, 2019
arrowPress Releases
September 19, 2019
Games Press
View All     RSS

If you enjoy reading this site, you might also want to check out these UBM Tech sites:


Procedural Content Generation: Thinking With Modules

by  [Design, Indie]

July 18, 2012 Article Start Previous Page 4 of 5 Next

A Modular Approach to Level Structure

In the three years we've been working on Ugly Baby, we haven't solved all of these problems, but we've had some success when combining simple, modular concepts to produce complex results.

Early successes in programmatically creating level skeletons in Aaaaa! were a boon, so we went back to those roots. It's actually pretty easy to create visually interesting things by taking simple structures and iterating on them. Take, for example, a sphere made out of cubes:

This time, instead of adding complexity to that algorithm, we took the output and iteratively modified it:

  1. Generate the sphere of cubes.
  2. Apply color to the pieces (for example, by saturating them, and selecting a hue based on the object's position around a central axis).
  3. Change the cubes to crosses.
  4. Only instantiate the objects around a certain portion of the sphere.

The end result doesn't look like a sphere at all:

What's so interesting to us about this simple concept is threefold: first, we can feed slightly different parameters in and get significantly different geometry out; second, in Ugly Baby, we can tie those parameters to the audio stream, so that the music drives the level's appearance; and finally, the modification pass is independent of the base structure, so we could just as easily apply the above to a grid of cubes and get something completely novel and (possibly) useful.

This was promising, so we formalized this. An Ugly Baby level generator consists of three types of modules:

  • Sequencers are modules that create things -- a sphere, a column, a grid, a cylinder, and so forth.
  • Selectors return a set of all objects that satisfy a set of conditions, such as everything on one side of a plane, or all objects bigger than a breadbox.
  • Mutators apply changes to objects, typically based on its properties. Examples include changing colors based on position or scaling based on orientation.

Here's all that in action:

Step 1: The player flies along a linear path, so let's start by simply creating a simple column of blocks along the falling axis.


# Instantiate the column:

sequencer_column = sequencer.Column()

queue = sequencer_column.iterate()

Step 2: This level's like a rails shooter, so let's create something that looks more like a tunnel. Let's swap out the single column for six of them (essentially the edges of a cylinder). This takes some basic parameters, such as the vertical distance between blocks and the number of columns of blocks around the axis.


# Instantiate the cylinder:

sequencer_cylinder = sequencer.Cylinder(layer_delta=4, blocks=6)

queue = sequencer_cylinder.iterate()

Step 3: Apply a scale to every piece that the sequencer generates.


# Change every piece's scale:

mutator.scale(queue, [1, 4, 1])

Step 4: We wrote a mutator node that simply reorients a piece such that one side faces the Z- axis, and apply that to all pieces.


# Turn pieces to face the player's falling (z) axis:


Step 5: An "Every-N" selector node simply grabs one out of every N pieces fed into it. Here, we want to select every fourth piece and use a mutator to turn them red.


# Get a list of every 4th pieces that comes into the queue:

every_4th_piece = selector.every_n(queue, 4)

# Turn those pieces reddish:

mutator.set_color(every_4th_piece, [255, 32, 0])

Step 6: Finally, let's orient them sinusoidally over vertical distance.


# Pan from -45..45 depending on a piece's position along the player's falling axis:

mutator.cyclic_rotate(queue, freq=0.1, low=[-45, 0, 0], high=[45, 0, 0])

Article Start Previous Page 4 of 5 Next

Related Jobs

Cold Iron Studios
Cold Iron Studios — San Jose, California, United States

Senior Systems Designer
Wargaming Mobile
Wargaming Mobile — Berlin, Germany

Lead Producer
Deep Silver Volition
Deep Silver Volition — Champaign, Illinois, United States

Environment Artist
Hyperkinetic Studios
Hyperkinetic Studios — Los Angeles, California, United States

Level Designer - Los Angeles

Loading Comments

loader image