Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

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

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

  1. jorisshh

    jorisshh

    Joined:
    Oct 6, 2009
    Posts:
    1,522
    Odin puts your Unity workflow on steroids, making it easy to build powerful and advanced user-friendly editors for you and your entire team.

    Awesome gamedev teams using Odin to save time and unlock the full potential of Unity:





    We’ve enjoyed 96% 5-star ratings on the AssetStore (thanks for the amazing feedback!) and are only just getting started!






    What makes Odin so great, you ask?

    With an effortless integration that deploys perfectly into pre-existing workflows, Odin allows you to serialize anything and enjoy Unity with 50+ new inspector attributes, no boilerplate code and so much more!

    First of all, Odin is super extendable, easily integratable and includes full source code access. And that’s just for starters.

    Read on below to find out how Odin deals with Serialization, Input Validation, Value Dropdowns, Powerful Lists, Customizable Layouts, Color Palettes, Asset Lists and more!

    We’re on a mission to build the best inspector and serializer ever for Unity, and that includes constant feature updates and patches, so don’t hold back with the suggestions!

    Odin Inspector & Serializer has been developed by the fantastic team at Sirenix, and published by Devdog.





    Serialize Anything!



    We’ve built a custom serialization protocol allowing you to either inherit from one of our SerializedBehaviour, SerializedScriptableObject etc. or add a few lines of code to your existing class, and everything serializable shall be serialized.

    Oh, and in case you were wondering, it has:
    • Full support for polymorphism, cyclic references and null values
    • Full prefab support
    • Serialize delegates
    • Build in Binary and Json Formatters
    • High performance and close to zero garbage generation
    • Follows all Microsoft's .NET serialization standards
    • Customize serialization by creating your own type formatters and data formats
    • Supports external references to objects such as Unity objects and assets.
    • Mergeable via source control

    Dictionaries



    Dictionaries can now finally be drawn and edited directly in the inspector.

    Note that while the serializer can serialize all dictionaries, the inspector is limited to only draw dictionaries with a primitive key type such as string, int, byte etc. The Value type can be anything you want.
    • Support all value types
    • Fully serialized just by inheriting from SerializedMonoBehaviour

    Input Validation



    Odin lets you easily write custom validation logic to ensure and give sensible feedback for invalid input.
    • Write custom input validation logic
    • Prevent invalid values from being set
    • Give designers useful feedback in the inspector
    • Provide custom UI messages to properties
    • Use build in attributes such as SceneObjectsOnly and AssetsOnly

    ...and Scene Validation



    • Odin also provides a Scene Validation Window, which lets you scan entire scenes for errors, warnings, missing references etc.

    Powerful Lists



    All arrays and lists implementing Microsofts’ IList interface are drawn by our powerful list drawer.
    • Drag and drop, insert and delete individual items
    • Cross-list and even cross-window item dragging
    • Paging for lists with many items
    • Nested list drawing
    • Fully customizable through settings and / or using attributes.

    Inline Editor





    Member References




    Customizable Layouts





    Arranging, labeling, hiding and grouping your properties helps the user to faster find what they are looking for.
    • Group properties into Tab Groups, Foldout Groups, Toggle Groups, Horizontal Groups, Button Groups and more
    • Hide or show properties based on your custom conditions
    • Custom property orderering
    • Mark properties readonly or disabled
    • Subscribe to on value changed
    • Hide or rename labels entirely
    • Display custom messages based on values
    • Easily draw custom GUI before or after properties

    And much much more!...

    Color Palettes



    Define project-based color palettes for your hole team to share. Color properties with a ColorPalette attribute will then show a color palette right next to the color picker in one line.
    • Works with all Unity's Color types
    • Palettes are stored as an asset, making the entire team share the same color palettes
    • Easily create new custom color palettes
    Value Dropdowns

    One way of preventing invalid values is by providing the user with a list of valid values to choose from.

    With Odin, the ease of dropdown selection is no longer limited to just enums. Simply reference a list and it will now give you a dropdown with values to choose from.
    • Create dropdowns for any type
    • Works with static and instance methods, fields and properties
    • Specify name for items that does not implement a proper ToString()



    Asset Lists



    Finding the right assets can sometimes be a hurdle in big projects with lots of files. The AssetList attribute replaces the default list drawer, with a toggle-list of all relevant assets relevant to your use case. The user can then toggle relevant assets in and out of your list.
    • Only shows assets assignable to your list type
    • Works on all asset types, including components within assets
    • Auto populate mode that automatically includes all relevant assets
    • Easily create new assets that meets your constraints
    • Filter the asset list with filters such as, asset folder location, gameobject tags, gameobject layers, name prefixes etc..
    But most importantly! Odin is fully customizable

    We make it easy to configure how you want the inspector to act in your project, and trivial to create new configurations for your own custom drawer types.
    • Customize the inspector to suit your needs and preferences
    • Create integrated configurations for your own drawers





    To-Do List for Odin:
    • Save play mode changes
    • Delegate Drawer for any delegate type
    • Adding a static inspector
    • Object snapshots / restore feature allowing you to restore an object to previously saved states



    - Your friends at Devdog and Sirenix.
     
    Last edited: Jun 15, 2017
  2. SuneT

    SuneT

    Joined:
    Feb 29, 2016
    Posts:
    41
    Are you going to Unite in Amsterdam next week? How about meeting up with Sirenix - the developers of Odin?

    Let us know if you'll be there - it'd be great to have a chat :)
     
  3. SuneT

    SuneT

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

    Also, seeing as Odin has now become more consolidated (500+ users already), the current $35 launch price is going to increase at some point during the next months.

    Odin Inspector & Serializer v. 1.0.3.0 changelog
    Highlights:

    • Property groups can now be placed within other groups in the same type, using group paths. Additionally, the same member can now be placed in multiple different groups at the same time. See the manual section "Grouping" on the "Design with Attributes" page for more information.

    • Added support for using Enum and Guid keys in dictionaries.

    Fixes:
    • Odin's editor compilation now fully validates the type names of all types that it compiles editors for so that editors are not generated for types with names containing invalid characters like spaces, ampersands, and tilde, or with names that are reserved keywords. Editor compilation errors should no longer occur because of invalid type names. The logic implements the official C# language specification for valid identifiers.
    • Fixed an issue where fields would become bigger than the Inspector width.
    • Fixed an issue where Odin serialized enums could not be changed in instances of prefabs where the property path did not exist on the original prefab asset.
    • Fixed an issue where Odin-serialized LayerMasks could not be changed in the inspector.
    • Unity CustomPropertyDrawers which draw abstract UnityEngine.Object types are now properly detected and used by Odin without error messages.
    • Fixed an issue with MathUtilities.Wrap returning negative values when it shouldn't.
    • Fixed an issue where setting an angle to 360 in the SirenixEditorFields.AngleAxisField would result in a broken quaternion value.
    • Fixed an issue where char properties marked with [Delayed] weren't actually delayed.
    • Moved StringMemberHelperExample script to the demo namespace.
    • Delayed sliding handles now works for all primitive fields, except ulong and decimal.
    • Fixed an issue where UnityEvents drawn in InlineEditors would cause null reference exceptions to be thrown when the InlineEditor has been expanded for the second time. This was caused by internal Unity state that was wrongly cached. Odin now clears this invalid internal state at the correct times.
    • Fixed cases where AnimationCurves and Gradients would act up when they were being serialized by Odin, especially when they were being serialized by Odin on prefab instances (in which case, they wouldn't work at all). To facilitate this fix, the concept of enforced (and extendable) type atomicity has been introduced to Odin's property system - AnimationCurves and Gradients are now marked as atomic, and largely act like primitive types in terms of the property system.
    • Fixed case where Gradient.mode would not be serialized in newer versions of Unity where it exists.
    • Fixed an issue where cross-window dragging of list elements would not work in most cases.
    • SpaceAttribute is no longer applied to collection elements.
    • Fixed an issue where, if you had an interface type field or property, the instance creator window would not find structs that implemented that interface.
    • Fixed an issue where dictionary, hashset, list, nullable and generic collection serialization formatters would be broken on IL2CPP builds due to incorrect stripping of their constructors. This would cause a multitude of deserialization errors when running builds on IL2CPP platforms.
    • Fixed an issue where the path for AssetLists would not correctly work with root folders named Assets.
    • Fixed a layout issue caused by InlineButton.
    • Fixed case where serialization for a type would break when a serialized member is hidden by another serialized member in a base type with the same name.

    Changes:
    • Added the missing optional order parameter to HorizontalGroup
    • HorizontalGroupAttribute now has an option for a title.
    • Preferences in Window > Odin Inspector > Preferences > Drawers > General are now stored as EditorPrefs, and are therefore local to the computer.
    • Preferences in Window > Odin Inspector > Preferences > Drawers > Color Palettes are now stored as EditorPrefs, and are therefore local to the computer. This only applies to display preferences - Color Palettes themselves are still saved to the asset and synced over source control.
    • SirenixEditorGUI.BeginBox now alternates between a light and a dark box background based on its z-index.
    • Renamed SirenixEditorGUI.VerticalLineSeperator to SirenixEditorGUI.VerticalLineSeparator and HorizontalLineSeperator to HorizontalLineSeparator.
    • The Serialization Warning message for SerializedMonoBehaviour is no longer shown in the demo examples. Hopefully, this helps new users to not accidentally click the button without reading the warning first.
    • All number fields now show a small handle, which enables you to change the value by dragging. This is useful when the number field does not have a label which also has this behavior, such as in the case of list elements.

    • Vector fields now hides the X, Y, Z and W labels in narrow spaces in order to better show the numbers

      This feature can be disabled in the Odin Preferences Window.
    Additions:
    • Added support for using Enum and Guid keys in dictionaries.
    • Added the TitleGroup attribute which groups properties vertically together with a title, an optional subtitle, and an optional horizontal line.

      This can serve as an alternative to using the PropertyOrder attribute.
    • Added a very rudimentary System.Type drawer, so you can see and change type instances in the inspector.
    • Added SirenixEditorFields.LayerMaskField, which is now used to draw LayerMasks in the inspector.
    • The size of buttons can now be set via the Button attribute.

    • Added PropertyChildren.Recurse, which returns an IEnumerable that recursively yields all children of a property depth first.
    • Added an option to hide the add button for lists in the inspector via ListDrawerSettings attribute. This can be used to create custom add buttons for lists.
    • Added the DictionaryDrawerSettings attribute, which enables you to change the display mode of the dictionary. More options will be added to this attribute in the future.
    • Added SplitVertical to RectExtensions.
    • Added property drawer for char properties.
    • Added support for using Enum and Guid keys in dictionaries.
    • Added inspector drawer for guids, and added SirenixEditorFields.GuidField. Additionally, SirenixEditorGUI.DynamicPrimitiveField now supports Guids.
     
  4. f0ff886f

    f0ff886f

    Joined:
    Nov 1, 2015
    Posts:
    201
    First off, thank you for this asset, it really is cool, and I'm just starting to identify how to use it best.

    I was wondering if you could suggest how to make some edits to visually clean up this List<>:



    Ideally I'd like to hide the entire class inheritance as it makes other folks quite confused, and any way to clean up the list would be helpful.

    In reality I'm trying to store a list of objects that the user can enable/disable, and ordering is important for my game logic. I think using a List<> will give me issues when trying to modify values in each object later, also I really only one one unique object allowed (so one TriggerDecision, one TriggerAnimation, etc).

    Note, this flow I'm creating specifically to take advantage of list inspectors from Odin, otherwise I have a hardcoded solution that works OK for now. So if you think there is a better way of representing an ordered set of unique objects, I'm all ears :)
     
  5. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Heya, good to see someone using the new forum thread. :D

    I would start by getting rid of the polymorphic object picker that takes up a lot of space and confuses your designers. The object picker can be disabled by using the [HideReferenceObjectPicker] attribute.
    Then you could rename the label for the enabled boolean by using the [LabelText] attribute so they still know which type action it is.

    I would also use the [ToggleLeft] attribute which moves the label to the other side of the check box to make it look a bit more intuitive.

    You could also throw in a [HorizontalGroup] attribute on Enabled property. That way the TriggerAnimation class can put the WaitForAnimation property on the same line.

    Code (CSharp):
    1.  
    2. public class MyComponent : SerializedMonoBehaviour
    3. {
    4.     [SerializeField]
    5.     [HideReferenceObjectPicker]
    6.     private CoreActionSubAction[] actionsToTrigger;
    7.  
    8. }
    9.  
    10. public abstract class CoreActionSubAction
    11. {
    12.     [ToggleLeft, LabelText("$GetInspectorTitle")]
    13.     [HorizontalGroup]
    14.     public bool Enabled;
    15.  
    16.     private string GetInspectorTitle()
    17.     {
    18.         return this.GetType().GetNiceName().SplitPascalCase();
    19.     }
    20. }
    21.  
    22. public class TriggerAnimation : CoreActionSubAction
    23. {
    24.     [HorizontalGroup]
    25.     [ToggleLeft]
    26.     [EnableIf("Enabled")]
    27.     public bool WaitForAnimation;
    28. }
    2.png

    In order to ensure that list is only can contain one object of each type. You could remove the Existing Add button and add your own using the [ListDrawerSettings] attribute:

    Code (CSharp):
    1.  
    2.     [SerializeField]
    3.     [HideReferenceObjectPicker]
    4.     [ListDrawerSettings(HideAddButton = true, OnTitleBarGUI = "DrawNewAddButton")]
    5.     [DisableContextMenu(disableForMember: false, disableCollectionElements: true)] // Prevents people from right-clicking and changing the value to null or another type.
    6.     private CoreActionSubAction[] actionsToTrigger;
    7.  
    8.     private void DrawNewAddButton()
    9.     {
    10.         if (SirenixEditorGUI.ToolbarButton(EditorIcons.Plus))
    11.         {
    12.             GenericMenu menu = new GenericMenu();
    13.  
    14.             var existingTypes = this.actionsToTrigger
    15.                 .Where(x => x != null)
    16.                 .Select(x => x.GetType())
    17.                 .ToHashSet();
    18.  
    19.             var coreActionTypes = typeof(CoreActionSubAction).Assembly
    20.                 .GetTypes()
    21.                 .Where(x =>
    22.                     x.IsClass &&
    23.                     !x.IsAbstract &&
    24.                     !x.IsGenericTypeDefinition &&
    25.                     x.GetConstructor(Type.EmptyTypes) != null &&
    26.                     typeof(CoreActionSubAction).IsAssignableFrom(x));
    27.  
    28.             foreach (var type in coreActionTypes)
    29.             {
    30.                 if (existingTypes.Contains(type))
    31.                 {
    32.                     menu.AddDisabledItem(new GUIContent(type.GetNiceName().SplitPascalCase()));
    33.                 }
    34.                 else
    35.                 {
    36.                     var localType = type;
    37.                     menu.AddItem(new GUIContent(type.GetNiceName().SplitPascalCase()), false, () =>
    38.                     {
    39.                         this.actionsToTrigger = this.actionsToTrigger.Append((CoreActionSubAction)Activator.CreateInstance(localType)).ToArray();
    40.                     });
    41.                 }
    42.             }
    43.  
    44.             menu.ShowAsContext();
    45.         }
    46.     }
    3.png

    Do note, that this custom Add button won't work with multi-selection. If you need help with that be sure to let me know :)

    There could still be added duplicate types by the user, by for instance ctrl+dragging element instances in order to copy them. If you also want to catch those cases, you could either make a warning messages using the [ValidateInput] attribute. Or you could simply remove all duplicate entries whenever the list has changed:
    Code (CSharp):
    1.  
    2.     [OnValueChanged("RemoveDuplicatesByType", includeChildren: true)]
    3.     public CoreActionSubAction[] actionsToTrigger;
    4.  
    5.     private void RemoveDuplicatesByType()
    6.     {
    7.         if (this.actionsToTrigger == null) return;
    8.  
    9.         var newArr = this.actionsToTrigger
    10.             .Where(x => x != null)
    11.             .GroupBy(x => x.GetType())
    12.             .Select(x => x.First())
    13.             .ToArray();
    14.  
    15.         if (newArr.Length != this.actionsToTrigger.Length)
    16.         {
    17.             var typesRemoved = this.actionsToTrigger
    18.                 .Except(newArr)
    19.                 .Select(x => x == null ? "Null" : x.GetType().Name)
    20.                 .ToArray();
    21.  
    22.             Debug.LogWarning("The already contains the action: " + string.Join(", ", typesRemoved));
    23.             this.actionsToTrigger = newArr;
    24.         }
    25.     }
     
    Last edited: Jul 2, 2017
    f0ff886f likes this.
  6. f0ff886f

    f0ff886f

    Joined:
    Nov 1, 2015
    Posts:
    201
    Holy mother of perl, I was hoping for a small hint, and you gave me the entire solution! That's pretty amazing, thank you very very much. I didn't realize such results are possible with Odin... I need to inspire myself a bit more reading the documentation!

    Now, I have one more question :) Is it possible to extend the support for Dictionary's to OrderedDictionary's somehow? The actual drawer should be the exact same since the only logic is under the hood. Can I do it on my end lightly?

    Thank you again, that response alone was worth the price, let alone the asset!
     
    bjarkeck likes this.
  7. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    Hey Vostok,

    The problem isn't the drawer for OrderedDictionary, but rather, how the property and prefab modification systems should handle the type on the backend side of things. Every new "special" type needs custom support so that the property system understands how it works and how to interact with it.

    OrderedDictionary only implements IDictionary, while we currently only support the strongly typed IDictionary<TKey, Tvalue>. It will probably be a little while before we can do something about that. The thing is, currently it involves retouching hundreds of different places around the property and prefab modification handling code to add support for a new "special" property type.

    Dictionary support was a big priority, so we took the time to add that before we went out of beta, but we can't keep adding new ones on demand, because 1) it all becomes messier when we do, 2) it takes quite a while to do, and 3) if we miss just a few spots here and there, a lot of things might break. OrderedDictionary also strikes me as the kind of class that needs its very own custom support, rather than just generic dictionary support. Alas, it's not something you can extend yourself - yet. For now, I'm afraid you'll need to make do with regular dictionaries.

    We're currently contemplating an extensive rewrite of the property system's innards to allow for adding custom property support far easier, say through inheriting from an implementing class much like the drawers are extended currently, and then move our IList, IList<T> and IDictionary<TKey, TValue> support into that system. It's no small task, though - there's quite a lot to think about, as translating complex types like dictionaries into a coherent property and prefab modification system is not very straightforward already, and "standardizing" the process of adding such custom support may prove very tricky indeed.

    There's so much to do in terms of new Odin features, though, so we're not sure when exactly we will get around to this particular one - within the next 2-3 months, hopefully. Pre-emitted serialization formatters for AoT, the new installer, property context persistence (so you don't lose minor things like which foldouts are expanded between reloads and opening/closing the editor), dated object snapshots, UnityEvent-style general delegate drawer, and so on and so forth, all have their place too.
     
  8. Froghuto

    Froghuto

    Joined:
    Oct 4, 2012
    Posts:
    61
    I have the following setup:

    Code (CSharp):
    1. [SerializeField] private List<Settings> _settings;
    2.  
    3. [Serializable]
    4. private class Settings
    5. {
    6.     [Range(0,1)]
    7.     public float EndT;
    8.     public bool Enabled;
    9. }
    What I would like to be able to do is to have a [ValidateInput] check if the list items all have a bigger EndT value than the preivous one (so they are always "ordered").
    But if I put a [ValidateInput] on the _settings field it is not triggered when I change the child class EndT value. And if I put it on the EndT field I have no reference to the _settings in the validate input method.
    So is this possible in the current version? =)
     
  9. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    No, ValidateInput doesn't currently catch it when child values are changed - adding an option for that can be done, though. I'll make a note of it.

    Meanwhile, you can maybe work in a hacky version of it using [OnInspectorGUI]. Something like this, for example:

    Code (CSharp):
    1. [SerializeField]
    2. [OnInspectorGUI("SettingsMessageGUI", append: false)]
    3. private List<Settings> _settings;
    4.  
    5. #if UNITY_EDITOR
    6.  
    7. private string ValidateSettings()
    8. {
    9.     for (int i = 0; i < _settings.Count; i++)
    10.     {
    11.         if (_settings[i] == null)
    12.         {
    13.             return "EndT value of all settings must be in ascending order - violated at index " + i + " with value " + _settings[i].EndT;
    14.         }
    15.     }
    16.  
    17.     if (_settings.Count > 0)
    18.     {
    19.         float lastT = _settings[0].EndT;
    20.  
    21.         for (int i = 1; i < _settings.Count; i++)
    22.         {
    23.             if (_settings[i].EndT <= lastT)
    24.             {
    25.                 return "EndT value of all settings must be in ascending order - violated at index " + i + " with value " + _settings[i].EndT;
    26.             }
    27.  
    28.             lastT = _settings[i].EndT;
    29.         }
    30.     }
    31.  
    32.     return null;
    33. }
    34.  
    35. private void SettingsMessageGUI()
    36. {
    37.     string errorMessage = ValidateSettings();
    38.  
    39.     if (errorMessage != null)
    40.     {
    41.         // This error box will be registered in the scene validator, so it all works with that
    42.         Sirenix.Utilities.Editor.SirenixEditorGUI.ErrorMessageBox(errorMessage);
    43.     }
    44. }
    45.  
    46. #endif
    47.  
    48. [Serializable]
    49. private class Settings
    50. {
    51.     [Range(0, 1)]
    52.     public float EndT;
    53.     public bool Enabled;
    54. }
    It runs the validation every frame, which is a bit wasteful, but that shouldn't be a problem unless you start having thousands of items in the list.
     
    Froghuto likes this.
  10. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    Odin is crashing the editor on macOS. I have been fighting the last several days trying to figure out why Unity Editor was crashing on my Macbook Pro. It was just after a Collab pull so I was blaming Unity, but it turns out it is Odin. As soon as I remove the Odin directory from my assets, I am able to start up the editor fine. But with Odin in place, the editor crashes on startup 100% of the time.

    Have you seen this before? What information can I provide?
     
  11. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    This is news to us. We are aware of one crash bug, which occurs in the Unity 2017.1 beta with .NET 4.6 enabled, in cases where, for example, scripts move between assemblies (IE, from outside the plugin folder and into the plugin folder). The next time you open the project, you will receive some error messages about asset importers crashing. It's caused by our editor .dll compilation, but happens inside Unity's assembly metadata asset importer. We have submitted a bug report to Unity, and can't do much more than wait, in this case.

    If this doesn't sound like your issue, we'd like to know as much as possible of the following:
    • Which version of Unity are you on? (And if 2017.1, are you using the new experimental .NET backend?)
    • Which version of Odin are you using?
    • Which version of macOS are you running?
    • Does deleting the GeneratedOdinEditors.dll files in the Sirenix/Assemblies/Editor folder, and/or also deleting the Library folder (to force trigger a full asset reimport) have any effect on the issue?
    • Does this happen when you import Odin into an empty project, or does it only happen in your project?
    • If it only happens in your project, can you isolate which part of your project may be causing Odin to crash?
    • And finally, if you can isolate it, could you send a zip of the part of the project that causes the issue to tor@sirenix.net, to help in tracking down the issue?
     
  12. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    According to Unity Tech, it is being caused by a race condition in the editor upon startup.

    Unity 2017.1, but NOT the experimental backend
    Not sure how to tell which version of Odin I am using, but it should be relatively up to date
    macOS Sierra 10.12.5
    Deleting the Library, Temp, or ProjectSettings folders does nothing (It was the first thing Unity Tech had me try)
    Deleting the GeneratedOdinEditors.dll DOES fix the problem!

    I can email you an empty project with just Odin in it that will crash on startup if you want to reproduce the issue, but deleting the generated DLL in that will fix it so I'm not sure if that will still be helpful or not. If nothing else, it would let you see the crash. Let me know if you want me to send it.
     
  13. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    Huh, that's interesting. Yes, please do send it to me at tor@sirenix.net. I'll make sure to add it to our current bug report regarding .dll's causing crashes in 2017.1.
     
  14. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    Sent!
     
  15. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    Or rather, NOT sent because your mail service kicked it back because the ZIP file contains a DLL in it. Presumably your mail service is trying to virus scan it and rejecting it :p

    <tor@sirenix.net>: host 127.0.0.1[127.0.0.1] said: 554 5.7.0 Reject,
    id=62830-19 - BANNED: .exe,.dll,Sirenix.OdinInspector.Attributes.dll (in
    reply to end of DATA command)
     
    Last edited: Jul 7, 2017
    Tor-Vestergaard likes this.
  16. Glader

    Glader

    Joined:
    Aug 19, 2013
    Posts:
    455
    Hi, this looks like something Unity3D itself should have included first party. It's such a shame that it doesn't. I'm looking to stop maintenance on a very crappy library I wrote called GladBehaviour which allows for serializing interfaces to the inspector, one of the features of your library, but I have a few questions before I retire it and pick yours up.

    1. It's not 100% obvious if you allow for serializing references to other MonoBehaviours that implement a certain interface. It looks like you allow users to create instances of objects that implement it that are then managed by the MonoBehaviour but references are important to me.
    2. Can Odin detected implementations of interfaces inside all assemblies within the project? Most of what I work on is written externally and brought in assemblies so this is an important feature to me that I need some clarity on.
    3. A lot of the work I do is open source with emphasis on community recruitment and contribution. Is it possible to link to and distribute a compiled assembly of just the Metadata (attributes)? I realize as a developer selling your asset you don't want to have it all out there but a major part of Odin itself appears to be interpreting the metadata on fields and properties. So the question is it possible to compile the Attributes into their own assembly and still have the drawers/inspector function correctly? Secondly, will an assembly like this containing only the Attribute types be allowed in a public repository?
    4. I realize you face the same issues with Interface serialization as the developers of competing solutions and myself. An inheritance of a custom MonoBehaviour implementation must be made. I understand why but I have a question related to the 3rd question. If you plan to support opensource collaboration how can/should we go about externalizing the SerializedMonoBehaviour, or whatever you called it, into its own seperate assembly away from the core of Odin. I think allowing for the distribution of these small assemblies as depedencies can foster Odin's use in open source project while also protecting the core of what is offered because the metadata/attributes and the MonoBehaviour implementation alone doesn't do anything without the drawers/custominspectors.
    I realize these are a lot of questions for a potential sale, I hope they don't come off in the wrong way either. I fully think a solution like Odin should be acquired by Unity and implemented. That solves basically everything from my point of view and I think we both agree with default inspector serialization and functionality is terrible but thankfully extendable. I just have some rather specific opensource related needs and it's why I've depended on the shaky half-working GladBehaviour library for so long.
     
  17. Duffer123

    Duffer123

    Joined:
    May 24, 2015
    Posts:
    1,215
    @jorisshh ,

    Hi. I've emailed as well. Think I'm going to purchase this Asset for sure.

    One Attribute I thought would add real value would be a hybrid of the other Asset attributes. It would be great if you could have a AttributeList-eque attribute but just for when you are selecting a single Asset (ie. restricting you to only selecting that GO or prefab by scene or not scene and by path, tags, layers etc?). Along these lines?:-

    [AssetRestrictSelection(bla bla, "Path" whatevers)]
    Public Gameobject go;

    Could this be done?
     
  18. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    I'll see what I can do - hopefully Odin will prove satisfactory!

    We do allow this, yes. You won't be able to select your target UnityEngine.Object (MonoBehaviour) instance in the current type selector dropdown, but interfaces allow dragging of UnityEngine.Objects - and if a GameObject is dragged, we attempt to find components on that GameObject for the interface in question. So in terms of dragging, it works just like Unity's object fields normally do.

    We detect types from all assemblies loaded in the current AppDomain, so yes, types from assemblies in the project will show up in the type selector.

    This is already in place - we have a completely standalone .dll that contains only Odin's attributes and nothing else, with no dependencies on the rest of Odin, and as a rule we don't mind that this .dll in particular is distributed.

    So, this one is a little tricky.

    You can't really compile "optional" references to assemblies, so if you work in such a dependency into an assembly of your own, it's going to be a hard dependency, and things won't really work at all without Odin being present.

    The possible solution comes in the form of compiler define symbols. Odin's main assemblies declare their presence in a project by adding the ODIN_INSPECTOR symbol, so source code (not compiled assemblies) can query whether Odin is present, and if it is, use its functionality safely.

    So you could safely have a script like this:

    Code (CSharp):
    1. public class YourMonoBehaviour :
    2. #if ODIN_INSPECTOR
    3.    SerializedMonoBehaviour
    4. #else
    5.    MonoBehaviour
    6. #endif
    7. {
    8. }
    You could, of course, also just distribute two assemblies - one that works with Odin present, and one that works without. Regardless, you can still distribute the attributes .dll and "use" that part of Odin freely. They just won't do anything if Odin isn't present in the project.

    I understand completely, and I hope I answered your questions to your satisfaction.

    In defense of Unity, there are actually some fairly decent reasons for Unity's serialization to be as it is, I should note. I've spoken with these people, at length - they are not incompetent, and in fact strike me as highly talented programmers; they just don't have the same luxuries as we do, and work within a different set of limitations. For now they seem satisfied by letting third-party plugins provide this sort of capability, rather than having to deal with it on their own.

    I'll add a final note that may be relevant for you. We have received many queries about redistributing Odin as part of other products, to render inspectors and provide serialization functionality. So we're considering offering a different (more expensive) licensing deal, that would let people distribute locked-down versions of Odin alongside their own products, that will only work with their products and won't provide general-purpose inspector or serialization capabilities in the project as a whole. There are plenty of financial, legal and technological issues to resolve before it can be done, but this is a thing we're considering.
     
  19. JFernald

    JFernald

    Joined:
    May 11, 2015
    Posts:
    4
    Hello,

    I just recently purchased Odin and I had a few questions.

    Is there any way to extend the MinMax slider implementation to retrieve the min and max values from a function rather than hard coding it in the Attribute? In addition, is it possible to restrict the steps to whole numbers?

    My use case is rather simple, I've got an imported 2d asset with multiple frames of animation and we need to define keyframes to use. Right now we are setting 2 different int values (Keyframe Start, Keyframe End), and while it works being able to use the min/max slider to represent these key frame values it would give them a visual representation what the frames represent in the grand scope of the animation at a glance (I.E. is the keyframe named Idle at the start or end of the animation?)

    Example:

    Code (CSharp):
    1.  [MinMaxSlider("EditorGetMinSliderValue", EditorGetMaxSliderValue)]
    2. public Vector2 MinMaxValueSlider;
    3.  
    4. public int EditorGetMinSliderValue()
    5. {
    6.        return -10;
    7. }
    8.  
    9. public int EditorGetMaxSliderValue()
    10. {
    11.        return 10;
    12. }
    I was also wondering if you could explain how I might hide the item highlighted in red so that it is not shown as part of the inspector. I essentially want to see the min/max int values but not the classname.MinMax in the box above it.

    odinquestion.png

    Here is the code:

    Code (CSharp):
    1.     public class MinMax
    2.     {
    3.         public int minValue;
    4.         public int maxValue;
    5.     }
    6.     //public List<MinMax> LevelRange = new List<MinMax>();
    7.     [DictionaryDrawerSettings(DisplayMode = DictionaryDisplayOptions.ExpandedFoldout), HideReferenceObjectPicker]
    8.     public Dictionary<string, MinMax> LevelRange = new Dictionary<string, MinMax>();
     
    Last edited: Jul 8, 2017
    Menion-Leah likes this.
  20. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    We may choose to add this in the future - I'll add it to the todo. For now, you can use a basic custom drawer like this:

    Code (CSharp):
    1. using Sirenix.Utilities;
    2. using System;
    3. using System.Reflection;
    4. using UnityEngine;
    5.  
    6. #if UNITY_EDITOR
    7.  
    8. using Sirenix.OdinInspector.Editor;
    9. using Sirenix.Utilities.Editor;
    10.  
    11. #endif
    12.  
    13. public class MyMonoBehaviour : MonoBehaviour
    14. {
    15.     [FancyMinMax("Min", "Max")]
    16.     public Vector2 thingy;
    17.  
    18.     public Vector2 limits;
    19.  
    20.     public float Min()
    21.     {
    22.         return limits.x;
    23.     }
    24.  
    25.     public float Max()
    26.     {
    27.         return limits.y;
    28.     }
    29. }
    30.  
    31. #if UNITY_EDITOR
    32.  
    33. [OdinDrawer]
    34. public class FancyMinMaxDrawer : OdinAttributeDrawer<FancyMinMaxAttribute, Vector2>
    35. {
    36.     private class Context
    37.     {
    38.         public MethodInfo MinMethod;
    39.         public MethodInfo MaxMethod;
    40.         public string MinErrorMessage;
    41.         public string MaxErrorMessage;
    42.     }
    43.  
    44.     protected override void DrawPropertyLayout(IPropertyValueEntry<Vector2> entry, FancyMinMaxAttribute attribute, GUIContent label)
    45.     {
    46.         PropertyContext<Context> context;
    47.  
    48.         if (entry.Context.Get(this, "context", out context))
    49.         {
    50.             context.Value = new Context();
    51.  
    52.             context.Value.MinMethod = entry
    53.                                      .Property
    54.                                      .ParentType
    55.                                      .FindMember()
    56.                                      .IsMethod()
    57.                                      .IsNamed(attribute.MinMethod)
    58.                                      .HasReturnType<float>()
    59.                                      .HasNoParameters()
    60.                                      .GetMember<MethodInfo>(out context.Value.MinErrorMessage);
    61.  
    62.             context.Value.MaxMethod = entry
    63.                                      .Property
    64.                                      .ParentType
    65.                                      .FindMember()
    66.                                      .IsMethod()
    67.                                      .IsNamed(attribute.MaxMethod)
    68.                                      .HasReturnType<float>()
    69.                                      .HasNoParameters()
    70.                                      .GetMember<MethodInfo>(out context.Value.MaxErrorMessage);
    71.         }
    72.  
    73.         Vector2 limits = new Vector2(float.MinValue, float.MaxValue);
    74.  
    75.         if (context.Value.MinErrorMessage != null)
    76.         {
    77.             SirenixEditorGUI.ErrorMessageBox(context.Value.MinErrorMessage);
    78.         }
    79.         else
    80.         {
    81.             limits.x = (float)context.Value.MinMethod.Invoke(entry.Property.ParentValues[0], null);
    82.         }
    83.  
    84.         if (context.Value.MaxErrorMessage != null)
    85.         {
    86.             SirenixEditorGUI.ErrorMessageBox(context.Value.MaxErrorMessage);
    87.         }
    88.         else
    89.         {
    90.             limits.y = (float)context.Value.MaxMethod.Invoke(entry.Property.ParentValues[0], null);
    91.         }
    92.  
    93.         var value = SirenixEditorFields.MinMaxSlider(label, entry.SmartValue, limits, attribute.ShowFields);
    94.  
    95.         value.x = Mathf.RoundToInt(value.x);
    96.         value.y = Mathf.RoundToInt(value.y);
    97.  
    98.         entry.SmartValue = value;
    99.     }
    100. }
    101.  
    102. #endif
    103.  
    104. public class FancyMinMaxAttribute : Attribute
    105. {
    106.     public string MinMethod { get; private set; }
    107.     public string MaxMethod { get; private set; }
    108.     public bool ShowFields { get; private set; }
    109.  
    110.     public FancyMinMaxAttribute(string minMethod, string maxMethod, bool showFields = false)
    111.     {
    112.         this.MinMethod = minMethod;
    113.         this.MaxMethod = maxMethod;
    114.         this.ShowFields = showFields;
    115.     }
    116. }
    It has some basic code for ensuring only whole numbers are set, as well.
     
    Last edited: Jul 8, 2017
    JFernald likes this.
  21. SayhiUnity

    SayhiUnity

    Joined:
    Jan 7, 2017
    Posts:
    10
    why this cool tool doesn't supprot ScriptableObject :(
     
  22. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    I'm not entirely sure what you mean - we do support ScriptableObject in every sense, to the exact same extent that we support MonoBehaviour. The general inspector upgrade works for ScriptableObjects out of the box, just as for MonoBehaviours and other Unity types, and we have both SerializedScriptableObject and SerializedMonoBehaviour, among many other types, for the extra serialization support.
     
  23. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    And sorry, I completely missed this part of the question. You can use the [HideReferenceObjectPicker] attribute to hide the reference object picker. You can still set the value to null or change the type, through the right-click context menu.
     
    Menion-Leah likes this.
  24. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    Sorry, I seem to somehow have completely missed this post among the others! This is indeed possible - the [AssetList] attribute acts exactly as you describe when you put it on a field with a single, say, GameObject.
     
  25. luizbeneton

    luizbeneton

    Joined:
    Apr 8, 2013
    Posts:
    20
    Hi there, first of all, congratz on Odin, this is wonderful :)
    But I'm having an issue and I cannot find the solution.

    I have this:
    Code (CSharp):
    1. public class LineUpBonusRuleSetSO : SerializedScriptableObject
    2. {
    3.     [OdinSerialize]
    4.     private LineUpBonusRuleSingle[] allRulesSingle;
    5. }
    6.  
    7. [System.Serializable]
    8. public abstract class ALineUpBonusRule
    9. {
    10.     [SerializeField]
    11.     public string id;
    12.     [OdinSerialize]
    13.     private ILineUpCondition[] conditions;
    14. }
    15.  
    16. [System.Serializable]
    17. public class LineUpBonusRuleSingle : ALineUpBonusRule
    18. {
    19.     [OdinSerialize]
    20.     private LineUpModifierSingle[] lineUpModifiers;
    21. }
    22.  
    23. [System.Serializable]
    24. public class LineUpModifierSingle
    25. {
    26.     public StatModifier modifier;
    27.     public int positionToApply;
    28. }
    29.  
    30. [System.Serializable]
    31. public class StatModifier
    32. {
    33.     public EStatModifierApplicationType applicationYype;
    34.     public EStatType statType;
    35.     public float value;
    36.  
    37.     public StatModifier(EStatModifierApplicationType pApplicationType, EStatType pStatType, float pValue)
    38.     {
    39.         applicationYype = pApplicationType;
    40.         statType = pStatType;
    41.         value = pValue;
    42.     }
    43. }
    When the scriptable object instance is serialized it works almost as expected.
    It can create the list of Interfaces (and find all available Implementations of the interface, and this is actually why I bought Odin).
    But when it tries to serialize the "LineUpModifierSingle[]" it cannot find the StatModifiers:

    badOdin.jpg

    Am I missing something?

    Thanks!
     
  26. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    Currently it is a requirement that all types to be created by the type selector drop-down must have a public parameterless constructor.

    We're working on a type resolver that can handle constructors and the like, but for now that limitation remains in place.
     
    luizbeneton likes this.
  27. Aurigan

    Aurigan

    Joined:
    Jun 30, 2013
    Posts:
    291
    Looks like list drag/drop ordering is completely broken: http://imgur.com/a/o1jxH

    I also find it odd to have to select the class OR null when adding elements to a List<>, I don't think there will be enough people who want to insert null elements to justify the hassle of having to pick every time for everyone else.

    Also ... might be helpful to add links on the unity asset store listing to this forum ... the support website link goes to devdog and their forums don't actually cover this asset.
     
    Last edited: Jul 10, 2017
  28. Duffer123

    Duffer123

    Joined:
    May 24, 2015
    Posts:
    1,215
    @Tor-Vestergaard ,

    Excellent, thanks for your response. I have purchased this asset.

    Something else I was wondering about was the current 32bit limit to mask enums and whether say DropdownValue-like (DropdownMaskValue?) attribute could be a way around that but with a DropdownValue where (bit mask enum) you could tick and select all, none or some of the dropdown values but with no 32 bit / 32 dropdown options limit... ? Hope you get what I mean with this... ? That would be a major functionality boost!
     
  29. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Thanks for gif. I've been unable to reproduce the issue though. We did have some drag and drop issues in earlier builds. What version of Odin and Unity are you using? Does it also break with other lists, or is it just in some cases?

    Agreed! We are working on an entirely new object-picker that can handle constructors, unity objects and much more. It's still a couple of months away though.

    Where did you find this link exactly? An earlier version of Odin could explain this as well.

    Edit: Ah, found it. Thanks!
     
    Last edited: Jul 11, 2017
  30. Aurigan

    Aurigan

    Joined:
    Jun 30, 2013
    Posts:
    291
    Think that was in 5.6.1p1, whatever the version is on the asset store at the moment ... I only tried on this one list, seemed to fail 90% of the time. It looks like there's placeholder text displaying in there still as well 'Custom Drag Test'.

    Hrm, image below ... which I now realize is showing that presumably because that's the publisher support link and you're not the publisher.

     
  31. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Yeah, We'll ask though. We do get a couple of forwarded Odin related emails from them from time to time.
     
  32. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    I hope you'll enjoy using Odin!

    That's a good idea, actually - a ValueDropdown variant that works on a list, which lets you select any of a group of values to be in the list. Like a MultiValueDropdown. I've added that to the "suggestions" part of our todo list.

    I should note that, if you use Odin's serialization, there is no 32 bit limit on mask enums - the limit is instead 64 bit, when you back your enum by a long or an ulong. (This is the hard limit imposed by C#.) This gives you a rather larger amount of flag options to use. Now, Unity will still complain about you using an enum with an invalid underlying type, but we have a workaround for the cases where Unity complains about serializing values for you (this also applies to when you want to serialize cyclic references).

    Basically, the special case is this: if you mark something both with [NonSerialized] and with [OdinSerialize], Odin will ignore the [NonSerialized] attribute and still serialize the value. Unity, however, will just see the [NonSerialized] attribute, and will pass the value over without logging any complaints. So the following code will let you use a 64-bit flag enum:

    Code (CSharp):
    1. [Flags]
    2. public enum ULongEnum  : ulong
    3. {
    4.     None = 0,
    5.     One = 1,
    6.     Two = 1 << 1,
    7.     Three = 1 << 2,
    8.     // etc
    9. }
    10.  
    11. public class MyMonoBehaviour : SerializedMonoBehaviour
    12. {
    13.     [NonSerialized, OdinSerialize] private ULongEnum uLongFlags;
    14. }
     
  33. luizbeneton

    luizbeneton

    Joined:
    Apr 8, 2013
    Posts:
    20
    Thanks @Tor-Vestergaard !
    A quick follow up question.
    I think I've read everything that is available to be read in Odin documentation.
    Did I miss this information, or is it missing on the Docs?

    Thanks!
     
  34. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    It's quite possible that we haven't mentioned this anywhere - the documentation is still a work in progress, and we've been kept very busy since the release. We are, however, going to create a FAQ that contains answers to questions like this one as soon as we have time, and just add to that regularly.
     
    luizbeneton likes this.
  35. Duffer123

    Duffer123

    Joined:
    May 24, 2015
    Posts:
    1,215
    @Tor-Vestergaard ,

    Thanks for the useful response.

    Would be great if your MaskedDropdownValue just smashed the 64 items/bit limit... ;)
     
  36. WilsonCWong

    WilsonCWong

    Joined:
    Mar 20, 2013
    Posts:
    35
    How would one go about serializing to a json file and deserializing from a json file using Odin?
     
  37. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    You'd use the SerializationUtility class, which wraps the serialization system and handles all the boilerplate code involved. Whether or not you should use this, depends on your exact use-case though. If you just want to save some very simple data to json, I'd advice you to use Unity's own JsonUtility class - it's a lot faster than Odin's json serialization, which is comparatively slow. (Odin's binary format is very fast and highly optimized - the json, not so much. We just never took the time to do it, as it wasn't a high priority. Json is never used in Odin, by default.)

    If you have more complex graph-like data, with nulls, polymorphism, references, reference cycles, etc, or a need to handle types changing very well, then Odin's json serialization may be suited for you. Some example code might be as follows:

    Code (CSharp):
    1. // Take a class with some random garbage string data, random numbers, and cyclic references
    2. public class MyData
    3. {
    4.     public string str = new string(Enumerable.Range(0, 20).Select(i => (char)UnityEngine.Random.Range(50, 150)).ToArray());
    5.     public List<float> numbers = new List<float>(Enumerable.Range(0, 10).Select(i => UnityEngine.Random.Range(0f, 100f)));
    6.     public MyData reference;
    7. }
    8.  
    9. // Somewhere, a method to serialize data to json might look something like this
    10. private void SerializeData()
    11. {
    12.     // Save to Assets folder
    13.     string path = Application.dataPath + "/data.json";
    14.  
    15.     // Initialize some data
    16.     var originalData = new MyData();
    17.     originalData.reference = new MyData();
    18.     originalData.reference.reference = originalData;
    19.  
    20.     // Serialization
    21.     {
    22.         var bytes = SerializationUtility.SerializeValue(originalData, DataFormat.JSON);
    23.         File.WriteAllBytes(path, bytes);
    24.     }
    25.  
    26.     // Deserialization
    27.     {
    28.         var bytes = File.ReadAllBytes(path);
    29.         var data = SerializationUtility.DeserializeValue<MyData>(bytes, DataFormat.JSON);
    30.     }
    31. }
    This code results in a file looking something like this:

    Code (csharp):
    1. {
    2.     "$id": 0,
    3.     "$type": "0|MyData, Assembly-CSharp",
    4.     "str": "FPgIse\u0088\u0081H2\u0088jKZajf\u008b\u0085f",
    5.     "numbers": {
    6.         "$id": 1,
    7.         "$type": "1|System.Collections.Generic.List`1[[System.Single, mscorlib]], mscorlib",
    8.         "$rlength": 10,
    9.         "$rcontent": [
    10.             42.8637581,
    11.             66.9849243,
    12.             67.48748,
    13.             17.33166,
    14.             37.87858,
    15.             47.66845,
    16.             44.09235,
    17.             2.579546,
    18.             57.4410744,
    19.             36.1908035
    20.         ]
    21.     },
    22.     "reference": {
    23.         "$id": 2,
    24.         "$type": 0,
    25.         "str": "i4qA|\u0089nml\u0093e_Kg5gj46P",
    26.         "numbers": {
    27.             "$id": 3,
    28.             "$type": 1,
    29.             "$rlength": 10,
    30.             "$rcontent": [
    31.                 34.50238,
    32.                 59.6199341,
    33.                 94.5346,
    34.                 2.83037424,
    35.                 39.6058846,
    36.                 12.63138,
    37.                 35.2091026,
    38.                 76.407074,
    39.                 36.4512253,
    40.                 90.41513
    41.             ]
    42.         },
    43.         "reference": $iref:0
    44.     }
    45. }
    As you can see, Odin's json format keeps a bunch of metadata in special $-prepended entries. As such, this is not strictly completely correct json, but these are necessary to be able to fully reconstruct the data to the specifications of Odin's serialization system.

    I should note that in either of these cases, there are some strict limitations. You cannot save references to UnityEngine.Objects. Neither Odin's serialization, nor Unity's JsonUtility, allows you to do this. If you save down a reference to, say, a prefab or a texture, that reference will be gone when you load the data back up. For more details on why this is so, I refer you to this post of mine in the old Odin forum thread.

    It also discusses how you might go about "sort of" saving entire GameObjects and Components and the like, which is not a service Odin provides out of the box, for reasons also explained in that post.
     
    WilsonCWong likes this.
  38. JFernald

    JFernald

    Joined:
    May 11, 2015
    Posts:
    4
    Hello again,

    I've been experimenting with Odin and I've run into an issue where the [ValidateInput] attribute seems not to be working the way I would expect.

    Code (CSharp):
    1.         public class myCustomClass
    2.         {
    3.             public int something;
    4.         }
    5.  
    6.         [Required, ValidateInput("ValidateClassList", "You must have at least 1 item in the list.", InfoMessageType.Warning)]]
    7.         public List<myCustomClass> classList;
    8.  
    9.         private static bool ValidateClassList(List<AbstractCycleListener> listeners)
    10.         {
    11.             return classList != null && classList.Count == 0;
    12.         }
    When I view the inspector with the above code, I'll see a warning message when 0 items are in the list. However if I add an item to the list the warning will not go away. If I save the scene with 1 element in the list and re-open unity the warning will go away and if I delete the item in the list the warning will not return.

    I've been able to work around the issue by using the [InfoBox("You must have at least 1 item in the list.", InfoMessageType.Warning, "ValidateClassList")] Attribute instead which seems to work as I'd expect.

    Is this a bug? Or am I missunderstanding how the ValidateInput attribute is supposed to work?
     
  39. roykoma

    roykoma

    Joined:
    Dec 9, 2016
    Posts:
    176
    By reading http://sirenix.net/odininspector/documentation/sirenix/odininspector/validateinputattribute i would expect it to work like follows:

    Code (CSharp):
    1.  
    2.  public class myCustomClass
    3.         {
    4.             public int something;
    5.         }
    6.         [Required, ValidateInput("ValidateClassList", "You must have at least 1 item in the list.", InfoMessageType.Warning)]]
    7.         public List<myCustomClass> classList;
    8.         private static bool ValidateClassList(List<AbstractCycleListener> listeners)
    9.         {
    10.             return listeners != null && listeners.Count >= 1;
    11.         }
     
  40. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Hello! We're loving Odin so far, but we're running into something of a roadblock. When we started out, we were intending to edit ScriptableObjects individually in their respective inspectors (we didn't think we'd have that many), so we put a lot of effort into setting up attributes and such and making it look really nice using Odin, right there directly in our ScriptableObject classes.

    However, the project has moved on and we now have hundreds of objects to deal with, so we've decided to go with a database and an editor window so we can sort through the whole collection of ScriptableObjects all at once. Our immediate problem is that we're not really incredibly familiar with editor windows- we had a certain expectation that we could build a list on the left, for navigating through our object collection, and then when you click one in the list, display its data on the right in the same window. We actually got this aspect working, more or less, but it's not including the formatting that we already put all of that work into getting to look perfect in the individual inspectors views.

    We had hoped that we would be able to make a Rect on the right side and just use Editor.DrawDefaultInspector() in there and have the ScriptableObject data show up the same way it does in the inspector windows- with those Odin attributes making it look nice and sexy. Unfortunately, we've been unsuccessful in our attempts to do this.

    Do you have any tips- are we overlooking something really simple, or are we going to have to build the ScriptableObject display formatting in the editor window from scratch?
     
    Last edited: Jul 13, 2017
  41. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    Great to hear that you're liking Odin!

    Your problem would be that once you've made your editor instance to draw in the right-hand part of your window using Editor.CreateEditor, you need to call OnInspectorGUI on the editor instance, not DrawDefaultInspector. DrawDefaultInspector will always draw Unity's default inspector, completely bypassing Odin's inspector code. OnInspectorGUI, however, is the same method that Unity invokes to draw Odin's inspector, so that should just work with no issues.

    Oh, and do remember to cache your editors and only create them when the object selection changes - we had some customers who were creating new editor instances every frame. It did end up working mostly fine for them, but initializing an Odin editor is somewhat expensive because we need to scan the object in question, create our whole property tree, and pre-generate and cache a lot of things. Not something you'd notice generally, but if you do it every frame, you will probably have a generally slower-feeling inspector.

    Hope that helps!
     
    Last edited: Jul 13, 2017
    Alex_May and DonLoquacious like this.
  42. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    186
    This is a bug. We originally tried to build the input validation a certain way, but it seems it's not working out as well as we'd hoped, due to the wide variety of ways that people can modify values from outside of the classical editor GUI loop (such as through other windows, or from context menus, and so on and so forth). A while back we updated the OnValueChanged drawer to be a lot more robust, and we're going to have to upgrade our InputValidation drawer to use the same system. This should be in the next patch.

    Edit: You can also use this alternate way of providing functionality very similar to input validation, while waiting for the next patch.
     
    Last edited: Jul 13, 2017
  43. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    That worked beautifully, thanks, and thanks as well for the note to cache the editor because that helps the stuttering I was seeing (even just drawing the default inspector as I was).

    Cheers!
     
  44. bilke

    bilke

    Joined:
    Jul 13, 2012
    Posts:
    54
    First thanks for this great asset, loving it already!

    1. Are properties with custom getters and setters supported? I have this property but it is not shown in the editor at all:

    Code (CSharp):
    1. [OdinSerialize]
    2. public bool Enabled {
    3.     get { return _enabled; }
    4.     set
    5.     {
    6.         _enabled = value;
    7.         DoSomething();
    8.     }
    9. }
    10. private bool _enabled = true;
    If I move [OdinSerialize] to the private variable the property is shown in the editor but it modifies the private var directly; not via its getters and setters.

    2. After importing Odin into a I would say medium-sized project I encountered an infinite recompile loop of GeneratedOdinEditors.dll eventually crashing Unity (5.6.2f1) but after disabling automatic rebuilding it seems to work.. Is there any way to debug this further?
     
  45. bilke

    bilke

    Joined:
    Jul 13, 2012
    Posts:
    54
    For 1. it seems I am out of luck as the documentation states:
     
  46. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Hi Bilke, glad to hear you are enjoying Odin!

    You had the right idea when you put the OdinSerialzie on the field instead of the property. You just needed to hide the private field, and show the property.

    Try this:

    Code (CSharp):
    1. [ShowInInspector]
    2. public bool Enabled {
    3.     get { return _enabled; }
    4.     set
    5.     {
    6.         _enabled = value;
    7.         DoSomething();
    8.     }
    9. }
    10.  
    11. [SerializeField, HideInInspector] // (I used SerializeField instead of OdinSerialize) - Always make unity serialize the value instead of Odin if you can get away with it. More on that in the docs :)
    12. private bool _enabled = true;
    We'll make sure to include an example like this. You are not the first to ask.


    If you go into the Odin Preferences window under Editor Types and check the "Enable Editor Generation Logging" It should provide a debug message as to why it is regenerating. From there we can investigate further.
     
    Last edited: Jul 14, 2017
  47. SayhiUnity

    SayhiUnity

    Joined:
    Jan 7, 2017
    Posts:
    10
    help!
    at first, Sorry for my poor English. hope you can understand what I say below

    Is there a conflict between Odin and Inventory Pro?
    after install Odi, the InventoryPro custom attribute ForceCustomObjectPicker lose efficacy (in my own script).
    2.png
    when I click the small circle in Inspector, It did't show the InventoryItemObjectPickerEditor (before install Odi, it can work). now it shows the Unity default picker editor.
    when I drop a InventoryPro item prefab in a InventoryItemBase field (this field have a ForceCustomObjectPicker attribute in script) manually, it shows this:
    1.png

    how to fix this?
     
  48. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Thanks for the screenshots, we are looking into this. In the meanwhile, you can try adding the attribute [DrawWithUnity] to the _item field and see if that fixes the issue?
     
  49. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    Is there a way to add dynamic text to an InfoBox?

    We have a property which is a list of items, and we have a list of item names that must be in that list. We are using an InfoBox with a validation method which looks through the list to verify all the required items are present. This works fine, however when the artist is missing a required item, right now it just says they are missing something which is only of partial help. I'd rather include a list of the items missing so that the artist knows what they need to add.

    So instead of for example an info box that says "
    Missing one or more required properties for this Hardware type" which is what it says now, I'd like it to say something like "
    Missing required properties (Mass, Diameter) for this Hardware type"

    Not entirely sure how this would work though, but it would sure be nice.
     
  50. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    There are a couple of ways doing this, and you can certainly do this with the InfoBox as well:
    You can add a $ sign in the InfoBox text to reference a member like so:
    Code (CSharp):
    1.  
    2.     [InfoBox("$GetSomeErrorMessage", InfoMessageType.Error, "ShowSomeErrorMessage")]
    3.     public List<string> SomeList;
    4.  
    5. #if UNITY_EDITOR
    6.  
    7.     private static string[] requiredItems = new string[] { "Mass", "Diameter" };
    8.  
    9.     private bool ShowSomeErrorMessage
    10.     {
    11.         get { return !string.IsNullOrEmpty(this.GetSomeErrorMessage()); }
    12.     }
    13.  
    14.     private string GetSomeErrorMessage()
    15.     {
    16.         var errorMessages = requiredItems
    17.             .Where(x => !this.SomeList.Contains(x))
    18.             .Select(x => x + " is missing.")
    19.             .ToArray();
    20.  
    21.         return string.Join(Environment.NewLine, errorMessages);
    22.     }
    23.  
    24. #endif
    The $ sign trick actually works for most of our attributes btw.

    Perhaps a more clean way of doing this (until we get our ValidateInput attribute updated) is to use OnInspectorGUI:

    Code (CSharp):
    1.  
    2.     [OnInspectorGUI("ValidateSomeListOnGUI", append: false)]
    3.     public List<string> SomeList;
    4.  
    5. #if UNITY_EDITOR
    6.  
    7.     private static string[] requiredItems = new string[] { "Mass", "Diameter" };
    8.  
    9.     private void ValidateSomeListOnGUI()
    10.     {
    11.         var errorMessages = requiredItems
    12.             .Where(x => !this.SomeList.Contains(x))
    13.             .Select(x => x + " is missing.");
    14.  
    15.         if (errorMessages.Any())
    16.         {
    17.             SirenixEditorGUI.ErrorMessageBox(string.Join(Environment.NewLine, errorMessages.ToArray()));
    18.         }
    19.     }
    20.  
    21. #endif
    Very soon you will be able to do this, which only get called if a value changes etc..

    Code (CSharp):
    1.     [ValidateInput("IsSomeListValid")]
    2.     public List<string> SomeList;
    3.  
    4. #if UNITY_EDITOR
    5.  
    6.     private static string[] requiredItems = new string[] { "Mass", "Diameter" };
    7.  
    8.     private bool IsSomeListValid(ref string errorMessage)
    9.     {
    10.         var errorMessages = requiredItems
    11.             .Where(x => !this.SomeList.Contains(x))
    12.             .Select(x => x + " is missing.");
    13.  
    14.         if (!errorMessages.Any())
    15.         {
    16.             return true;
    17.         }
    18.  
    19.         errorMessage = string.Join(Environment.NewLine, errorMessages.ToArray());
    20.         return false;
    21.     }
    22.  
    23. #endif
    24.