Search Unity

Improve workflows, validate decisions, and avoid errors with Presets

October 11, 2019 in Technology | 10 min. read
Share

Is this article helpful for you?

Thank you for your feedback!

With Presets, you can customize the default state of just about anything in Unity – Components, Importers, Managers – without coding. Presets can benefit development teams of all sizes, from streamlining repetitive tasks or validating design decisions, to enforcing standards and project templating. In this post, we’ll dig into many of the features provided by Presets, including basic functionality, tips and tricks, and then delve into some advanced use cases.

First things first: what is a Preset?

Fundamentally, a Preset is an Asset that allows you to override the defaults for a given Component, Importer, or Manager (anything that extends Unity.Object, in fact). Would it make more sense in your project for a RigidBody to default to a mass of 10 instead of 1, but have gravity turned off? Make it so with a preset.

We can use Presets on things that get imported or instantiated in the hierarchy: Textures, FBX files, MonoBehaviour components like Light, Camera, or RigidBody.

Note that Presets are an Editor feature, designed to improve your authoring workflow, not a runtime feature. The Presets aren't shipped with your binary, so don't expect GameObject.AddComponent<RigidBody>() to apply these same defaults. However, the Editor-time ObjectFactory API does support Presets, so ObjectFactory.AddComponent<RigidBody>() operates with the assumption that the project Presets will be respected.

To create a Preset, all you need to do is go to an existing importer or component of the same type, make your adjustments to it in the Inspector, then click the Preset Icon in the upper right. A window will open where you can choose "Save Current To…" to save out the asset.

Tips and tricks for working quickly with Presets

Here are a few fun ways to apply Presets in your project.

Click the Preset Icon

OK, this one is (hopefully) pretty obvious. Clicking the Preset Icon in any Inspector will allow you to select any preset for the same component or file Type.

Drag-and-drop to Inspector

You can simply drag a preset from the Project Window and onto a component in the Inspector if you want to change the values of that component.

You can also add a new Component to a GameObject by dragging the preset into the Inspector.

Drag-and-drop to Hierarchy

Presets can also be used to create new objects in the hierarchy. You can quickly create a new GameObject containing the preset's associated component by dragging from the Project Window straight into the hierarchy.

This workflow is a little bit like working with a prefab, but note that Presets are not prefabs! The preset – by design – maintains no link with any object created from that preset.

You can even drag-and-drop multiple Presets and generate a GameObject with all the associated components!

Managing Presets

Presets work in conjunction with the Preset Manager, a UI found within Edit →  Project Settings…

To add a default, open the Preset Manager, click "Add Default Preset", then select the Type for which you want a default. Finally, click the circle to the right of None (Preset) and select a preset asset that matches the Type from the Select Preset popup.

Here's a more populated Preset Manager in which we can see the RigidBody 10 preset that we created at the start of this blog post, applied as the project default for RigidBody.

With this setup, every RigidBody created will, by default, come with a mass of 10 and gravity disabled.

But check out how the Preset Manager allows more than one default per Type. This powerful extension of the preset idea – new in 2019.3 – means that we can have multiple "defaults" between which we can discriminate based on a name. Looking at Camera in the image above, you'll observe that the blank filter uses the Camera Gameplay preset. This means that by default every camera will use this preset. But we've tied the filter "UI" to a Camera UI preset for use with our UI cameras. With this setup, if I create any camera with "UI" anywhere in the GameObject name – "UICamera", "Camera UI", or "HUDUICamera" –  the Camera UI preset will be used in preference to the default one.

Some notes about Preset Manager:

  • String matching is case-insensitive, so in the "UI" example above be careful of a name like "GuideCamera", which includes the matching "UI" string but may not be what you intended!
  • Where an object matches multiple defaults for a single type, the preset applied will be the LAST match in the list.
  • You can reset any component to a default by choosing "Reset" for that component.
  • For importers, it's the file name, rather than the GameObject name, that drives which filter gets used.

Presets for managers

One of the great virtues of Presets is their ability to align a team on key art and design decisions: what color are our lights? How heavy do we want RigidBodies to be? What are our standards for importing normal maps? When applying Presets to managers, we can carry this idea forward to the project level. Let's look, for example, at the Physics Manager and the Tags and Layers Manager.

In the example, we've gone to rather a lot of trouble to ensure a specific set of physics interactions between layers (enemies don't interact with other enemies, allies and their bullets don't interact with other allies). These decisions may have relevance for follow-on or adjoining projects, and Presets make it easy to memorialize decisions and share those from one project to another.

In the animation below, watch how we save the two managers out and apply them to a separate project. This can be a big help for bootstrapping new projects in your organization.

Of course there's an API!

For the majority of cases, the visual interface above is probably sufficient, but if you're authoring, say, an import pipeline or building tooling it might help to have programmatic control over your Presets. To this end we've exposed the Presets API for your enjoyment and productivity.

The following examples are all available on Github.

Let's start with a simple example. Imagine we want to add a tool that applies one or more properties from a single light to all the lights in a scene.

[MenuItem("CONTEXT/Light/Replicate Color in Scene")]
public static void ApplyAllLights(MenuCommand command)
{
    // Get our current selected light
    var referenceLight = command.context as Light;
    if (referenceLight != null)
    {
        // Create a Preset out of it
        var lightPreset = new Preset(referenceLight);
        // Find all Light components in the scene of our reference light
        var allLights = referenceLight.gameObject.scene.GetRootGameObjects()
            .SelectMany(r => r.GetComponentsInChildren<Light>(true));
        // Choose which serialized property we want to apply to everyone
        var propertyToApply = new[] { "m_Color" };
        // Apply the Preset with only the selected property to all the Lights
        foreach (var light in allLights)
        {
            lightPreset.ApplyTo(light, propertyToApply);
        }
    }
}

This code takes a reference object, a light, and then creates a Preset on-the-fly (var lightPreset = new Preset(referenceLight)). It then uses m_Color from the referenceLight to apply that value to all other lights in the scene.

Here's how our new tool behaves inside Unity.

Note that this demonstrates a programmatic example of a partial preset, i.e., the code grabs and applies just a subset of the full preset property set. This is a functionality we hope to expose in UI sometime soon.

For our second example, let's look at how we can apply a single preset to all assets of the same Type in a project folder.

[MenuItem("CONTEXT/Material/Replicate Material Color in Folder")]
public static void ApplyAllMaterialsInFolder(MenuCommand command)
{
    // Get our current selected Material
    var referenceMaterial = command.context as Material;
    if (referenceMaterial != null)
    {
        var assetPath = AssetDatabase.GetAssetPath(referenceMaterial);
        if (!string.IsNullOrEmpty(assetPath))
        {
            // Create a Preset out of it
            var materialPreset = new Preset(referenceMaterial);
            // Find all Material assets in the same folder
            var assetFolder = Path.GetDirectoryName(assetPath);
            var allMaterials = AssetDatabase.FindAssets("t:Material", new[] { assetFolder })
                .Select(AssetDatabase.GUIDToAssetPath)
                .Select(AssetDatabase.LoadAssetAtPath<Material>);
            // Select the first color entry; in the standard shader this entry is _Color
            var propertyToApply = new[] { "m_SavedProperties.m_Colors.Array.data[0]" };
            // Apply the Preset with only the selected property to all the Materials
            foreach (var material in allMaterials)
            {
                materialPreset.ApplyTo(material, propertyToApply);
            }
        }
    }
}

As before, we take a reference object, this time a selected Material (var referenceMaterial = command.context as Material), then use it to create a Preset on-the-fly (var materialPreset = new Preset(referenceMaterial)). Then we locate the folder in which this material resides and apply the Color property to all materials in the same folder.

Again, let's see how this plays out in the Editor.

Finally, let's look at a somewhat more complex example. In this snippet, we're taking a selected light and – depending on whether a Light default already exists – either creating that preset or updating the default preset to match the selection. Follow the comments in the code below to understand how it works.

public static void UpdateOrAddLightDefaults(MenuCommand command)
{
    // Get our current selected light
    var referenceLight = command.context as Light;
    // Get the list of default Presets that apply to that light
    var defaults = Preset.GetDefaultPresetsForObject(referenceLight);


    if (defaults.Length == 0)
    {
        // We don't have a default yet, let's create one!
        var defaultLight = new Preset(referenceLight);
        // We're kind people, so let's nicely ask the user where to save this default
        var path = EditorUtility.SaveFilePanelInProject("Create Default Light preset",
                "Light", "preset", "Select a folder to save the new default");
        // If the selected path already contains a Preset, its instanceID would be changed by a plain replace
        // We use this trick to replace the values only so an Object referencing the existing asset will not break
        var existingAsset = AssetDatabase.LoadAssetAtPath<Preset>(path);
        if (existingAsset != null)
        {
            EditorUtility.CopySerialized(defaultLight, existingAsset);
            defaultLight = existingAsset;
        }
        else
        {
            AssetDatabase.CreateAsset(defaultLight, path);
        }
        // Load the existing default list
        // We don't want to lose any configuration that may point specific GameObject because of the filters
        var existingDefault = Preset.GetDefaultPresetsForType(defaultLight.GetPresetType()).ToList();
        // Insert the new one at the beginning of the list with no filter
        // so it applies to any Light that doesn't have a default
        existingDefault.Insert(0, new DefaultPreset("", defaultLight));
        // Set the new list as default for Lights.
        Preset.SetDefaultPresetsForType(defaultLight.GetPresetType(), existingDefault.ToArray());
    }
    else
    {
        // We want to update the values only to the last default
        // because maybe other Presets apply to other objects first
        // and we don't want to change them
        var lastPreset = defaults.Last();
        lastPreset.UpdateProperties(referenceLight);
    }
}

Here's an animation showing how this works in Unity. Notice how on our first pass through we are guided through the creation of a new preset, but on our second pass, we simply update the existing one.

Next steps: Presets are what you want them to be!

We have several ideas in the works for upcoming versions of the feature, including (as mentioned above) partial Presets, improved filtering functionality, and application of Presets to folders. But as we said at the outset, Presets are all about making Unity behave the way you need it to behave. With that in mind, it's important to us that we hear from you. Do you have ideas for what you could be doing to improve your workflow with this feature? Let us know your thoughts in the comments!

October 11, 2019 in Technology | 10 min. read

Is this article helpful for you?

Thank you for your feedback!