Search Unity

[RELEASED] Odin Inspector & Serializer - The Ultimate Workflow Tool ★★★★★

Discussion in 'Assets and Asset Store' started by jorisshh, Jun 15, 2017.

  1. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Ah sorry! I completely overlooked your post.

    But nice find, that is a bug on our part. We don't' actually set it to false. We just have our own object field that mimics Unity's and adds support for interfaces etc, but we should only use that where its needed, and use Unity's own object picker wherever we can.

    I like the suggestion to toggle it somehow as well. Maybe something like an [Enable/DisableCrossSceneReferences] attribute... Maybe. We'll fiddle around with it a bit.

    For now. If you used the [DrawWithUnity] It should work.
     
  2. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Hi Julian,

    Not sure I got your question right. But you can display properties in the inspector using the ShowInInspector attribute and from there you can have anything show up in the inspector.

    tata.png

    Note that I've added an empty setter. If no setter is defined, Odin will treat the value as a read-only property which disables the GUI in the inspector, so this is just a way to make everything editable in the inspector.

    Also, by default, Odin draws an object picker for polymorphic types so you can set the value to null, and/or change its type. But in this example, I've added the HideReferenceObjectPicker since we, in this case, have a singleton, and are not interested in that.
     
  3. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    This would actually be fairly simple to do, using a very simple custom property drawer. Basically, you can create a custom attribute, say, HideablePropertyAttribute, and put that on all your name fields that you want optionally hidden. Then you make a drawer for HideablePropertyAttribute that checks whether the inspected type (IE, the component) has a special attribute defined on it, and if it does, it hides the property. Let's call that attribute, uh, HideHideablePropertiesAttribute. (I'm sure you can think of better names - perhaps you want to name it so that it's only meant to be used for names of physical properties in particular. Your call!)

    Here's the attributes and the drawer:

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. #if UNITY_EDITOR
    5.  
    6. using Sirenix.OdinInspector.Editor;
    7.  
    8. #endif
    9.  
    10. [AttributeUsage(AttributeTargets.Class)]
    11. public class HideHideablePropertiesAttribute : Attribute
    12. {
    13. }
    14.  
    15. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method)]
    16. public class HideablePropertyAttribute : Attribute
    17. {
    18. }
    19.  
    20. #if UNITY_EDITOR
    21.  
    22. [OdinDrawer]
    23. public class HideablePropertyDrawer : OdinAttributeDrawer<HideablePropertyAttribute>
    24. {
    25.     protected override void DrawPropertyLayout(InspectorProperty property, HideablePropertyAttribute attribute, GUIContent label)
    26.     {
    27.         var targetType = property.Tree.TargetType;
    28.        
    29.         // Only call next drawer if the hide attribute is not defined on the target type
    30.         if (!targetType.IsDefined(typeof(HideHideablePropertiesAttribute), true))
    31.         {
    32.             this.CallNextDrawer(property, label);
    33.         }
    34.     }
    35. }
    36.  
    37. #endif
    And here's some code showing example component and data type declarations:

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. [HideHideableProperties]
    5. public class NamesHiddenComponent : MonoBehaviour
    6. {
    7.     public Mass mass;
    8.     public Radius radius;
    9.  
    10.     // etc....
    11. }
    12.  
    13. public class NamesShownComponent : MonoBehaviour
    14. {
    15.     public Mass mass;
    16.     public Radius radius;
    17.  
    18.     // etc....
    19. }
    20.  
    21. public abstract class PhysicalProperty
    22. {
    23.     [HideableProperty]
    24.     public string Name;
    25. }
    26.  
    27. [Serializable]
    28. public class Mass : PhysicalProperty
    29. {
    30.     public float Kilogram;
    31. }
    32.  
    33. [Serializable]
    34. public class Radius : PhysicalProperty
    35. {
    36.     public int Meter;
    37. }
    And here's how it ends up in the inspector:



    It would also be fairly easy for you to customize it further - create several different hideable attribute types to distinguish between different hideable categories, or just pass parameters to the class-declared hiding attributes stating what sort of stuff should be hidden, which the drawer can then check against, and so on. There's a variety of possibilities.

    Additionally, Odin has a [Path] attribute coming next patch. It's used like this, and should hopefully provide for all your path needs!

     
    Last edited: Jul 26, 2017
  4. mSkull

    mSkull

    Joined:
    Apr 19, 2016
    Posts:
    11
    I've been working on the Path attribute, and here it is in its current form.
    The mentioned Path attribute is now split up in two attributes FolderPath and FilePath.

    FolderPath in action:


    FilePath in action:


    Both attributes support a wide variety of features:
    • [FolderPath(AbsolutePath = true)]. By default, both attributes will give you a path relative to your Unity project, but if you need it, you can get an absolute path instead.
    • [FolderPath(ParentFolder = "$SomePath")]. You also specify parent folders and get a path which is relative to that. The parent path can either be absolute or relative to your Unity project. This also supports retrieving values from members by using the $ symbol.
    • [FilePath(Extensions = "cs, meta, .png")]. Specify a single or multiple extensions for FilePath, with a comma separated list. This also supports retrieving values from members by using the $ symbol.
    • [FilePath(UseBackslashes = true)]. By default, all paths will use forward slashes to be compatible with iOS. But if you want, you can have backslashes instead.
    • [FolderPath(RequireValidPath = true)]. If you want to ensure a valid path, you can use the RequireValidPath option to display an error for missing directories or files.
    • Drag and drop. You can drag and drop both folders and files from your project view to your path properties.
    • Show in explorer. Right click, and select show in explorer.
    • Create directory. If the specified directory does not exist, you can right click the property and select "Create Directory".
    If you have any requests or suggestions feel free to share! The attributes will be included in the next patch.
     
    Froghuto and bilke like this.
  5. julianr

    julianr

    Joined:
    Jun 5, 2014
    Posts:
    1,212
    Looks good. If I had a list setup on a script like in your example, could I access the values stored in that list from another script. I've had issues bringing back results on unity where it cannot access the custom list values even though its declared public. So going into more detail... Script A contains the list being defined in its own class, and Script B in its own class brings back the results. In my custom list there are strings and int values defined within a public declared class, which is then defined by list<customlist>
     
  6. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    Oh very nice. You guys are amazing! I was actually going to request this feature a while back but wasn't sure about it. You guys just never fail to deliver on more Odin goodness.
     
    bjarkeck likes this.
  7. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    I would still say that it sounds like you just need to make a property that gets and sets exactly what you need. We can show and make properties editable in the inspector. So anything, no matter where it comes from, can be exposed in the inspector.

    I'm a little confused. Is your problem related to the inspector? Or is it more of a c# question? In any case, perhaps you could share the code that is not working for you? That way it would be easier for me to understand what you are trying to do. :)
     
  8. julianr

    julianr

    Joined:
    Jun 5, 2014
    Posts:
    1,212
    Sure. I don't think I have a working example anymore, to the point where I was trying everything to get it working. Its more C# related but I was exposing the public list to the inspector so I could see the values change on the fly. I'll go grab a copy of Odin now and if I get stuck I'll get back to you. Thanks
     
  9. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Lovely :p
     
    julianr likes this.
  10. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    Ah really interesting, thanks a lot for the insight!

    Uh facy pants drag&drop! Really nice implementation, great work!
    Since you seem to be on the hunt for more handy attributes, here is another suggestion you could get inspiration of: [SaveDuringPlay] from cinemachine (https://twitter.com/CiroContns/status/889855667376467973).

    Really like where this asset is going. You already implemented most of the custompropertydrawers I came up with over time :D
     
    Last edited: Jul 27, 2017
  11. Dennin-Dalke

    Dennin-Dalke

    Joined:
    Aug 10, 2014
    Posts:
    25
    Would be possible to filter the values being presented on the new item dropdown menu? For example, if I have an array of interfaces, and I don't wanna allow all classes that implement this interface be added to this array, just some ones that match specific criteria, what is the best way to hide the ones that don't match the criteria?
     

    Attached Files:

  12. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    I'm having an issue with dictionary serialization.

    I have a class that derives from SerializedScriptableObject

    Code (CSharp):
    1. public abstract class HardwareTemplate : SerializedScriptableObject, IHardware
    2.  
    That class has a private field backing a property.
    Code (CSharp):
    1.  
    2.     [HideInInspector]
    3.     [InfoBox("$GetMissingProperties", InfoMessageType.Error, "MissingRequiredProperties")]
    4.     private Dictionary<string, PhysicalProperty> properties;
    5.     [ShowInInspector]
    6.     public Dictionary<string, PhysicalProperty> Properties
    7.     {
    8.         get
    9.         {
    10.             return this.properties;
    11.         }
    12.  
    13.         set
    14.         {
    15.             this.properties = value;
    16.         }
    17.     }
    18.  
    Now this used to be a List, and when it was it worked fine. But now that we changed it to a Dict, it isn't serializing properly. The value is lost every time you play or reload the editor or assemblies are reloaded, etc
     
  13. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    Hey there,

    I hit a serialization problem on iOS with Dictionaries. It is really just a simple Dictionary<string, int> and works on windows and android, but not iOS. Here is the script:
    Code (CSharp):
    1. // --------------------------------------------------------------------------------------------------------------------
    2. // <copyright file="AnimatorToggleInterface.cs" company="Supyrb">
    3. //   Copyright (c) 2017 Supyrb. All rights reserved.
    4. // </copyright>
    5. // <author>
    6. //   Johannes Deml
    7. //   send@johannesdeml.com
    8. // </author>
    9. // --------------------------------------------------------------------------------------------------------------------
    10.  
    11. using UnityEngine;
    12. using System.Collections.Generic;
    13. using Sirenix.OdinInspector;
    14. using UnityEngine.UI;
    15.  
    16. namespace Supyrb.Marbloid
    17. {
    18.     [RequireComponent(typeof(Animator))]
    19.     [RequireComponent(typeof(Toggle))]
    20.     public class AnimatorToggleInterface : SerializedMonoBehaviour
    21.     {
    22.         [SerializeField]
    23.         private Animator anim;
    24.  
    25.         [SerializeField]
    26.         private Toggle toggle;
    27.  
    28.         [SerializeField]
    29.         private string targetParameter = string.Empty;
    30.  
    31.         [SerializeField, ReadOnly]
    32.         private Dictionary<string, int> hashLookup;
    33.  
    34.         private bool initialized;
    35.         private int targetHash;
    36.         private bool isOn;
    37.  
    38.         void OnEnable()
    39.         {
    40.             if (!initialized)
    41.             {
    42.                 Initialize();
    43.             }
    44.  
    45.             isOn = toggle.isOn;
    46.             if (anim.GetBool(targetHash) != isOn)
    47.             {
    48.                 SetBool(isOn);
    49.             }
    50.         }
    51.  
    52.         private void Initialize()
    53.         {
    54.             if (string.IsNullOrEmpty(targetParameter))
    55.             {
    56.                 Debug.LogError("Missing target parameter", this);
    57.                 return;
    58.             }
    59.             SetTargetParameter(targetParameter);
    60.             toggle.onValueChanged.AddListener(SetBool);
    61.             initialized = true;
    62.         }
    63.  
    64.         private void SetTargetParameter(string parameter)
    65.         {
    66.             if (!hashLookup.ContainsKey(parameter))
    67.             {
    68.                 Debug.LogError("Missing target hash for parameter " + parameter, this);
    69.                 return;
    70.             }
    71.             targetHash = hashLookup[parameter];
    72.         }
    73.  
    74.         public void SetBool(bool value)
    75.         {
    76.             if (!initialized)
    77.             {
    78.                 Initialize();
    79.             }
    80.             anim.SetBool(targetHash, value);
    81.             isOn = value;
    82.         }
    83.  
    84. #if UNITY_EDITOR
    85.  
    86.         [Button]
    87.         private void UpdateSupportedTriggers()
    88.         {
    89.             var animController = anim.runtimeAnimatorController as UnityEditor.Animations.AnimatorController;
    90.             if (animController == null)
    91.             {
    92.                 Debug.LogError("Missing animation controller", this);
    93.                 return;
    94.             }
    95.             var animParameters = animController.parameters;
    96.             hashLookup = new Dictionary<string, int>();
    97.  
    98.             for (int i = 0; i < animParameters.Length; i++)
    99.             {
    100.                 var parameter = animParameters[i];
    101.                 hashLookup[parameter.name] = parameter.nameHash;
    102.             }
    103.         }
    104.  
    105.         void Reset()
    106.         {
    107.             anim = GetComponent<Animator>();
    108.             toggle = GetComponent<Toggle>();
    109.             UpdateSupportedTriggers();
    110.         }
    111. #endif
    112.     }
    113. }
    Here is how it looks in the inspector:
    upload_2017-7-28_10-5-22.png

    And this is the error when the object is enabled:
    Code (CSharp):
    1. Sirenix.Serialization.BinaryDataReader.PeekEntry (System.String& name)
    2. Sirenix.Serialization.BinaryDataReader.ReadToNextEntry ()
    3. Sirenix.Serialization.BaseDataReader.SkipEntry ()
    4. Sirenix.Serialization.BinaryDataReader.ExitNode ()
    5. Sirenix.Serialization.BaseDataReader.SkipEntry ()
    6. Sirenix.Serialization.BinaryDataReader.ExitNode ()
    7. Sirenix.Serialization.BaseDataReader.SkipEntry ()
    8. Sirenix.Serialization.BaseDataReader.SkipEntry ()
    9. Sirenix.Serialization.BinaryDataReader.ExitNode ()
    10. Sirenix.Serialization.ComplexTypeSerializer`1[T].ReadValue (IDataReader reader)
    11. Sirenix.Serialization.UnitySerializationUtility.DeserializeUnityObject (UnityEngine.Object unityObject, IDataReader reader)
    12. Sirenix.Serialization.UnitySerializationUtility.DeserializeUnityObject (UnityEngine.Object unityObject, System.Byte[]& bytes, System.Collections.Generic.List`1& referencedUnityObjects, DataFormat format, Sirenix.Serialization.DeserializationContext context)
    13. Sirenix.Serialization.UnitySerializationUtility.DeserializeUnityObject (UnityEngine.Object unityObject, SerializationData& data, Sirenix.Serialization.DeserializationContext context, Boolean isPrefabData)
    14. Sirenix.OdinInspector.SerializedMonoBehaviour.UnityEngine.ISerializationCallbackReceiver.OnAfterDeserialize ()
    15. Sirenix.Serialization.UnitySerializationUtility:DeserializeUnityObject(Object, IDataReader)
    16. Sirenix.Serialization.UnitySerializationUtility:DeserializeUnityObject(Object, Byte[]&, List`1&, DataFormat, DeserializationContext)
    17. Sirenix.Serialization.UnitySerializationUtility:DeserializeUnityObject(Object, SerializationData&, DeserializationContext, Boolean)
    18. Sirenix.OdinInspector.SerializedMonoBehaviour:UnityEngine.ISerializationCallbackReceiver.OnAfterDeserialize()
    Any ideas where the problem comes from?
     
  14. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    That would be because your dictionary is currently only being shown, not serialized. [ShowInInspector] just causes something to be shown, it will not necessarily be saved. And private variables are never saved "by default" - you need to mark your private "properties" field with the [SerializeField] attribute, and then the dictionary will be saved and persisted. Odin will by default serialize public fields or fields marked by [SerializeField] that Unity wouldn't serialize, and always serializes fields/auto-properties marked with [OdinSerialize] regardless of whether Unity would serialize them or not.

    Huh. That's... not a lot to go on. I can see that something went wrong somewhere, as it's skipping data entries in a fairly deep node hierarchy (given the level of recursion), which means that data is being "lost" (IE skipped over). It's skipping starting from an ExitNode call in ComplexTypeSerializer, meaning that a type formatter didn't manage to read all (or any) of its node data, so the rest is being skipped while reading to the node's exit point. This happens two more times down the call stack, indicating that at least three type formatters are failing to read all of their node data before ExitNode is called, which is a little unusual.

    However it actually doesn't fail until a point deep down in the skipping, during actual binary data parsing, in BinaryDataReader.PeekEntry, which has very few ways to fail, generally indicating a corrupt data stream with invalid binary formatting, which is odd, and seems almost unrelated. I think we need more data than this.

    Is that all there is to the error message? No exception message or log, line numbers, or anything else you can add that might help us pinpoint what the issue is more exactly? If not, then I'll cook up a build that does a bunch of extra message logging during deserialization, and dumps the entire deserialization data stream into the log as a base64 string if anything fails. If you'd run that and send me the entire log from the iOS build, it would definitely help diagnose the issue.

    On another note, we're currently trying to get our hands on the proper hardware to run iOS build tests, as we've heard of a few dictionary-related issues on iOS. We're not really Apple people here at Sirenix, so we haven't got any Mac or iOS hardware of our own. Once the first paypal payments from Odin sales finally come through soon (we haven't actually received a cent yet!), we'll buy the hardware and get right on putting that platform through its paces.
     
    Last edited: Jul 28, 2017
  15. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    Thanks for the answer and taking a look at it. I was kind of irritated as well when I got the error. I got the message from Performance reporting. I also set the script call optimization to "Fast but no Exceptions" so I'm actually really impressed how much Performance Reporting was able to catch from that error ;)

    Sadly I don't really have the time right now, but I'll start a development build on the weekend and try to get you a better error message.
     
  16. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    It's not something that you can do out of the box right I'm afraid. But we are actively working on a new instance resolver which will have support for lots customizations like these.

    From our roadmap text posted on the previous page.
    For now though, you could provide the user with an error message saying if they've provided an invalid action.

    Code (CSharp):
    1.  
    2.     [SerializeField, ValidateInput("IsValidAction")]
    3.     private ISomeActionA someObject;
    4.  
    5.     [SerializeField, ValidateInput("IsValidAction")]
    6.     private ISomeActionA[] someObjects;
    7.  
    8.     private static bool IsValidAction(ISomeActionA value, ref string errorMessage)
    9.     {
    10.         if (criteria)
    11.         {
    12.             errorMessage = value.GetType().GetNiceName() + " Is not a valid action.";
    13.             return false;
    14.         }
    15.  
    16.         return true;
    17.     }
     
  17. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    The [HideInInspector] attribute states "Makes a variable not show up in the inspector but be serialized". I previously had [SerializeField] on their, but removed it because it appeared to be breaking the inspector, and since [HideInInspector ] said it would still serialize it, I removed it then the inspector started working.

    Without [SerializeField]


    With [SerializeField]


    So if I don't use that attribute, the inspector works but won't serialize, and if I do use it the inspector wont' work, but presumably it serializes (I didn't verify), so yeah bit of a pickle there :)
     
  18. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    The issue you're having there with the reference should be fixed in the latest version of Odin. If you don't want to upgrade for some reason, you can put a [PropertyOrder(int.MaxValue)] on your private field, which will cause it to be last in the order, and thus not referenced by other attributes. As well as the [SerializeField], of course, which has to be there for it to be saved.

    Edit: I should also say, Unity's docs are misguiding; [HideInInspector] doesn't cause anything to be serialized, it just hides stuff, without having any effect on serialization at all. I think that's what the docs mean - that it can still be serialized, despite not being shown.

     
    Last edited: Jul 28, 2017
  19. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    Ahh I didn't realize there was an update, so let me grab that.
     
  20. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Just a small request that OnValueChanged be able to trigger parameterless Events/UnityEvents directly. It's a minor annoyance given that you can just toss in a one-line function to trigger the event that way, so up to you guys, but given the context it seems like something that might be nice as standard functionality.

    Cheers!
     
  21. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Cool! Added it to our list of suggestions.
     
  22. rerwandi

    rerwandi

    Joined:
    Dec 8, 2014
    Posts:
    544
    I never messing around with custom inspector before.
    And here I'm having fun with Odin :D
     
    recon0303 and bjarkeck like this.
  23. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Niice, that's so cool. Thanks for sharing!
     
  24. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Another request: an alternative/update to the SpaceAttribute that works for properties and not just fields. I just found myself in a situation where I had to ShowInInspector 10 or so getter-only properties in a row, and the lack of Space support is painful.

    Also, Space doesn't seem to work properly for BoxGroups and Foldouts- it places the space inside of the box area, instead of above the area label as would be expected. I've just been going over the recent changes, and I think I can abuse the VerticalGroup's margin ability to give myself that extra space, but it feels a bit hack-ish (assuming it would work at all).

    Cheers!

    Edit: I guess margins haven't been worked into the VerticalGroupAttribute yet (not surprising since it's brand-new). Not a big deal, as I wasn't really comfortable using a one-item "group" to abuse the margin functionality anyhow.
     
    Last edited: Jul 30, 2017
  25. SuneT

    SuneT

    Joined:
    Feb 29, 2016
    Posts:
    41
    The latest version of Odin is out, and here's the changelog :)

    Odin Inspector & Serializer v. 1.0.4.0 + 1.0.4.1 changelog
    Upgrade notes for 1.0.4.0:

    • Delete the Sirenix/Demos folder before upgrading, as we have deleted a demo script that is now obsolete.
    Highlights:
    • Added a new attribute called InlineProperty which can be applied to classes, structs and members. Instead of drawing the value in a foldout, Odin will draw the label to the left and its inner content to the right.
    • Added a new attribute Called LabelWidth which changes the width of the label.
    • Deprecated the ShowForPrefabOnly and EnableForPrefabOnly attributes and added 8 new prefab related attributes which gives you much more control.
      • Added a new attribute called DisableInPrefabs which disables a property if it is drawn from a prefab asset or a prefab instance.
      • Added a new attribute called HideInPrefabs which hides a property if it is drawn from a prefab instance or a prefab asset.
      • Added a new attribute called HideInPrefabInstances which hides a property if it is drawn from a prefab instance.
      • Added a new attribute called HideInPrefabAssets which hides a property if it is drawn from a prefab asset.
      • Added a new attribute called HideInNonPrefabs which hides a property if it is drawn from a non prefab instance or asset.
      • Added a new attribute called DisableInPrefabInstances which disables a property if it is drawn from a prefab instance.
      • Added a new attribute called DisableInPrefabAssets which disables a property if it is drawn from a prefab asset.
      • Added a new attribute called DisableInNonPrefabs which disables a property if it is drawn from a non-prefab asset or instance.
    • Added a new attribute called HideInInlineEditors which hides a property if it is drawn within an InlineEditorAttribute.

    • Completely revamped the [ValidateInput] attribute. It can now detect changes to child values, as well as customize the error message and type - see the updated demo example. Additionally, it now also runs validation when an undo or redo is performed. Finally, support for preventing/rejecting invalid value changes has been removed from [ValidateInput], as it did not work correctly before in many cases, and proved infeasible to implement properly.

    • Upgrades to HorizontalGroup
      • Added PaddinLeft, PaddingRight, MinWidth and MaxWidth properties.
      • All properties now supports percentage. This includes Width, Min and Max Width, Padding and Margin. Values between 0 and 1 will be treated as percentage, 0 = ignored, otherwise pixels.
      • All properties are no longer private set.
      • Fixed an issue where horizontal groups start out too wide and slowly becomes the right size over time as the inspector window updates.
      • HorizontalGroup no longer sets the label width based on the width of the columns. This means that you can now do stuff like this: (and still maintain a proper label width which you couldn't do before).

    Fixes:
    • Fixed bug where Color and Color32 couldn't be properly copy pasted.
    • Unity's own context menu for Color and Color32 fields no longer shows up when the field is right-clicked.
    • Fixed issue where references to members with [HideInInspector] on them would not be drawn when the reference is expanded.
    • Fixed case where strings and boxed enums that were exact the same object instance would count as references to each other in the inspector.
    • Fixed rare error where changing a value's type between two different strongly typed list types would cause internal errors in the property system when updating a property's chlidren.
    • Fixed error where [ValueDropdown] wouldn't work properly with polymorphism.
    • Fixed error where [ValueDropdown] wouldn't work on list elements.
    • Fixed various errors with flag enums causing invalid cast exceptions. These should no longer happen, regardless of the enum's values and underlying type.
    • Fixed issue in DeepReflection with invalid IL code being emitted for invoking methods on value type instances. This was causing various attributes such as ShowIf and HideIf to throw exceptions when used in structs and referring to property and method members.
    • Fixed case where dictionaries that were referenced polymorphically would cause the inspector to break.
    • Fixed a lot of GUI Spaceing issues.
    • InfoBoxes now also work on methods.
    • Fixed issue, where int fields couldn't be changed with sliding, if mouse movement was too slow.
    • Fixed possible compiler error when compiling to iOS on xcode.
    • Fixed invalid generic argument exceptions being caused by enum serialization on the new .NET 4.6 scripting backend.
    • Fixed bug on the new 4.6 experimental scripting runtime where emitting a serialization formatter for an empty type with no values to serialize would cause Unity to crash, due to incorrect parsing of the emitted empty IL code.
    • Fixed compiler errors in new group system source code when using Unity's old compiler.
    • Fixed rare case where a missing editor type could cause compiler errors when generating editors.
    • MemberFinder now correctly includes the requested method parameters in the error message when it fails to find a member.
    • 1.0.4.1: Fixed case where AssemblyUtilities might throw an ArgumentOutOfRangeException when processing assemblies that are not located anywhere within the project folder.
    • 1.0.4.1: Fixed an issue where interacting with an object field in a box group, would cause all box backgrounds change color slightly.
    • 1.0.4.1: Button size now works with ButtonGroups.
    • 1.0.4.1: Fixed an issue where the hover effect for foldouts was delayed.

    Changes:
    • Lowered most error logs in DelegateFormatter to warning level, as many of them can occur in perfectly legitimate situations.
    • All members marked with [HideInInspector] now have a default property order value of int.MaxValue, by default placing them last in the order of properties. This ensures that it is only in very rare cases that there will be references to members that are hidden.
    • Enum flag names in the flag dropdown now have nicified names.
    • OdinEditorWindow now implements a scroll-view, and ensure the window is repainted when needed. A bit of padding around the drawn content has also been added.
    • DefaultSerializationBinder now also tries to resolve type names using AssemblyUtilities.GetType.
    • Slightly altered the sensitivity of field sliding to be a bit higher around a value of 0.
    • Updated Demo Scene and Documentation.
    • Improved error messages received when incorrectly applying the [ShowOdinSerializedPropertiesInInspector] attribute to classes that are not specially serialized and do not support special prefab serialization.
    • Property labels in tab groups now align with labels outside of tab groups.
    • Changed InfoBoxAttributeDrawer's priority to (0, 10001, 0)
    • Changed SpaceAttributeDrawer's priority to (1.1, 0, 0)
    Additions:
    • Added a very rudimentary Delegate drawer.
    • Odin now correctly guesses that fixed size buffers are serialized by Unity in version 2017.1 and up.
    • Added RectExtensions.ExpandTo.
    • Added new constructor to [OnInspectorGUI] that lets you specify two methods to invoke: a method to prepend, and a method to append.
    • Added support to Odin for drawing types derived from NetworkBehaviour, and added the SerializedNetworkBehaviour type. Note that [SyncVar] and SyncLists still have the exact same limitations as before, regardless of whether Odin is serializing the type or not.
    • The HideIf and Showif attributes now have an options to disable the fade in and fade out animations.
    • The DictionaryDrawerSettings attribute now has an option to marke a dictionary ReadOnly as well as changing the labels for the Key and Value columns.
    • Added a TextureUtilities class containing static helper functions.
    • Added support for injecting GUI before and after each element is drawin inside a list. This is achived using the [ListDrawerSettingsAttribute(OnBeginListElementGUI = "OnBeginListElementGUIMethodName", OnEndListElementGUI = "OnEndListElementGUIMethodName")].
    • Added support to the ListDrawerSettings attribute for defining custom label text for each list element. This is achived using the [ListDrawerSettingsAttribute(ListElementLabelName = "ListElementLabelName")].
    • Added PropertyTree.OnUndoRedoPerformed
    • 1.0.4.1: You can now override the number of items per page for lists via the ListDrawerSettings attribute.
    • 1.0.4.1: The TabGroup attribute now also supports naming tabs dynamically using the $ sign.

    • 1.0.4.1: Added a very rudimentary VerticalGroup attribute. This is particularly usefull if you want to make columns using HorizontalGroups.

      We'll be introducing more features to this attribute in the future.

    Check out Odin Inspector & Serializer on the Asset Store!
     
    Last edited: Jul 30, 2017
  26. Dennin-Dalke

    Dennin-Dalke

    Joined:
    Aug 10, 2014
    Posts:
    25
    This sounds amazing!!! Being able to customize the Constructors and from where we'll collect new instances sounds incredible, looking forward to it!
     
  27. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Hiya guys! Another bug report here.

    If you write a new value drawer for a type, and override equals or overload the == operator for that type in such a way that two different objects can return equal, it potentially causes error-less cascading layout problems in the inspector for more than 1 property of that type in a window.

    For instance, I've made a LocalizedString class that holds an array of Language->String tuples internally, which I've made render (with a new Odin value drawer) as a typical string for the currently selected system language, using a near-copy of the StringDrawer in Odin. This works absolutely perfectly, until I override Equals to check for currently-selected-string equality, which returns true if both are empty as well. This is making it so that, somewhere in Odin (or maybe in Unity?), an equality check is occurring when drawing properties in the inspector that should be a ReferenceEquality check instead, and the second element to be rendered is half-confused into thinking it's the first element, and so on and so forth.

    Does that make sense? I can include code for reproduction if necessary. Here's a picture of what successful and failed drawing looks like, with the only difference being the Equals override.

    IMG_03082017_212732_0.png IMG_03082017_213024_0.png
     
  28. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Ah, yes, that actually makes perfect sense when I think about it. We keep track of all seen references internally using a dictionary, and that dictionary would use the object's own equality operator to compare, if they have the same hash code. I'm not at a computer right now, but I'll implement a fix later today when I can by making a custom equality comparer for that dictionary, which only checks reference equality (which is what we're doing everywhere else). If you'd like, I'll send you a build then.

    Another fix (that you could use now without waiting for me) would be for you to make your LocalizedString type atomic. Merely create a LocalizedStringAtomHandler, mark it with the [AtomHandler] attribute, and derive it from BaseAtomHandler<LocalizedString>. Implement all abstract members, and you're good to go.

    This would make Odin treat your LocalizedStrings as if they were atomic, single values - like strings, or floats. It will have no child properties, and will for most intents and purposes be treated like a primitive value (no references possible). We use this to enforce certain kinds of behavior for, for example, gradients - which had been acting weird before we enforced the atomicity concept on them. You can see the sources of GradientAtomHandler and AnimationCurveAtomHandler as examples.

    Oh, and you'd also need to mark the assembly as containing atom handlers - you'd just have to write [assembly: AtomContainer] next to your handler class.

    Edit: So I managed to scrape together a moment to fix the error. It turns out that this was also happening in the reference tracking in the serialization system, so even after the atom fix I suggested, you will probably see weird behaviour, as after deserialization, the values will all have become legitimately the same reference. That is, they will, if it was Odin serializing the LocalizedString values.

    I had to run out the door before I could make a build and send it off, but I'll make sure to send you the fix asap.
     
    Last edited: Aug 4, 2017
    DonLoquacious likes this.
  29. c-Row

    c-Row

    Joined:
    Nov 10, 2009
    Posts:
    853
    Is it possible to serialize a two-dimensional array with Odin? (though from what I have seen so far it's probably more a question of 'how' :) )
     
  30. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    We don't currently support showing two-dimensional arrays in the inspector (though, as always, there are ways, albeit with certain limitations - essentially, prefab modifications won't work), but Odin will serialize arrays of any dimension without blinking.

    I am currently on a phone, but I can give you some example code later, if you want.

    And of course, Odin will eventually have full multi dimensional array support out of the box.
     
    DonLoquacious and c-Row like this.
  31. SuneT

    SuneT

    Joined:
    Feb 29, 2016
    Posts:
    41
    Here's a GIF of the latest work on displaying lists and two-dimensional arrays as tables in Odin.

     
  32. c-Row

    c-Row

    Joined:
    Nov 10, 2009
    Posts:
    853
    Just what I was hoping for - looks promising!
     
  33. Bulwark-Studios

    Bulwark-Studios

    Joined:
    Sep 20, 2013
    Posts:
    18
    Hi !
    We do a lot of refactor at the studio and we know that serializers don't like refactors :)

    Is there a way to copy / paste values or change the class type in the inspector and keep the previous values if the properties have the same name ? (Check the attachment to view an exemple)

    Thanks!
     

    Attached Files:

  34. bradbecker

    bradbecker

    Joined:
    Dec 26, 2014
    Posts:
    130
    The tables looks great. What's the ETA?
     
  35. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Currently there is no "easy" way to rename a type serialized by Odin. In many cases, Odin will take care of things and make sure to retain all of your data. However, in some cases (such as when polymorphism is in effect, like it is in your example case), it is not possible for Odin to reconstruct your data perfectly, and some things will be lost.

    I'll quote from a former post of mine in this thread, which explains what Odin does when types go missing or change their names:

    Now, there are ways to do renaming - they're just a little tricky. If you're using forced text serialization in the Unity editor (which you should, as a general rule), all of Odin's serialized data will be stored as node lists in the text asset files. The serialized data will look something like this, in the file:

    As you can see, the type names are stored as plain text. It would be possible for you to find all assets where your type is being used, and using a text editing program like notepad++, replace all instances of your old type name with your new type name. You can see how this might go wrong, of course - this would also replace it in saved strings, and so on, and there is some potential for asset file corruption. Done prudently, however, and with backups of your project in place (always use source control systems!), it is perfectly possible to rename even polymorphically serialized types without losing any data.

    We have a few ideas for type renaming utilities - editor windows that will essentially go through all serialized data and fix type strings, etc, and perhaps an overall better ability for the serialization system to guess at the new type name if a type has been renamed (this would be difficult to do reliably, but we do have a few ideas that could possibly pan out), however our plates are full enough as it is, so those features will remain on the backburner for now, unless we get a lot of requests for them. The editor window that fixes type name entries would be fairly easy to do, though... I'll add it to our todo list. Maybe it'll make it into a patch in the more immediate future, if it turns out to really be that easy.

    What you're seeing is just the rendering part of the table/2D array support; we also need to add support for 2D arrays to the backend property system. Table support for plain lists and arrays might be coming pretty quickly, possibly as soon as the next patch, but properly supporting 2D arrays in the inspector requires us to finish the property rebuild that we mention in our roadmap (said rebuild is slated for 1.1) - so that part might be a little while yet.
     
    Last edited: Aug 9, 2017
  36. Joshi

    Joshi

    Joined:
    Nov 12, 2014
    Posts:
    2
    When using .NET 4.6, I can't use SerializedMonoBehaviour. In the example scene "Odin Attributes Overview" all the Serialized MonoBehaviours cause lots of errors ( https://pastebin.com/nZ1e4GUL ). Any ideas?

    /e Also - but that's not .NET4.6 exclusive - I get an error when loading my project that 'Assembly-CSharp-firstpass' or one of its dependencies is missing.
    https://pastebin.com/s5zgHr9Y
     
    Last edited: Aug 10, 2017
  37. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    You should upgrade Odin - we fixed that error on .NET 4.6 a while back, in 1.0.2.1:

    Edit: And I believe the other error, with assembly name resolution failure, should also be fixed in the newer versions of Odin. If it's not, please post an issue with further details on our issue tracker, ideally along with a minimal project that reproduces the error, or otherwise some way of reproducing it when starting from a clean project. In that case, we'd get right on fixing it.
     
  38. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    @Tor-Vestergaard Any update on the serialization issue for value-equal objects? I did the atomic fix for display in the inspector and made it a concrete class so that it's Unity-serializable, just in case, but I'm a bit scared to go all-in on my database and fill it with a bunch of information, or go back to Odin serialization for it, if there's a chance some of it could get lost down the line.

    Cheers!
     
  39. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Oh, I seem to have forgotten to send the build when I got home, after all. I'm sorry - it was a very busy, fraught day. I implemented the fix a while ago, and in my head I thought I'd sent it to you. I'll make a build from our latest stable branch commit and send it to you right away.
     
  40. Joshi

    Joshi

    Joined:
    Nov 12, 2014
    Posts:
    2
    Thank you! I had problems with that particular purchase on the asset store and they revoked it (seems like I paid twice, got my moeny back once but do not own the asset any more). As a result I imported an old version of Odin into my project (but thought it was the most up2date since the assetstore didnt propose upgrading it). Got in contact with Unity, hope they can fix it.
     
  41. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Ah, that makes sense. I think I remember your review mentioning that, even. Hope it all works out for you!
     
  42. Bulwark-Studios

    Bulwark-Studios

    Joined:
    Sep 20, 2013
    Posts:
    18
    This workaround totally do the job for us thanks!
     
  43. bradbecker

    bradbecker

    Joined:
    Dec 26, 2014
    Posts:
    130
    Has anyone gotten Odin to work with Playmaker? They have customer inspector code so Odin doesn't override them to give reordering capabilities, etc.
     
  44. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Right now, Odin doesn't touch anything that already has a custom inspector defined. What you can do is try to define your own custom editor class for the PlayMaker type in question, and derive it from OdinEditor instead of Unity's Editor. You don't need to implement anything, just leave the class itself empty - that should already be enough.

    If Unity picks up that custom inspector over Playmaker's own (which it might not), then Odin should draw the component as it would any other. However, any custom stuff that Playmaker did in their editor will be gone, and it seems fairly likely that something in Playmaker might break if whatever custom stuff they do in their editors goes missing. You'll have to play around with it yourself, and see if there is a way to do it without breaking Playmaker.
     
  45. PIGSSS-GAMES

    PIGSSS-GAMES

    Joined:
    Jul 2, 2014
    Posts:
    8
    Is there any way to make Odin display list of each object as 'page'? I have to use Odin for setting list character information and each of info has pretty large info inside.
     
  46. HobieKenobi

    HobieKenobi

    Joined:
    Sep 12, 2014
    Posts:
    10
    Hello, I've been using Odin and I'm get nice results using monobehaviour, but now I'm changing some of these over to networkbehaviour and Odin stops displaying as it was. Am I possibly needing to change something for Odin to be ok with networkbehaviour, cheers Hobie
     
  47. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Hey there.

    Yup, you can do that via the ListDrawerSettings attribute:
    Code (CSharp):
    1. [ListDrawerSettings(NumberOfItemsPerPage = 1)]
    2. public SomeClass[] SomeArray;
    Another alternative is to populate the members of SomeClass with a FoldoutGroup (or ToggleGroup) and force them to be collapsed by default.

    Code (CSharp):
    1.  
    2. public class MyComponent : MonoBehaviour
    3. {
    4.     [SerializeField]
    5.     private SomeClass[] SomeArray;
    6. }
    7.  
    8. [Serializable]
    9. public class SomeClass
    10. {
    11.     [FoldoutGroup("$SomeTitle", expanded: false)]
    12.     public string SomeTitle;
    13.  
    14.     [FoldoutGroup("$SomeTitle")]
    15.     public string Test2;
    16.  
    17.     [FoldoutGroup("$SomeTitle")]
    18.     public string Test3;
    19. }
    20.  
    foldout.png

    Odin should have support for NetworkBehaviour as of version 1.0.4.0, and there are currently no known issues with it as far as I'm concerned? Could you provide a bit more information, perhaps you've encountered a bug, or maybe Odin is not drawing the type at all. You can find out if it is drawing editors for your component in the odin preference window.
     
    Last edited: Aug 15, 2017
  48. HobieKenobi

    HobieKenobi

    Joined:
    Sep 12, 2014
    Posts:
    10
    hehehe yes umm, I forgot to update, all good now thank you
     
    bjarkeck likes this.
  49. waheyluggage

    waheyluggage

    Joined:
    Aug 22, 2015
    Posts:
    15
    Hello

    This looks interesting, does it support cross referencing gameobjects across scenes? Like if I have 2 scene files loaded in Unity, then drag a GameObject from scene 1 onto a property for an object in scene 2, will that serialize correctly once my game loads up and I've loaded both scenes back in? Thanks!
     
  50. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    It does support setting references across scenes, but as for now they won't be serialized (Meaning that as soon as you close and open Unity the reference will be lost). I don't know if they plan to implement a serialization for that feature, I fear it would require to dig really deep into unity.