Squidi.net Sites:     Blog   |   Webcomics   |   Three Hundred Mechanics   |   Three Hundred Prototypes   |   Free Pixel Project
 

  Blueprints Detailed - Overviw

This is a quick history of the Blueprint concept, along with some of the basic premises behind its operations. By the end of this article, hopefully, you'll understand why I think blueprints are so darn important.

 

  A Brief History

Way back in January 2007, I tried to break down procedural design into three elements: algorithm, input, and seed. The input defines what the algorithm will create, while the seed defined the input. I tied this altogether into a system that I called Templates.

A Template was little more than a way to define changing values through a simplified programmable interface. For instance, an algorithm that creates a dungeon may allow rooms to be between 3 and 10,000 tiles in size, but using purely random values can create extremely unpredictable results. A better approach, I thought, would be to have the input limit the possible values rather than the algorithm. So, I figured you could define the input as a piece of code:

roomSize = [random 5 15]

By combining all the values for a particular algorithm into a single template, you could define classes of output. Do this enough and you can start to define your game based on the composition of those classes. For instance, a small level might have between 5 and 6 rooms while a large level 10 - 15 rooms. You can then define dungeonA as a small level, small level, large level. You can define a world as having dungeonA and dungeonB. You can compose the entire variety of a very complex game within this template system using just the one level design algorithm!

Over time, I've refined the concept a bit and added a few wrinkles. I've decided to pass over the term "Template" for "Blueprint" because it seemed to make a better metaphor. You give a blueprint to a contractor and he'll build the house to the specifications listed in the blueprint.

 

  Anatomy of a Blueprint

A blueprint is not a particularly complicated beast. At its most simple, it is just a named object (like FireDungeon) with a list of named properties that represent values to a procedural algorithm. When an algorithm is about to create a game object, it is passed a blueprint that will define that object to some extent. Note that a blueprint doesn't have to represent the final values - just the input needed to derive those values. For example:

@blueprint FireDungeon
  @property tileSet = "firetiles.png"
  @property numberOfRooms = (rand 5 15)
@end

This is a really simple example, but hopefully, you'll note that is doesn't look a whole lot different from the original concept for Templates. The thing to notice here is that properties are actually a bit of LISP-like code that evaluates to a specific number. So (rand 5 15) will produce a different random number between 5 and 15 every time it is called.

Generally speaking, however, you don't want it to return a different value EVERY time. You just want to evaluate that number once. This is where the mastering feature of blueprints comes in. Like in publishing, you can create a master of a blueprint which represents a final, ready for publishing version. What this does is just goes through the list of properties and evaluates each one to a final value. So the master candidate for the above blueprint might look something like this:

@blueprint FireDungeon MASTER
  @property tileSet = "firetiles.png"
  @property numberOfRooms = 7
@end

That way, every time you query numberOfRooms, it will return the same value. Only properties are mastered, so you can still have user functions that will return unique values every time they are queried (you can create spawn and loot tables this way). Mastering will seem more important when I discuss Blueprint Factories and Mods in a moment.

Blueprints are hierarchal. Each blueprint can have a single parent from which it inherits all its properties, user functions, and variables from. So you can have a Spear which is descended from Weapon, and Weapon which is a child of Item. Any properties declared on Item will also be on Spear. Spear can also override any inherited properties with its own values.

The final wrinkle in the basic Blueprint is that blueprints can reference each other. For instance, you can create a Blueprint that creates a spear, then the Blueprint which creates the CaveMan can reference the spear blueprint. You can even create lists of blueprints or procedurally make decisions about them:

@blueprint Item
  @property name = "Some Item"
  @property value = 1
@end

@blueprint Weapon : Item
  @property name = ""
  @property damage = 1
@end

@blueprint Spear : Weapon
  @property name = "Worn Spear"
  @property damage = (rand 10 15)
@end

@blueprint PointedStick : Weapon
  @property name = "Pointed Stick"
  @property damage = 6
  @property value = 2
@end

@blueprint CaveMan : Enemy
  @property name = "Angry CaveMan"
  @property weapon = (pickOne PointedStick Spear)
  @property loot = (Fire LoinCloth PirateHat)
@end

When you try to create a CaveMan, the weapon property will be randomly set to either the PointedStick or Spear blueprints, which you can then pass to the weapon generator algorithm to be built. His loot property will return a list of three blueprints as well. We just defined a new enemy with a randomly selected weapon without touching a line of code!

In this way, we can define a CaveMan as both a series of specific values and as a composition of several other blueprints. In fact, most objects in a videogame (or in real life) can be described based on their attributes and composition. By progressively building up, composing objects of other objects, we can literally define an entire game through blueprints. Not just a game either. A procedurally generated game.

 

  Domains

Blueprints are great and powerful and all, but with hard references to each other, it is very hard to add new blueprints to a closed system without changing things. For instance, what if you want to add a new potential weapon to the CaveMan's arsenal? Well, you'd have to manually add it to the CaveMan each time you wanted to add one. If you have a dozen or even hundreds of enemies, this can be a very big pain in the butt, and a problem which can lead to lots of small mistakes that are difficult to catch.

What would be better is to be able to grab all the blueprints that were weapons and also primitive. First, we'd have to manually add these keywords to the weapons, but that's easy enough to do (especially since the keywords will be inherited)

@blueprint Weapon : Item
  @domain type = weapon
@end

@blueprint PointedStick : Weapon
  @domain type += primitive
@end

Through inheritance and a plus-equals, the PointedStick will have the keywords weapon and primitive associated with the domain named type. When the blueprints are added to the big list of blueprints, they will also be collected into sets based on these keywords, which you can then query.

@blueprint CaveMan : Enemy
  @property weapon = (pickOne [type: weapon primitive])
@end

The command [type: weapon primitive] will return a set of all the blueprints with both of those keywords in the type domain. If you add a new weapon to the big list of blueprints, it'll be added to these domain sets as well, so the sets returned by that command will automatically be up to date whenever they are called. You can add as many weapons as you want to the game, and the CaveMan will automagically randomly select it to use. And if you want a weapon that is not primitive, that's easy too: [type: weapon !primitive]

Automatic Domain Sets are the cornerstone to creating an easily extensible collection of blueprints. Being able to define a new object and have it automatically integrated is a very powerful approach to procedural design. It's not enough to define new blueprints. It has to be easy to integrate them as well.

 

  Mods

Playing RPGs, especially of the Diablo variety, you'll frequently come across and item that reads like "Gnarled Staff of Whoop Ass +1". It is an item that is modified from its original value, usually through some named modifier. For instance, in this case, the Staff has the modifiers "Gnarled", "of Whoop Ass", and "+1". Each one of these mods changes the values of the original Staff in some small way.

As luck would have it, mods are an easy addition to blueprints. When mastering a blueprint, you can apply any number of mods in a particular order. The properties on a mod will replace the blueprints property, either outright or with a new value based on the original one.

@mod OfWhoopAss
  @property name = (strcat &source.name " of Whoop Ass")
  @property damage = (* &source.damage 2.3)
  @property value = (+ &source.value 120)
@end

Notice that I use &source.XXX to refer to a property on the object that is being modified. At the moment, properties are the only thing which can be referenced on another blueprint (not variables or user functions), and even then, only &source.XXX for blueprint mods and factories. I might add in the ability to access parent values or something like that, but I'll add that if and when I find a reason to.

This mod, affectionally named OfWhoopAss will modify a blueprint. It will change the name from XXX to XXX of Whoop Ass. It will increase the damage 130% and increase the value by 120.

Blueprint mods are special versions of non-masterable blueprints. As such, they can have variables and user functions.

Mods have a special named domain MODS, and a single list of keywords. It's much simpler than the blueprint version: [MODS: itemPrefix] will return a set of all mods with the itemPrefix category.

 

  Factories

The next major feature of Blueprints are a special type of blueprint called a Factory. Similar to mods, a Factory will modify another blueprint. Unlike mods, a Factory is treated as if it were a blueprint. It'll probably be easier if I just show you:

@factory {MagicalWeapon}
  @substitute (pickOne [type: weapon])
  @modlist ((pickOne [MODS: itemPrefix]) (pickOne [MODS: itemSuffix]))
  @property value = (* &source.value 1.2)
@end

This {MagicalWeapon} isn't a final blueprint in and of itself. Instead, it will substitute itself with a different blueprint (in this case, another blueprint in the type domain with keyword weapon - so essentially any random weapon). This substitute blueprint is created in its stead. The mods declared in the @modlist are then applied to that substitute blueprint. The rest of the properties are then applied in the same way that blueprint mod properties are. They replace previous values with new or derived values.

This particular factory will return a random weapon with a random prefix mod and a random suffix mod, and increase the value by 20%. Again, because of the use of automatic domain sets, you can add new weapons and modifiers and they will automatically be used at no additional cost to you. So, if you want to randomly create a "Gnarled Staff of Whoop Ass", this blueprint factory can do it.

It's through factories that you can do your most impressive composition. For instance, let's say that you want to define a generic weapon drop that is a regular weapon 60% of the time, a magical weapon 30% of the time, and unique 10% of the time... simple. Just define {RandomWeaponDrop} with the property _masterSubstitute = (pickOnChance 60 {AnyBasicWeapon} 30 {AnyMagicWeapon} 10 {AnyUniqueWeapon}).

 

  Conclusion

We've just defined all the weapons and enemies in a game, along with the formulas used to procedurally generate aspects of them, all without creating a single line of code... okay, technically, the Blueprint system is several thousand lines of code, but it is a general purpose library that only needs to be written once.

Basically, if you can define an object based on values and composition (especially PROCEDURAL values and composition), the blueprint system can do it. It has easy expansion built into it, and has several mechanisms that can make a lot of major decisions as data and not code. To give you an idea of how expansive this is, many, if not most, of the roguelike games out there could be done in a single game engine, with their differences being defined entirely as blueprints.

But blueprints aren't all powerful. Though they have their own programming language, they aren't intended to do a lot of the heavy lifting there. You aren't going to write the code to generate a dungeon map inside a blueprint. It is possible, I guess, but it would be going against its specialized purpose. Blueprints are intended to provide the inputs to the algorithms, not the algorithms themselves.

Likewise a blueprint isn't a game object, even though it ultimately describes one. You still need some way to convert the values on a blueprint into a game object usable by the game system. A blueprint is a collection of values. It doesn't know or care how those values will be used. So though you can define an entire game through blueprints, you'll still need code to translate that description into actual game files.

And finally, blueprints don't have a rigid structure. How you define the blueprints, mods, domains, and factories will play a major part in the system's successes. If you aren't careful, an improperly designed system of blueprints could collapse under its own weight. That being said, once the system has been designed, you and your players can add to it easily, and these new additions will just work.

Blueprints solve a small but complicated problem in a broad but simple way. They are the first step in a three step process. The blueprints will define the game, the generators will build the game, and the game engine will play the game. But so powerful are blueprints that with proper generators, they can build the data set for an entire game - not just your game either. Any game. Want to generate a random level for DOOM? You just need to write a generator which will output a WAD file (gosh, I'm old).

Blueprints are just that: blueprints. You give them to the contractor and he will build something according to the specifications listed on the blueprints. You can define as much or as little of a game as you'd like and leave it up to the generator (contractor) to build it.

 

 





Copyright 2009 Sean Howard. All rights reserved.