Search Unity

Presets Feature

Discussion in 'Editor Workflows' started by markvi, Aug 29, 2017.

  1. laessnb

    laessnb

    Joined:
    Jun 10, 2014
    Posts:
    101
    Cool, all of this sounds awesome!
     
  2. Revolter

    Revolter

    Joined:
    Mar 15, 2014
    Posts:
    216
    Cannot configure the preset for CanvasScaler
     
  3. BrunoMikoski

    BrunoMikoski

    Joined:
    Feb 25, 2014
    Posts:
    5

    If anyone is needing a solution for Unity 2018, I've made this version to help me dealing with presets per folder, its not as fancy, but does the job!


    https://github.com/badawe/PresetManager

    Allow you customize what PropertyModification you want per folder specifically.
     
  4. Revolter

    Revolter

    Joined:
    Mar 15, 2014
    Posts:
    216

    That's awesome, I will check it out, thanks!
     
    BrunoMikoski likes this.
  5. fherbst

    fherbst

    Joined:
    Jun 24, 2012
    Posts:
    802
    Is there a particular reason why presets for Material are not working? It does not show in the preset manager list and it's not possible to make a specific Material a preset.
     
  6. bastien_humeau

    bastien_humeau

    Unity Technologies

    Joined:
    Jun 14, 2017
    Posts:
    191
    It is intended to be excluded from the Preset Manager. You cannot set a default for Materials with the Preset system. Maybe it should be investigated at some point and allowed if possible? I could check with the team responsible for Materials to see whether they can have a look into it. I remember that we initially excluded it because of implementation details on how new materials are created and the amount of work required to support default for them. It could be re-evaluated and maybe this decision can be changed now.

    It should be possible from any Material to create a Preset, save it in the project, modify it, and apply it to any other Material though. I can see the Preset button in the Material header and create a new preset in any of my local projects at least?
     
  7. fherbst

    fherbst

    Joined:
    Jun 24, 2012
    Posts:
    802
    The problem is that from a workflow perspective, Material presets are one of the more important ones.
    Especially in projects with URP/HDRP, there's often a custom "main shader" that is used for most materials in a project.
    For these cases, a technical artist / lead would want to set the default material properties so artists can properly use that, and also that material would / should be used when importing assets.

    Somehow it seems that URP/HDRP somehow "hack" the system and inject their own "material presets"? How are they doing that? (would at least be a workaround)
     
    Unifikation likes this.
  8. MikePage_Artrix

    MikePage_Artrix

    Joined:
    Mar 18, 2016
    Posts:
    15
    @bastien_humeau Hi Bastien, a quick Q, should the import presets be applied when 'reimporting' an existing asset? We've got a load of existing textures and we've setup default presets based on various string filters. But when we reimport the asset the preset is not applied.

    Thanks!
     
  9. bastien_humeau

    bastien_humeau

    Unity Technologies

    Joined:
    Jun 14, 2017
    Posts:
    191
    HDRP and URP keep a copy of some material they have decided would be the default ones and they duplicate those ones on demand. This not Presets at all, just default version of the assets they need.
    I'm having a look at how Material creation is working and maybe we could make some changes to support Presets when created in some folders. No promises here, but we will try to look into it.

    No, default only apply when an asset is imported for the first time. However, you can use the Reset on any importer to change its values back to the defaults, and that will use Presets.
    From script, it is also possible to use Unsupported.SmartReset to get the same behaviour than the inspector right-click Reset.
    Another option if you are using Partial Presets and does not want to Reset the full importer, is to gather the list of Presets from the manager and apply each of them to your importer.
    Code (CSharp):
    1. public void Test()
    2. {
    3.     // Load the importer
    4.     var importer = AssetImporter.GetAtPath("Assets/someasset.extension");
    5.     // Gather all Presets in the PresetManager that apply to this Importer
    6.     foreach (var preset in Preset.GetDefaultPresetsForObject(importer))
    7.     {
    8.         // Apply all of them in order
    9.         preset.ApplyTo(importer);
    10.     }
    11.     // Set the Importer dirty to make sure the AssetDatabase will write the metafile to disc.
    12.     EditorUtility.SetDirty(importer);
    13.     // Trigger re-import with new values
    14.     importer.SaveAndReimport();
    15. }
    Edit: The above script should be a manual step that you can add in your project. Making it automatic on assets import would create dependencies issues and inconsistency in the Library or asset cache server.
     
    andreiagmu and fherbst like this.
  10. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    @bastien_humeau Could you give me some insight on how Presets filter the serialized properties?

    In a couple of my plugins, I've been iterating over a component's variables via SerializedProperty.NextVisible(true) but I only recently discovered that it excludes fields marked with
    [HideInInspector]
    (even though they are serialized). When I use SerializedProperty.Next(true), I get a bunch of undesired properties like "m_CorrespondingSourceObject", "m_PrefabInstance", "m_Father" (lol) and so on. I see that Presets either exclude these properties or simply store their values as null.

    Is there a function that returns true for Unity's internal properties or are you filtering the properties in another way? I'd really appreciate your reply.
     
    fherbst likes this.
  11. bastien_humeau

    bastien_humeau

    Unity Technologies

    Joined:
    Jun 14, 2017
    Posts:
    191
    Nothing exposed, unfortunately, the filtering is done through different systems in the native Preset code.
    We are serializing Objects as if they were part of both a Prefab and a Metafile. This allows us granularity to exclude properties from AssetImporters that are asset-related and should not be stored in Presets, along with re-using the Prefab exception system that ignores all the properties you listed in your post.

    SerializedObjects are using their own flags when reading/reading all properties from an Object which is UI related and not aware of either Preset of Prefab way to read through them.
     
    yasirkula likes this.
  12. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Thank you for the reply :) As a result, I've created my own workaround solution.

    My best wishes for the Presets team, the new "applying partial set of properties" feature introduced with 2020.1 will definitely make the tool more useful (although the linked documentation doesn't talk about this feature yet).
     
  13. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    Is there any way I can fix it in cases where this internal filtering fails me? For example, I'm using the PSDImporter package, but the preset saves a lot of things that should not be part of it, such as rects for individual sprites or the names of layers. These belong to each imported asset instance, not on the preset. Is it safe if I simply delete some of the YAML in the preset file? It currently has 264 properties, but it should only save whatever the texture importer shows (so basically maybe 30).
     
  14. bastien_humeau

    bastien_humeau

    Unity Technologies

    Joined:
    Jun 14, 2017
    Posts:
    191
    It is safe, but will probably not have the result you expect. Each missing property will be added back with its default value, which means you will end up enforcing "no sprite" instead of not overriding sprites values.
    Starting in 2020.1, we have added the notion of Partial Presets, it is now possible to set a list of properties you want to be ignored in your Preset, either from the UI or using the Preset.excludedProperties API. I would recommend you to go that way if you want your importer presets to never apply the sprites data.
     
    Xarbrough likes this.
  15. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    I keep coming back to this because it kind of spoils the entire preset feature for me. How did Unity think this would work for anything but Textures? I've started creating some presets and it's more work than coding my own importer because I have to manually review every property on the object for exlusion before the preset is usable. Now what do I do about, e.g. 'Object for Packing' on the Sprite Atlas? It's pretty unlikely I ever want to save the list of sprites as an atlas preset, but I also can't click the property to exclude it, because the editor doesn't support it. The debug inspector also doesn't show the exclude popup. So now I need to dig into the YAML file to find the property names manually? And it's not that easy, since it doesn't seem to work if I add "m_PackedSprites" to the excluded properties list.

    Is Unity currently working on improving the presets feature on this end? I'd love for this to work, but I really think presets should not include all properties by default. It's probably too difficult for Unity to decide on the set of relevant properties as well, so I would rather make everything opt-in. For example, when creating a new preset from a target object, I imagine a popup wizard that shows me all properties with toggles next to them. By default, Unity selects reasonable things (texture compression settings etc), but then I could quickly select which ones I want.
     
    andreiagmu, DanQuell and firstuser like this.
  16. firstuser

    firstuser

    Joined:
    May 5, 2016
    Posts:
    147
    I realllllllllly wish that Unity would display the text serialized .asset stuff as beautifully as the .preset files, idk if it's even possible to be honest but you can probably see where my enthusiasm comes from when comparing:

    .asset
    Code (CSharp):
    1. m_ColorGamuts: 0300000000000000
    .preset equivalent
    Code (CSharp):
    1. - target: {fileID: 0}
    2.     propertyPath: m_ColorGamuts.Array.size
    3.     value: 2
    4.     objectReference: {fileID: 0}
    5.   - target: {fileID: 0}
    6.     propertyPath: m_ColorGamuts.Array.data[0]
    7.     value: 3
    8.     objectReference: {fileID: 0}
    9.   - target: {fileID: 0}
    10.     propertyPath: m_ColorGamuts.Array.data[1]
    11.     value: 0
    12.     objectReference: {fileID: 0}
    Size, Type, etc all clearly visible.
    :D
     
  17. sabint

    sabint

    Joined:
    Apr 19, 2014
    Posts:
    26
    Hi, can you please allow saving presets in Package folders? Often I'm working on a script that's in a package, and the Editor won't allow me to save it anywhere except the containing project.
     
    firstuser and fherbst like this.
  18. bastien_humeau

    bastien_humeau

    Unity Technologies

    Joined:
    Jun 14, 2017
    Posts:
    191
    Hello!
    I think that if it's an internal package in your project (as in folder and files directly inside the Packages folder and not from the package cache), then it should be allowed to create Presets in this folder.
    Could I ask you to open a bug for this issue and let me know the bug number? We will look into it as soon as possible.
     
  19. sabint

    sabint

    Joined:
    Apr 19, 2014
    Posts:
    26
    @bastien_humeau I have filed Case 1359538 for this issue.
    Btw, is there an online interface somewhere to browse existing bugs and maybe vote on them? Would be dope if it did.

    I haven't tested local package, because all my packages are in their own git repos, and cloned into a directory outside the main project (so that I can easily reference from multiple projects).
     
  20. andreiagmu

    andreiagmu

    Joined:
    Feb 20, 2014
    Posts:
    175
    Note: In the post below, I described my situation using texture assets as an example. But I guess this could also be valid to any other type of imported assets.


    Hi! In my project, on the [Project Settings -> Preset Manager] window, I've set some default preset filters for TextureImporter.

    When I import textures from the Asset Store (or external textures with included .meta files), I'd expect that my custom default preset/filters would be automatically applied to the newly imported textures (instead of having to click Reset on each texture's inspector).

    @markvi @bastien_humeau Is this use case not supported?
    In case that's not supported, is there an example of how to achieve this behavior via scripting?

    It could be either triggered automatically on each new texture import (even from the "Asset Store import" or "included .meta file" scenarios I mentioned above), or maybe something like a manual MenuItem that would apply the default presets to all textures of the project (ideally, apply to the textures that aren't using the default presets already).
    This "bulk preset applier" could also have the option to process only the textures inside a specific folder and its subfolders, etc.

    By the way, I'm using Unity 2021.3.18f1.
    If there's already a tool that does what I described above, I'll be grateful if someone can point me to it :D
    @yasirkula This "bulk preset applier" could be an interesting tool for your collection ;)
     
    Last edited: Feb 5, 2023
    yasirkula likes this.
  21. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    There's a little preset button in the inspector for when you select an asset (like your Textures), where you can create/apply presets. So you could search for t:Texture2D, select the folder, mark all, and apply your preset.
     
    Thygrrr and andreiagmu like this.
  22. rfadeev

    rfadeev

    Joined:
    Oct 1, 2013
    Posts:
    21
    You can try to use this example code from Unity to get presets applied automatically on import: https://docs.unity3d.com/2023.1/Documentation/Manual/DefaultPresetsByFolder.html

    Using it as has its own limitations when all assets of matching type inside single folder would get preset from that folder applied. But it could be a good starting point to do the "bulk preset applier" you mention.

    To apply default preset to all textures in the project using PresetsPerFolder, you can filter all assets with "t:texture", select all, right-click and "Reimport".

    But I agree, default preset behavior is not well documented for the assets imported from Asset Store packages. I think similar problem applies to samples from Unity Package Manager packages.
     
    andreiagmu likes this.
  23. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    838
    How can i apply newly made rules in the Preset Manager to existing assets?

    I can Reset the assets, but that makes me lose *all* settings. I'd like to override the one setting defined in the Preset, which has the reset set to not be included.
     
  24. AxonGenesis

    AxonGenesis

    Joined:
    Jun 24, 2016
    Posts:
    90
    Is there a scripting callback or way to detect when a preset is applied to a component?

    The problem I am encountering is that when I apply a preset for my custom component type, it includes absolute object references. For example, if I save a preset for object A and then apply it to object B, it is applied with references to object A, instead of remapping to B.

    What I'd like is a way for my component script to detect when a preset is applied so it can handle additional setup to remap references to local objects. Under normal usage conditions I cannot make any assumptions about what the object references should be.

    Otherwise the preset system works perfectly, so I'm hoping there's a way I can solve this to keep the existing workflow and avoid creating a custom preset solution.
     
  25. AxonGenesis

    AxonGenesis

    Joined:
    Jun 24, 2016
    Posts:
    90
    I believe I found a solution though it's a bit of a hack, so I'm open to a better idea if anyone has one.

    I added the serialized field IsPreset to my component base class. CheckPreset() is called from my editor UI script (OnInspectorGUI()) and makes the assumption that if the game object has no parent and no scene or scene name, then it is a preset object. When the preset is applied to an object in the scene, the IsPreset flag is set to true and triggers OnPresetApplied() once.

    Code (CSharp):
    1. #if UNITY_EDITOR
    2.         public bool IsPreset = false;
    3.  
    4.         public void CheckPreset()
    5.         {
    6.             /// If was saved previously as a preset
    7.             bool wasPreset = IsPreset;
    8.  
    9.             /// A preset will not belong to a scene
    10.             IsPreset = gameObject.scene == null || string.IsNullOrEmpty(gameObject.scene.name);
    11.          
    12.             /// Preset objects cannot have a parent transform
    13.             if (transform.parent != null) IsPreset = false;
    14.  
    15.             if (!IsPreset && wasPreset) {
    16.                 OnPresetApplied();
    17.             }
    18.         }
    19.  
    20.         public virtual void OnPresetApplied()
    21.         {
    22.             Debug.Log(name + ".OnPresetApplied");
    23.         }
    24.  
    25. #endif
    The caveat to this approach is that it only works on singly selected items and will not work correctly with an unparented object in a new scene that hasn't been saved yet, however that is an unlikely use case for the component types I'm working with. Also, OnPresetApplied may be invoked when instantiating a prefab containing the component in question, however that's an added benefit since the same referencing conditions would need to be checked and corrected to avoid nulls (or object references no longer in the current context).
     
  26. AxonGenesis

    AxonGenesis

    Joined:
    Jun 24, 2016
    Posts:
    90
    I ended up abandoning the built-in presets system and implemented my own using ScriptableObjects. There are several features that would make the preset system more useful for a wider variety of components:
    • Ability to exclude specific properties
    • Callback methods to handle special data when saving and applying presets
    The main issue with my components is that presets affect object references and other properties that don't make sense to be in the preset and result in errors.

    If anyone else is looking for a custom solution, here's what I did. This uses my own flavor of MonoBehavior as AxonGenesisBehavior, which is the base class for my components. BehaviorPreset is the base class for my presets, using reflection to assign fields with matching names.

    Code (CSharp):
    1.    
    2.    public class BehaviorPreset : ScriptableObject
    3.    {
    4.  
    5.         public bool DebugEnabled = false;
    6.  
    7.         public virtual void ApplyTo(AxonGenesisBehavior target)
    8.         {
    9.             if (target == null) return;
    10.  
    11.             Undo.RegisterCompleteObjectUndo(target, "Apply Preset");
    12.  
    13.             Type type = GetType();
    14.             Type targetType = target.GetType();
    15.             FieldInfo[] targets = targetType.GetFields();
    16.             FieldInfo[] fields = type.GetFields();
    17.             foreach (FieldInfo f in fields) {
    18.                 foreach (FieldInfo t in targets) {
    19.                     if (t.Name == f.Name) {
    20.                         var val = f.GetValue(this);
    21.                         if(DebugEnabled) Debug.Log("Write: " + f.Name + " => " + val);
    22.                         t.SetValue(target, val);
    23.                         break;
    24.                     }
    25.                 }
    26.             }
    27.             //target.Name = name;
    28.             target.OnPresetApplied(this);
    29.             Debug.Log("The preset '" + name + "' has been applied to '" + target.name + "'.");
    30.         }
    31.  
    32.         public virtual void ReadFrom(AxonGenesisBehavior target)
    33.         {
    34.             if (target == null) return;
    35.  
    36.             Type type = GetType();
    37.             Type targetType = target.GetType();
    38.             FieldInfo[] targets = targetType.GetFields();
    39.             FieldInfo[] fields = type.GetFields();
    40.             foreach (FieldInfo f in fields) {
    41.                 foreach (FieldInfo t in targets) {
    42.                     if (t.Name == f.Name) {
    43.                         var val = t.GetValue(target);
    44.                         f.SetValue(this, val);
    45.                         if (DebugEnabled) Debug.Log("Read: " + f.Name + " => " + val);
    46.                         break;
    47.                     }
    48.                 }
    49.             }
    50.             name = target.Name;
    51.             target.OnSavePreset(this);
    52.         }
    53.    }
    54.  
    To create a preset for each type, I derived a new class and add the properties I want the preset to store. For example, for my Tween component (simplified for this example). The members of the class were simply copy-pasted from my main component class.

    Code (CSharp):
    1.    
    2.    public class TweenPreset : BehaviorPreset
    3.    {
    4.         public float DefaultValue = 0f;
    5.         public float MinValue = 0f;
    6.         public float MaxValue = 1f;
    7.  
    8.         public float Amount = 1f;
    9.         public float Phase = 0f;
    10.         public float InPoint = 0f;
    11.         public float OutPoint = 1f;
    12.         public float Smoothness = 1f;
    13.     }
    14.  
    The code to expose all of this in the UI is a bit lengthy but essentially I created my own drop-down menu using the built-in preset icon as a button (so the user experience is somewhat similar) and implemented a popup window for naming presets and handling basic logic.

    A list of presets can be retrieved for each type using FindAssets:

    Code (CSharp):
    1.  
    2.    ScriptableObject[] presets = GetAllInstances(presetType);
    3.  
    4.         public static ScriptableObject[] GetAllInstances(Type type)
    5.         {
    6.             if (type == null) return null;
    7.             string[] items = AssetDatabase.FindAssets("t:" + type.Name);
    8.             if (items != null && items.Length > 0) {
    9.                 ScriptableObject[] instances = new ScriptableObject[items.Length];
    10.                 for (int i = 0; i < items.Length; i++) {
    11.                     string path = AssetDatabase.GUIDToAssetPath(items[i]);
    12.                     instances[i] = (ScriptableObject)AssetDatabase.LoadAssetAtPath(path, type);
    13.                 }
    14.                 return instances;
    15.             }
    16.             return null;
    17.         }
    18.