Concept
SporeLab is a niche tool that initially only aimed to reconcile missing references and empty folders caused by switching between branches in a version control system (mainly Git). Based on my experience, this issue persisted until the 2020 tech stream of the Unity editor.
SporeLab’s concept eventually evolved into being an experimentation tool, meaning it would allow developers to, for instance, quickly prototype an idea branching from their current project without breaking changes made in production.
Of course, one could just use version control and create another branch to experiment their idea, but the root problem aforementioned remains. Hence, I considered this tool to be very niche.
Menus
When an experiment is backed up, SporeLab creates a copy of the experiment in the project and moves its files to the project’s persistent data location. Users can create multiple backups differentiated by the time the backup was created.
Sampling Optimization
On much larger projects, each asset can depend on multiple assets, which would be a pain every time the user wants to create an experiment and load samples. So, I created a class that would refresh and cache assets to decrease the lookup time when loading samples and their dependencies.
AssetIndexer class
public static class AssetIndexer
{
// Key: asset path; Value: asset dependencies
private static Dictionary<string, string[]> assetIndex = new Dictionary<string, string[]>();
/// <summary>
/// Initializes asset index by caching dependencies for all valid asset paths.
/// </summary>
internal static void Initialize()
{
assetIndex.Clear();
var paths = AssetDatabase.GetAllAssetPaths()
.Where(path => path.StartsWith("Assets/")) // Must be inside project
.Where(path => !AssetDatabase.IsValidFolder(path)) // Must not be a folder
.Where(path => !SporeLab.Configuration.Ignored(path)) // Must not be user-ignored
.Where(path => !SporeLab.Configuration.ContainsIgnoredPath(path)) // Must not be subpath of a user-ignored path
.ToArray();
for (int i = 0; i < paths.Length; i++)
{
AddAsset(paths[i]);
}
}
/// <summary>
/// Gets the asset's dependencies.
/// </summary>
/// <param name="path">The asset path.</param>
/// <returns>An array of asset paths that the input asset path depends on.</returns>
internal static string[] TryGetDependencies(string path)
{
if (assetIndex.TryGetValue(path, out string[] dependencies))
{
return dependencies;
}
// On-demand caching
AddAsset(path);
return assetIndex[path];
}
/// <summary>
/// Updates assets' dependencies.
/// </summary>
/// <param name="paths">The array of asset paths.</param>
internal static void UpdateDependencies(string[] paths)
{
for (int i = 0; i < paths.Length; i++)
{
assetIndex[paths[i]] = AssetDatabase.GetDependencies(paths[i], true);
}
}
static void AddAsset(string path)
{
if (!assetIndex.ContainsKey(path))
{
assetIndex.Add(path, AssetDatabase.GetDependencies(path, true));
}
}
}
Example Experimentation
While an experiment is being created, SporeLab looks for dependencies that a sample - an object selected for experimentation - depends on. In the image, an experiment called ExpCompare
was created. These were the steps for populating the experiment:
- The file
Tables.asset
was selected as a sample. - SporeLab searches dependencies for
Tables.asset
and findsTableCollection.cs
. - SporeLab experimentalizes
Tables.asset
andTableCollection.cs
. - The files
ExpCompare_Tables.asset
andExpCompare_TableCollection.cs
are created.