Mechanic #037 - PGC Templates|
An explicit way to define and control random decisions in relationship to a particular algorithm.
We return to the promising waters of procedurally generated content once more, and again, this is less a game mechanic so much as it is a design mechanic. Even then, it's not really a "new" idea.
One of the greatest initial problems of PGC is that it is not standardized. There are various algorithms out there, but there is no framework available for the big picture. You can randomly design objects or levels, but not entire games. To me, this represents a problem of scale, not complexity. So what needs to happen is that the randomization aspect needs to be patternized.
Example 1: Simple Level Algorithm
Output: GameObject Level
Tileset: [ list of tilesets ]
Number of rooms: [ 1 - infinite ]
Room width: [ 5 - 20 ]
Room height: [ 5 - 20 ]
At the most basic level, you've got the algorithm. It is a black box that takes certain inputs and produces something. For instance, this website will take a number of values and then generate a dungeon based on it. If we compartmentalize that, we end up with an Algorithm Object.
Simply put, it is an object that represents a single algorithm, of which the inputs are published along with acceptable values. The example above is a simple level design algorithm which places X room of size W x H using a specific tileset. Along with each of those inputs is a set of values which represent the upper and lower bounds. The tileset is a special case in that rather than using bounds, it is represented through a set of all available tilesets (it is assumed that a tileset subclasses a TileSet object so polymorphism does its magic).
The problem is, we dont want every single input to be equally viable. The way the algorithm is customized is through these inputs. It would produce unique levels every time if we simply selected valid input each time, but we wouldn't be able to control it. It would always be completely random. We solve this through the use of an Algorithm Template
Example 1: Swamp Temple Template
Algorithm: Simple Level Algorithm
Tileset: (static) swamp set
Number of rooms: (random)[ 8 - 12 ]
Room width: (bell curve)[ 5 - 15 ]
Room height: (static) 7
A template is basically a minor programming language for deciding inputs. For each input, we give it a special value selector. We can use this to set a static input (like room height always being 7) or give it a random number between X and Y (which are a subset of the algorithm's bounds). We can do other stuff like give it weighted randomness, like a bell curve peaking halfway between two points, or a set of values to choose from based on individual weights.
We can even assign simple conditionals. For example, the following template will produce a level made entirely of hallways:
Example 1: Skinny Rooms Template
Algorithm: Simple Level Algorithm
Number of rooms: -
Room width: ( rnd() > 50% ) ? (random)[ 1 - 10 ] : 1
Room height: ( Room width == 1 ) ? (random)[ 1 - 10 ] : 1
There could even be variables, initialization code, and so on. But the thing to remember is that a template is like a blueprint. It produces a set of inputs for a particular algorithm. That's all it does.
So why bother? Well, we have basically standardized the randomization process into a discreet, shared form. Remember, the algorithm only contains the possible values. If you pass the same values, it will produce the same output. The randomization - the decision making - is made at the template level, and through it, we can create customized templates to allow for more or even less control over the randomization process.
We can start referring to templates as input to other templates. For example:
Example 1: Castle Dungeon Template
Algorithm: Simple Dungeon Algorithm
Floors: (random)[ 2 - 4 ]
- Floor: Castle Ground Floor Level Template
- Floor[Floors - 1]: Throne Room Level Template
- Floor[other]: (random)[ Simple Castle Level Template, Barracks Template, Dungeon Template ]
This template creates a Castle Dungeon with between 2 and 4 floors. The first floor will be a ground floor-type level and the last floor will be a throne room, and any floor in between will be one of simple castle, barracks, or dungeon. All those are templates to an algorithm (or several) which will produce a Level GameObject (GameObjects are objects used by the game directly so as not to be confused with algorithms which produce other templates). Since the Castle Dungeon Template has control over which templates it will use, it can make sure that only the compatible algorithms will be enacted.
To add a small degree of additional customization, templates can have things called Template Mods placed on them. Simply put, they override some of the template values. For instance, a Small Level mod may overwrite the number of rooms input without touching anything else.
It should be possible to use global mods for any template using a specific class of algorithms (like "Small Level" could be applied to an Level Template) but I think a better approach would be to make the mods part of the templates themselves. That way, each template can decide what a "Small Level" means to it, or if it means anything at all. The advantage here is that we can the select levels based on mod availability. We might make a set of all the level templates that have a mod called "Small Level". This would add additional indirect control for smat(er) template selection.
In order for this to work, this language needs to be formalized. There needs to be a big document out there that says what all the possible random selection algorithms are, what values can do what, how templates can be related to each other, and so on. This is the start to a very big, and I dare say, mind numbing project. Still, I think at the end of it, the potential for a standardized language for procedural generation is a strong incentive.
There needs to be some sort of mechanism for pass through templating. By this I mean that you should be able to pass templates that will be used by the algorithm. For instance, you've got a dungeon in the game and you already know what the final boss should be and that defeating him will give you a key to another dungeon. You don't want to leave that up to change, so you want to be able to tell the Dungeon template that you want Boss A and Reward B to be used by the level generator for the last level. Some decisions need to be made at higher levels and then delegated to where it will be implemented.
With this system, designing a new game would be a matter of creating templates and organizing them appropriately. Then there would be one big template at the top which puts everything together. That almighty Game template. That's where the final destination of PGC is going to be. When you can define the Game template, you've won.
Organizing templates can be done by putting them into sets that are used by other other templates. For instance, you could have a set of "dungeon monsters". Then if you create a Balrog (or download one from a friend's website), simply drop the Balrog into that group (it will automatically be added to the "all monsters" set) and it will become available to any dungeon to be added. This set mechanism, which I need to consider further, will allow the idea of plugins to happen in a realistic way.