Overview
Smithy uses Generator objects to procedurally generate object hierarchies, both in the Editor Object Assembler and at runtime. When Generators are instanced, they build a reference collection of Plug Nodes . When needed, Generators are passed one or more GameObjects. The Generator then searches this object for Socket Nodes.
If any are found, the Generator finds Plug Nodes that share Tags with the Socket Node. The Generator will then make a selection from these Plugs, based on selection weight. The selected Plug's GameObject is instantiated as a child of the Socket object, and the Plug and Socket are aligned and connected in world space.
The Generator then checks the instanced Plug object for Sockets as well. If any are found, the process begins again, as long as the initial Socket object is still above its maximum generation depth.
Generator Class
public class Generator(IEnumerable<string> resourcePaths, int seed)
The runtime Generator class is named Generator
, and is the only class that actually handles the recursive procedural generation routine. Generators are objects, instanced like so:
Generator myGenerator = new Generator();
This will instantiate a new Generator object with an unseeded Random provider, and will create a plug reference collection of all Smithy-enabled objects in the Resources folder. These properties can be changed by supplying the following parameters:
Parameters
Random Seed
Generators can be provided with an int
value as a random seed. Generators that share the same seed and operating with the same inputs will generate consistent results every time.
int mySeedValue = 42;
var myGenerator = new Generator(mySeedValue);
Seeds can be set and tested in the Editor Object Assembler.
A seed value of 0 will result in an unseeded Random generator.
Resource Paths
By default, Generators reference every Smithy-enabled object in a project's Resources folder. However, Generators can also be passed an IEnumerable<string>
collection. These strings are paths relative to the Resources folder of your Unity project, and will limit the plug reference collection the Generator is able to create:
var resourcePaths = new[]{"/Level1/A", "/Level1/B", "/Level1/C"};
var myGenerator = new Generator(resourcePaths);
In the example above, the Generator will only look for Plug Nodes from the Resources/Level1/A, Resources/Level1/B, and Resources/Level1/C folders. This is useful in limiting the scope of objects that can be generated, as well as increasing generation performance.
Generation Options
Generators can be passed an array of GenerationOption[]
, that modify the Generator's behavior. These options will affect every Generate
call the Generator makes. These same options can be passed on a per-call basis.
GenerationOptions.DisableJitter
Prevent Node jitter from being applied on instantiation.
GenerationOptions.DisableMaterialSelection
Prevent Node material selection from being applied on instantiation. All Plug objects will instantiate with their default materials.
GenerationOptions.ForceFillSockets
Ignore Socket Fill Probability. All sockets in the hierarchy will be filled with Plugs.
GenerationOptions.IgnoreSelectionWeight
Ignore Plug Selection Weight. All plugs will have an equal possibility of selection.
Methods
Generate
public GameObject Generate(GameObject baseGameObject)
public GameObject Generate(GameObject baseGameObject, GenerationOptions[] options)
The Generator recursively instantiates a GameObject
hierarchy, staring from the provided GameObject. It will continue until all Socket Nodes have been checked, or, the hierarchy reaches its maximum generation depth.
This method returns a GameObject
, an instance of the baseGameObject
parameter. In most cases, baseGameObject
will be a prefab, although any GameObject
can be passed.
var myGenerator = new Generator();
var myGameObject = Resources.Load<GameObject>("baseObject");
var myProceduralObject = myGenerator.Generate(myGameObject);
If the GameObject
exists in the hierarchy, the Generator will not instantiate a new copy. Plugs will be generated for the extant copy.
public GameObject activeBaseObject;
void Start()
{
var myGenerator = new Generator();
myGenerator.Generate(activeBaseObject); //This will still return a reference to "activeBaseObject".
}
Keep in mind that in most projects, creating a new generator for each Generate
call is performance expensive, and is almost always unnecessary.
Additionally, Generate
can be passed an array of GenerationOption[]
that operates in the same way the Generator instantiation parameter does, but only over the single Generate
call.
Include
public void Include(IEnumerable<GameObject> includedAssets)
The Generator will search the supplied IEnumerable<GameObject>
for Plug Nodes and add them the the Generator's Plug reference collection. Future Generate calls will also include these Plugs.
var morePlugs = GameObject.FindGameObjectsWithTag("Some_Tag");
var myGenerator = new Generator("/Foo");
myGenerator.Include(morePlugs);
Asset Bundles
AssetBundle Assets can be added to Generator selection sets by first loading the AssetBundle content then using Include
to add it to the Generator. As Generators can't be created from AssetBundles, this is currently the only way to generate from bundled content.
To build a Generator that selects from only AssetBundle Assets, first create a new Generator with a Resource Path parameter that leads to an empty Resource folder. Then, Include
the loaded AssetBundle content, like so:
var myLoadedAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle"));
var myLoadedAssets = myLoadedAssetBundle.LoadAllAssets<GameObject>;
var myGenerator = new Generator("/EmptyFolder");
myGenerator.Include(myLoadedAssets);
Exclude
public void Exclude(IEnumerable<GameObject> excludedAssets)
Any Plug Nodes in the excludedAssets
collection will be removed from the Generator's Plug reference collection, if they exist.
var oldPlugs = GameObject.FindGameObjectsWithTag("Level1");
myGenerator.Exclude(oldPlugs);
Exclude
is best used when managing Generators that operate over large reference collections, or very specific ones (such as selection sets based on achievements or player progress). Use Exclude
when it would be overly difficult, performance-intensive, or time-consuming to instantiate a new Generator with the appropriate Plug reference collection.
Set Depth
public void SetDepth(int depth)
Sets the Generator's maximum generation depth to the supplied int
. Used when tying generation depth to a runtime-specific variable, or set of variables. Very useful in setting generation depth based on performance settings, for example.
int depth;
if (Application.platform == RuntimePlatform.Windows)
{
depth = 20;
}
else
{
depth = 5;
}
var myGenerator = new Generator();
myGenerator.SetDepth(myDepth);