Search Unity

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

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

  1. mkgame

    mkgame

    Joined:
    Feb 24, 2014
    Posts:
    592
    Hi,

    I just using odin as editor extension, for having a better control over the Unity editor solution. But now I had have to remove Odin from my project, because I had have disadvantages with.

    - I have a list of [System.Serializable] objects. These instances have a lot if fields. In the Unity solution all the instances got a grouping parent element (named by the first field of the instance), so I can collapse the lot of fields. In this way the Editor works faster and I have a good overview. Odin blows up all the fields, this is the main reason why I had to remove it from my project.

    - Adding a new entry does not clone the last entry. Makes me just more work copying the values with additional copy & paste operation. If you have a lot of entries, you don't want this disadvantage. This was in my situation as worse as the first point.

    - Just a bug, expanded the list and tried to change an enum in a [System.Serializable] instance on the last position. This didn't work. I had to collapse the list and move to the last page, then this change was applied.

    I hope you will fix these issues.
     
    Last edited: Nov 21, 2017
  2. amateurhr

    amateurhr

    Joined:
    Jun 28, 2013
    Posts:
    24
    Ran into a problem with a really basic scenario.
    Without Odin - specifying a static default value or a constructor initialized value on a Serializable custom class that is stored in a list correctly starts all new instances of this class with the default value.
    Once Odin is installed, adding new items to the list does not respect the default values. This is near unusable as is.

    Code (CSharp):
    1.  
    2. [Serializable]
    3. public class MyData
    4. {
    5.     public float Pitch = 1.0f;
    6.  
    7.     public MyData()
    8.     {
    9.         Pitch = 1.0f;
    10.     }
    11. }
    12.  
    13. public class MyBehaviour : MonoBehaviour
    14. {
    15.     public List<MyData> Data = new List<MyData>();
    16. }
    Adding an item to the list in the inspector will yield Pitch = 0.0f
     
  3. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Yeah, that's not a bad idea, you could easily make a [DisallowChildObjects] attribute. It could look something like this:

    Usage:
    Code (CSharp):
    1. public class SomeMonoBehaviour : MonoBehaviour
    2. {
    3.     [DisallowChildObjects]
    4.     public List<GameObject> Test1;
    5.  
    6.     [DisallowChildObjects]
    7.     public List<MonoBehaviour> Test2;
    8. }
    Attribute:
    Code (CSharp):
    1. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    2. public class DisallowChildObjectsAttribute : Attribute
    3. {
    4. }
    Drawer:
    Code (CSharp):
    1. [OdinDrawer]
    2. [DrawerPriority(DrawerPriorityLevel.WrapperPriority)]
    3. public class DisallowChildObjectsAttributeDrawe<T> : OdinAttributeDrawer<DisallowChildObjectsAttribute, T>
    4.     where T : UnityEngine.Object
    5. {
    6.     public override bool CanDrawTypeFilter(Type type)
    7.     {
    8.         return type.InheritsFrom<Behaviour>() || type == typeof(GameObject);
    9.     }
    10.  
    11.     private class Context
    12.     {
    13.         public string ErrorMessage;
    14.     }
    15.  
    16.     protected override void DrawPropertyLayout(IPropertyValueEntry<T> entry, DisallowChildObjectsAttribute attribute, GUIContent label)
    17.     {
    18.         var context = entry.Property.Context.Get(this, "isValid", (Context)null);
    19.  
    20.         if (context.Value == null)
    21.         {
    22.             context.Value = new Context();
    23.             entry.OnValueChanged += i => Validate(entry, context.Value);
    24.             Validate(entry, context.Value);
    25.         }
    26.  
    27.         if (context.Value.ErrorMessage != null)
    28.         {
    29.             SirenixEditorGUI.ErrorMessageBox(context.Value.ErrorMessage);
    30.         }
    31.  
    32.         this.CallNextDrawer(entry, label);
    33.     }
    34.  
    35.     private static void Validate(IPropertyValueEntry<T> entry, Context context)
    36.     {
    37.         context.ErrorMessage = null;
    38.         var targets = entry.Property.Tree.WeakTargets;
    39.         var values = entry.WeakValues;
    40.  
    41.         for (int i = 0; i < targets.Count; i++)
    42.         {
    43.             // Multiselection
    44.             var target = targets[i] as Behaviour;
    45.             var value = entry.Values[i];
    46.  
    47.             if (target == null)
    48.             {
    49.                 break;
    50.             }
    51.  
    52.             if (value == null)
    53.             {
    54.                 continue;
    55.             }
    56.  
    57.             var go = value as GameObject;
    58.             if (go == null)
    59.             {
    60.                 var behaviour = value as Behaviour;
    61.                 if (behaviour)
    62.                 {
    63.                     go = behaviour.gameObject;
    64.                 }
    65.             }
    66.  
    67.             if (go == null)
    68.             {
    69.                 continue;
    70.             }
    71.  
    72.             if (target.GetComponentsInChildren(typeof(Transform)).Contains(go.transform))
    73.             {
    74.                 context.ErrorMessage = "Child objects are not allowed.";
    75.                 break;
    76.             }
    77.         }
    78.     }
    79. }
     
  4. Kolyasisan

    Kolyasisan

    Joined:
    Feb 2, 2015
    Posts:
    397
    Sorry for an incredibly dumb question, but can we really serialize all of that fancy stuff and use them with binary serialization and saving? I am currently really interested in this plugin. If we really can save/load all that in binary format then I'm completely into it.
     
  5. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
  6. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Not currently, but thanks for asking, it's a good idea. After this next patch, we're planning on taking the whole validation workflow to the next level :)
     
  7. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Okay, so I've taken a look at this, and it looks very much like it's the fact that the type "System.Linq.Expressions.MemberExpression" (which I assume is the type you're using here) is crazy, and not outright supported out-of-the-box by our serializer because of its implementation. It has no special serialization implementations that tells our system how to serialize it - so it doesn't know how to serialize itself, and in fact it isn't even really meant to ever be serialized at all. It's just that our serialization system is brave and gives it a go anyways - which does generally work out in the vast majority of cases.

    So what's happening is that, each time the value changes, it is "serialized" by Odin (and all its internal values go missing) and then deserialized thereafter, and then you get a "borked" version of the value deserialized, because all of its internal values are weird, and that borked value is no longer equal to the possible value that exists in your value dropdown, so it is changed yet again to match the value in the dropdown, but yet again it does not persist properly - and repeat ad infinitum.

    There is no easy fix here. MemberExpression and its derived types were never made to be serialized in the traditional C# sense. Right now you would have to create custom serialization formatters for all member expression types that you want to be able to serialize (to tell Odin how it is to be done), and that would no doubt be an utter pain. It is quite possible that Odin will contain some facilities to make extending this a lot easier in the future and give it "broad" instructions for how to serialize all MemberExpression derived types, but for now, I'm afraid it doesn't.

    I'm afraid this just isn't an easy, "vanilla" serialization case.
     
    ugen_oak likes this.
  8. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Had a look at this, and it turned out to be a bug in how we created objects with "simulated" default Unity-style values for Unity-serialized members. This has now been fixed. If you'd like the fix, please send me a mail at tor@sirenix.net, referencing this conversation, along with your Odin invoice ID, and I'll send you a build with the fix included.
     
  9. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Not sure what you mean by "all that", but Odin will indeed serialize (almost) anything for you - interfaces (polymorphism), null values, dictionaries, delegates, and so on. As for the formats, yes, Odin sports its own custom binary format, as well as the ability to save to a merge-friendly node format in the editor, and json as well.
     
  10. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    I'm sorry to hear you've had issues. I'll answer your points individually.

    This is indeed possible - you merely have to tell Odin what to use as the label for the dropdown, and Odin will show you a collapsible dropdown for each list element.

    Code (CSHARP):
    1. [Serializable]
    2. public class MyData
    3. {
    4.     public string name = "some name";
    5. }
    6.  
    7. [ListDrawerSettings(ListElementLabelName = "name")]
    8. public List<MyData> Data = new List<MyData>();
    Turns out there are a lot of different ways people would like elements to be added to lists and removed from lists. We're likely going to be extending the functionality of that "+" button and those 'X' buttons in a patch very soon, as a lot of requests have to do with this sort of stuff.

    This is an insidious bug related to Unity's control ID system, that we're trying to figure out, but which is proving fiendishly difficult to reproduce and track down. If you can provide any code examples with step-by-step instructions for reliably reproducing the issue, we would greatly appreciate it. It does, however, seem so elusive that it seems likely that we will have to add in extra detection code to our next patch, just to detect where exactly the bug is happening, and to produce detailed error messages that people can send us that will help figure out its origin.
     
  11. Kolyasisan

    Kolyasisan

    Joined:
    Feb 2, 2015
    Posts:
    397
    Sorry, I just can't really understand it. AFAIK not everything in Unity can be serialized and saved (Vector3, for example). Can Odin's serialization save everything or nearly everything that is defined as serializable?
     
  12. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Unity can definitely serialize Vector3, and so can Odin. Usually, what data you can have in your inspector is limited by what Unity is capable of serializing. Odin extends this with its own serializer, so that you can view, edit and save data in the inspector that isn't supported directly by Unity - such as dictionaries, for example.
     
  13. Kolyasisan

    Kolyasisan

    Joined:
    Feb 2, 2015
    Posts:
    397
    Thank you very much for your attention. Now I'll surely buy Odin.
     
    bjarkeck likes this.
  14. ugen_oak

    ugen_oak

    Joined:
    Oct 18, 2016
    Posts:
    6
    Thanks for the great explanation of the problem. It clarified everything.

    Since it's potentially so much work to write custom serialization for MemberExpression, what I think I'll do is create a proxy object that can be serialized containing all of the data to make a MemberExpression. Then I'll use that to create the MemberExpression at runtime. I was just hopeful that direct serialization would work since it would simplify the process (and potentially reduce some of my runtime costs).

    I might actually try out writing some custom serialization formatters just to familiarize myself with that process. I'm extremely impressed with how powerful this tool is and look forward to being able to take full advantage of it.

    Thanks again for the response, and thanks for the awesome tool.
     
    bjarkeck likes this.
  15. mkgame

    mkgame

    Joined:
    Feb 24, 2014
    Posts:
    592
    I didn't know that, my fault. And I can select the name for the node additionally, awesome.
    [ListDrawerSettings(ListElementLabelName = "name")]

    For the another issues, good to hear, that they are already in sight.

    Thanks for your answer!

    I have another trouble with the Unity/Odin inspector, maybe you can improve that. Selecting an enum value in the inspector (Odin/Unity) gives us a drop down list. Unfortunatelly I have about 150 entries and it will grow to 450 (RTS game http://www.metadesc.com/studio/ , 5 races, all buildings/upgrades/researches/skills). I can fix this issue by creating an editor script for the affected classes, but it would be better to having it fixed everywhere. My fix is simple, typing in the enum value, else scrolling through so much enum values is madness. A good solution could be auto completion by typing the enum value name.
     
  16. puzzlekings

    puzzlekings

    Joined:
    Sep 6, 2012
    Posts:
    404
    Hey @Tor-Vestergaard just bought this and it looks awesome.

    However it made me feel pretty stupid at the start because my lists were all expanded and I think in the Getting Started 101 it should tell you how to deal with a simple use case for lists like this.

    After much googling I found I needed to have this precede my list:

    [ListDrawerSettings(ListElementLabelName = "Name")]
    public List<LevelChoice> LevelChoices;

    However Unity was complaining that it needed a Using Directive so I had to guess putting this in

    #if UNITY_EDITOR

    using Sirenix.OdinInspector;

    #endif

    Is this correct? Could not find it in any of the examples...
     
  17. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    This isn't something Odin does out of the box, but you can make a custom enum drawer that lets you do this. For example, this is an enum drawer that will apply to all enums that have more than a certain number of possible values:

    Code (CSHARP):
    1. using Sirenix.OdinInspector.Editor;
    2. using System;
    3. using UnityEngine;
    4. using Sirenix.Utilities.Editor;
    5.  
    6. [OdinDrawer]
    7. [DrawerPriority(0, 0, 2)] // Prioritize over regular enum drawer
    8. public class BigEnumDrawer<T> : OdinValueDrawer<T>
    9. {
    10.     public override bool CanDrawTypeFilter(Type type)
    11.     {
    12.         // Draw all enums with more than 30 possible values
    13.         return type.IsEnum && Enum.GetValues(type).Length > 30;
    14.     }
    15.  
    16.     protected override void DrawPropertyLayout(IPropertyValueEntry<T> entry, GUIContent label)
    17.     {
    18.         // Get current value
    19.         Enum value = (Enum)entry.WeakSmartValue;
    20.  
    21.         // Just for demonstration to show where the drawer applies, draw a colored but regular enum field.
    22.         // TODO: This is where you would write your own code to select the new enum value.
    23.         GUIHelper.PushColor(Color.green);
    24.         value = SirenixEditorFields.EnumDropdown(label, value);
    25.         GUIHelper.PopColor();
    26.  
    27.         // Set (possibly) changed value
    28.         entry.WeakSmartValue = value;
    29.     }
    30. }
    You don't need to add the preprocessor statements - the Sirenix.OdinInspector namespace is safe to use directly in build code. And you bring up a good point - we do need to work on our onboarding and introduction to initial concepts, and we are doing just that. It's just taking some time - after all, there's only so many of us, and not all of us can work on Odin full time :)
     
  18. doq

    doq

    Joined:
    Aug 17, 2015
    Posts:
    121
    Does Odin have facilities to make custom property drawer as easy as custom editors? I'm trying to create a property drawer with a button that displays a dropdown menu with options to switch the display of the property. At the moment I'm displaying a GenericMenu when a button is clicked, but I wonder if there's an easier way like using DropDownAttribute in a custom editor.
     
  19. mSkull

    mSkull

    Joined:
    Apr 19, 2016
    Posts:
    11
    Currently, we don't really have a way to validate assets directly, but if you have any prefab fields you can use the InlineEditor attribute, which would then also include them in the scene validation.
     
  20. Westy661

    Westy661

    Joined:
    Sep 13, 2015
    Posts:
    14
    Quick question: Is it true that i cant use the polymorphic object picker if my classes are derived from MonoBehaviour? I want to add different subclasses to a list on demand, and i also want to instantiate the list elements as monobhaviour. Thanks in advance!

    #Update: I hacked in the function, but the presentation is far from perfect. I would like to add these via the object picker. Any advice guys?


     
    Last edited: Nov 25, 2017
  21. ocimpean

    ocimpean

    Joined:
    Aug 10, 2013
    Posts:
    128
    I apologize for the basic question, but can this asset be used just for the editor part and VS replacement, I am tired of opening VS for minutes at the time in my projects and looking for a simple - faster alternative?
     
  22. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Making a custom drawer in Odin is very easy - please see our manual for details. As for your specific use case, I'm afraid I'd need a little more detail and context before I could make any suggestions as to good solutions.

    Odin can draw polymorphism (subclasses in fields of base classes) in Unity-serialized objects, but I'm afraid that Unity itself won't actually serialize the subclasses, and so all of your subclass-specific data will be lost upon reload. Disabling the object picker for Unity-serialized data is a very conscious decision on our end to prevent people from using it and thinking that it will work - which it won't.

    You need to use Odin's serializer through a class like SerializedMonoBehaviour to both view and persist polymorphic data.

    You can use Odin just for the editor part, yes, by going into the preferences and enabling Editor Only Mode, which prevents Odin from being included in your builds. Odin, however, does not replace Visual Studio in any way, and has nothing to do with editing code at all; Odin is merely a code library for making certain things easier to do through code, but we do not have anything to do with the writing of the code itself.
     
  23. doq

    doq

    Joined:
    Aug 17, 2015
    Posts:
    121
    Yes I've read the manual on drawers, though I may have missed some details as there's a lot to take in. But I'm using OdinValueDrawer to draw the property like so, https://streamable.com/unovf

    Code (CSharp):
    1.  
    2.    
    3.     [CreateAssetMenu(fileName ="FloatVariable", menuName ="Loqheart.ScriptableObject/Variable/Float")]
    4.     public class FloatVariable : ScriptableObject
    5.     {
    6.         public float value;
    7.     }
    8.  
    9.     [Serializable]
    10.     public class FloatReference
    11.     {
    12.         public bool useConstant;
    13.         public FloatVariable variable;
    14.         public float constantValue;
    15.  
    16.         public float Value
    17.         {
    18.             get { return useConstant ? constantValue : variable.value;  }
    19.         }
    20.     }
    21.  
    22.     [OdinDrawer]
    23.     public class FloatReferenceDrawer<T> : OdinValueDrawer<T> where T : FloatReference
    24.     {
    25.         void OnUseConstant(object value)
    26.         {
    27.             (value as T).useConstant = true;
    28.         }
    29.  
    30.         void OnUseVariable(object value)
    31.         {
    32.             (value as T).useConstant = false;
    33.         }
    34.  
    35.         protected override void DrawPropertyLayout(IPropertyValueEntry<T> entry, GUIContent label)
    36.         {
    37.             T value = entry.SmartValue;
    38.             GUILayout.BeginHorizontal();
    39.             EditorGUILayout.LabelField(label, GUILayout.Width(100f));
    40.             if (GUILayout.Button(ScriptableObjectResources.optionMenuButtonTexture, ScriptableObjectResources.optionMenuButtonWidth, ScriptableObjectResources.optionMenuButtonHeight))
    41.             {
    42.                 GenericMenu menu = new GenericMenu();
    43.                 menu.AddItem(new GUIContent("Constant"), value.useConstant, OnUseConstant, value);
    44.                 menu.AddItem(new GUIContent("Variable"), !value.useConstant, OnUseVariable, value);
    45.                 menu.ShowAsContext();
    46.             }
    47.  
    48.             if (value.useConstant)
    49.             {
    50.                 value.constantValue = EditorGUILayout.FloatField(value.constantValue);
    51.             }
    52.             else
    53.             {
    54.                 value.variable = EditorGUILayout.ObjectField(value.variable, typeof(FloatVariable), false) as FloatVariable;
    55.             }
    56.             GUILayout.EndHorizontal();
    57.         }
    I see ValueDropdownAttribute and CustomeContextMenuAttribute, and I'm wondering if there's a way I could build property drawers as easily.
     
    bjarkeck likes this.
  24. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Yeah, we have the CustomValueDrawer attribute that lets you create a custom value drawer with a single method. You might find that useful :)

    Otherwise, I think you are on the right track using an OdinDrawers for this sort of thing.

    And thanks for sharing the scripts btw, we are actually also playing around with scriptable object variables our selfs, here is my current take on it. It's still a WIP.

    OdinScriptableObjectVariables.png

    Usage:
    Code (CSharp):
    1. public class SomeMonoBehaviour : MonoBehaviour
    2. {
    3.     public IntReference test1;
    4.     public FloatReference test2;
    5. }
    Odin Drawer:
    Code (CSharp):
    1. [OdinDrawer]
    2. public class VariableReferenceDrawer<TReference, TVariable, TValue> : OdinValueDrawer<TReference>
    3.     where TReference : VariableReference<TValue, TVariable>
    4.     where TVariable : ScriptableObjectVariable<TValue>
    5. {
    6.     protected override void DrawPropertyLayout(IPropertyValueEntry<TReference> entry, GUIContent label)
    7.     {
    8.         var value = entry.SmartValue;
    9.  
    10.         GUILayout.BeginVertical();
    11.         {
    12.             var btnRect = GUIHelper.GetCurrentLayoutRect();
    13.             btnRect.width = EditorGUIUtility.labelWidth;
    14.             btnRect = btnRect.AlignRight(18);
    15.             btnRect.y += 4;
    16.  
    17.             if (GUI.Button(btnRect, GUIContent.none, "PaneOptions"))
    18.             {
    19.                 var menu = new GenericMenu();
    20.                 menu.AddItem(new GUIContent("Constant"), value.UseConstant, () => value.UseConstant = true);
    21.                 menu.AddItem(new GUIContent("Variable"), !value.UseConstant, () => value.UseConstant = false);
    22.                 menu.ShowAsContext();
    23.             }
    24.  
    25.             EditorGUIUtility.AddCursorRect(btnRect, MouseCursor.Arrow);
    26.  
    27.             if (value.UseConstant)
    28.             {
    29.                 entry.Property.Children["ConstantValue"].Draw(label);
    30.             }
    31.             else
    32.             {
    33.                 entry.Property.Children["Variable"].Draw(label);
    34.             }
    35.         }
    36.         GUILayout.EndVertical();
    37.     }
    38. }
    Variable and reference definitions:
    Code (CSharp):
    1. [CreateAssetMenu(fileName = "FloatVariable", menuName = "Scriptable Object Variables/Float")]
    2. public class FloatVariable : ScriptableObjectVariable<float>
    3. {
    4. }
    5.  
    6. [Serializable]
    7. public class FloatReference : VariableReference<float, FloatVariable>
    8. {
    9. }
    10.  
    11. [CreateAssetMenu(fileName = "IntVariable", menuName = "Scriptable Object Variables/Int")]
    12. public class IntVariable : ScriptableObjectVariable<int>
    13. {
    14. }
    15.  
    16. [Serializable]
    17. public class IntReference : VariableReference<int, IntVariable>
    18. {
    19. }
    Base classes:
    Code (CSharp):
    1. public abstract class ScriptableObjectVariable<T> : ScriptableObject
    2. {
    3.     [LabelText("$name")]
    4.     public T Value;
    5. }
    6.  
    7. public abstract class VariableReference<TValue, TVariable>
    8.     where TVariable : ScriptableObjectVariable<TValue>
    9. {
    10.     public bool UseConstant;
    11.  
    12.     public TValue ConstantValue;
    13.  
    14.     [SuffixLabel("$GetValueString", true)]
    15.     public TVariable Variable;
    16.    
    17.     public TValue Value
    18.     {
    19.         get { return !this.UseConstant && this.Variable ? this.Variable.Value : this.ConstantValue; }
    20.     }
    21.  
    22.     private string GetValueString()
    23.     {
    24.         return this.ConstantValue.ToString() + "    ";
    25.     }
    26. }
     
    doq likes this.
  25. doq

    doq

    Joined:
    Aug 17, 2015
    Posts:
    121
    Awesome! After watching Ryan Hipple's Unite 2017 talk on Scriptable Objects, I was looking for a way to genericize the ideas in his talk though it seems there isn't a way to create a generic Scriptable Object. I have an ECS framework similar to Entitas, and I want compare the pros and cons between them. Thanks for the code example!
     
    bjarkeck likes this.
  26. Stroved

    Stroved

    Joined:
    Jan 3, 2014
    Posts:
    16
    I've just purchased Odin and already its made editing my objects easier, so thank you for creating this! I also cannot wait for the next update (which I saw on Reddit) to make my life easier while making quests/spells/items etc.

    I do have an issue however: my TextArea's are always highlighting all of the text. I cannot double click on a word to get it to select that, or click once to move my cursor to the position I clicked. I am not using any Odin specific attributes, just Unity's TextArea:

    Code (CSharp):
    1. [FoldoutGroup("General")] [TextArea(1, 30)] public string toolTip;
    I've done a bit of searching but cannot find anyone bringing this issue up. Is this a bug or am I missing something?
     
  27. Uli_Okm

    Uli_Okm

    Joined:
    Jul 10, 2012
    Posts:
    95
    Hi!
    I didn`t found this anywhere: Is it possible to make Odin show const values in the inspector? As a readonly field?
    Thanks
     
  28. EETechnology

    EETechnology

    Joined:
    Aug 15, 2015
    Posts:
    185
    Hey

    I have 2 questions

    1. I have a class that doesn't extend MonoBehaviour, and Odin shows it in inspector. However, if this class is empty(that means, without any attributes or variables), Odin shows a message : "The class has no attributes and no custom drawer...". How can I disable this, because I want the class to be empty.

    2. Are there any attributes that dont require variables? Like only show an info box without any variable?
     
  29. doq

    doq

    Joined:
    Aug 17, 2015
    Posts:
    121
    What's an idiomatic way of initializing an OdinDrawer? I'd like to setup some variables for reuse. Creating a constructor for the custom drawer prevents Odin from drawing. So, I either have to check for null on a variable before initializing or in the DrawPropertyLayout function or I need to create a separate static class.

    Code (CSharp):
    1.     [OdinDrawer]
    2.     public class ReferenceDrawer<TReference, TVariable, TValue> : OdinValueDrawer<TReference>
    3.         where TReference : Reference<TValue, TVariable>
    4.         where TVariable : Variable<TValue>
    5.     {
    6.         string[] popupOptions = { "Constant", "Variable" };
    7.         GUIStyle popupStyle;
    8.  
    9.         /*
    10.         ReferenceDrawer()
    11.         {
    12.             Debug.Log("The constructor isn't called and property is drawn in a default manner without Odin.");
    13.         }
    14.         */
    15.  
    16.         protected override void DrawPropertyLayout(IPropertyValueEntry<TReference> entry, GUIContent label)
    17.         {
    18.             if (popupStyle == null) // Since constructors don't work, check for initialization here
    19.             {
    20.                 popupStyle = new GUIStyle(GUI.skin.GetStyle("PaneOptions"));
    21.                 popupStyle.imagePosition = ImagePosition.ImageOnly;
    22.             }
    23.  
    24.             TReference value = entry.SmartValue;
    25.             GUILayout.BeginHorizontal();
    26.  
    27.             var btnRect = GUIHelper.GetCurrentLayoutRect();
    28.             btnRect.width = EditorGUIUtility.labelWidth;
    29.             btnRect = btnRect.AlignRight(18);
    30.             btnRect.y += 4;
    31.  
    32.             value.useConstant = EditorGUI.Popup(btnRect, value.useConstant ? 0 : 1, popupOptions, popupStyle) == 0;
    33.             EditorGUIUtility.AddCursorRect(btnRect, MouseCursor.Arrow);
    34.             entry.Property.Children[value.useConstant ? "constantValue" : "variable"].Draw(label);
    35.          
    36.             GUILayout.EndHorizontal();
    37.         }
    38.     }
     
  30. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Hey, I've tried reproducing the issue and other than it selects everything on the first click - which is something that unity just does - I can't reproduce your issue. Would you be willing to report an issue over on the issue-tracker, with some more information, a screenshot of the inspector, which unity version you are using etc..

    Glad to hear you are looking forward to the editor window features! I can't wait for it either :D If you have any feature-requests or thoughts, I'm all ears :)
     
  31. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
  32. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    When I simply want to display something in the inspector I usually use the OnInspectorGUI attribute on a private method wrapped in #if UNITY_EDITOR, and draw everything I need in there. You can even put attributes like InfoBox on it, and change the order of it using PropertyOrder. Also if you do this in an empty class, it will also remove the message, since Odin now has something to draw.
    We'll improve upon that message for sure, we've gotten a lot of similar questions regarding it lately.
     
  33. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    In your case, I would actually do it exactly like that - you could make it static - but beyond that, it is actually good practice to initialize GUIStyles as soon as you use them and not before that, in some static constructor or otherwise. GUIStyles may only be instantiated from unity's thread, so doing it in a constructor, could easily give you an error.

    There is also the entry.Context(this, "some key", someValue), that gives lets you store values tied to specific properties. We very often use these to store whether a foldout is collapsed or expanded etc...
     
    doq likes this.
  34. Stroved

    Stroved

    Joined:
    Jan 3, 2014
    Posts:
    16
    I've created the report: https://bitbucket.org/sirenix/odin-inspector/issues/233/textarea-selects-all-text-everytime

    The only suggestion I have is to add screenshots with the documentation. Its nice seeing exactly what the code does, but sometimes I'm looking for specific functionality and a screenshot will help do that quickly :)
     
    bjarkeck and doq like this.
  35. Korindian

    Korindian

    Joined:
    Jun 25, 2013
    Posts:
    584
  36. EETechnology

    EETechnology

    Joined:
    Aug 15, 2015
    Posts:
    185
    Hello!


    Thanks for the quick reply!
    When I try to use an empty function with OnInspectorGUI and InfoBox, the InfoBox is always shown, even if the visibleIfMember is false

    Code (CSharp):
    1.      
    2. [OnInspectorGUI, InfoBox("The rope", "BrokenRope")]
    3.         private void ShowErrorBox() {
    4. }
    5.  
    Further more, how can I draw the InfoBox using a function in OnInspectorGUI? Is there a way to do that or is everything obfuscated?

    Thanks!
     
  37. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    301
    Ah, We'll take a look at that, thanks.

    Yeah, Check out the SirenixEditorGUI, GUILayout and EditorGUILayout classes. SirenixEditorGUI has the method for drawing a message-box.
     
  38. doq

    doq

    Joined:
    Aug 17, 2015
    Posts:
    121
    @bjarkeck In the ReflectionExample, the property.LastDrawValueRect check on line 105 is (x:0.00, y:0.00, width:0.00, height:0.00), so clicking on a property never contains the mousePosition.
     
  39. jorisshh

    jorisshh

    Joined:
    Oct 6, 2009
    Posts:
    1,522

    Ho ho ho

    Tis the season of Christmas surprises, and we have an awesome one for you!

    Sponsored by all your favorite Unity Asset Store developers, Nordic Game Jam, Pocket Gamer Connects, and co-hosted by Game Analytics, we (Joris and I – Devdog) are launching the second edition of our yearly Christmas Giveaway Calendar for all Unity game developers!

    You can already now sign up right here.


    So what’s this all about?
    For the past weeks, we’ve been collecting sponsored gifts related to Unity (asset vouchers, product keys, conference tickets etc.), and throughout each day of December leading up to Christmas Day on the 25th, we will be sending out these sponsored gifts as early gamedev Christmas presents via e-mail to hundreds of lucky winners.

    The total prize pool is at $35,000, with over 1200 presents donated by the awesome sponsors!


    Merry Christmas from Devdog, and every single one of the sponsors.

     
    bjarkeck likes this.
  40. doq

    doq

    Joined:
    Aug 17, 2015
    Posts:
    121
    Is it possible to serialize references to interfaces with Odin? Say I have an interface IHandler that want my MonoBehaviours and ScriptableObjects to implement, and I want to be able to store references to them in another ScriptableObject. The references don't persist between application runs in the ScriptableObject even though things seem to work in the editor.

    Edit: After some more research, I think I'm approaching things the wrong way. Basically, I want to create a Drawer kind of like UnityEvent where I can drop in a reference to a GameObject or ScriptableObject and go through the Components to select a method and store a reference to that in a ScriptableObject. So my guess is I have to drop the idea of using an interface, and store a UnityEngine.Object reference in my ScriptableObject, but how do I get a reference to a method? I'm guessing I have to use reflection to store the method name, and at runtime call the reference's Type.InvokeMember function.

    Edit2: I think I found out why things aren't working. Since the GameObject I'm getting a reference to is serialized in the Scene it doesn't have a fileid, so the ScriptableObject can't save a reference to it.

    Edit3: I think I need to used ExposedReference and IExposedPropertyTable to resolve references to Scene objects.
     
    Last edited: Dec 2, 2017
  41. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    Hi,
    I have a problem and I am not sure if it is a bug in Odin or if there is some kind of setting how to solve it:
    I have a custom class with a custom property drawer.
    As long as Odin is not in the project, everything works fine. But when import odin, My property Drawer is replaced with the one of Odin inspector :(

    Edit: I just found out what seemed to cause it:
    My custom data was extending IDictionary<K, V>...
    However, after commenting that out I sometimes have the inspector as it have been before and sometimes my ReorderableList throws always NullReferenceExceptions on GetHeight() (which it never did before):
    NullReferenceException: SerializedObject of SerializedProperty has been Disposed.
     
    Last edited: Dec 1, 2017
  42. EETechnology

    EETechnology

    Joined:
    Aug 15, 2015
    Posts:
    185
    Hey!

    How do I draw custom stuff on SceneGUI. I tried creating an Editor Script, but it makes everything in the inspector ugly again :-(

    Thanks in advance!
     
  43. Bulwark-Studios

    Bulwark-Studios

    Joined:
    Sep 20, 2013
    Posts:
    18
    Hi!

    I would use the Odin serializer to save and load data in a file at runtime of SerialzedScriptableObject or anything that can be serialized.
    I didn't dig enought in the source code to understand the serializer engine yet. I just tried to use the SerializationUtility.SerializeValue and SerializationUtility.DeserializeValue methods.

    This is what i'm using:
    Code (CSharp):
    1. byte[] bytes = SerializationUtility.SerializeValue(t1.baseData, DataFormat.JSON);
    2. Debug.Log(System.Text.Encoding.UTF8.GetString(bytes));
    3. // Save to a file code here
    4.  
    5. // After a reboot of the app
    6. // Extract bytes from a file code here
    7. DataBase data = SerializationUtility.DeserializeValue<DataBase>(bytes, DataFormat.JSON);
    8. Debug.Log(data);
    The json is printed in the console, but I have warnings when I try to serialize UnityEngine.Object.* objects:
    In this case DataBase is a ScriptableObject.
    When I want to deserialize the json I am catching an ArgumentNullException in UnitySerializationUtility.DeserializeUnityObject.

    The serialization and deserialization works fine on non-Unity objects.
    Is there a way to serialize and deserialize Unity objects at runtime?

    Thanks
     
    Last edited: Dec 4, 2017
  44. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    I can answer my own question now:
    In Plugins/Sirenix/Odin Inspector/Config/Editor/InspectorConfig it is possible to prevent Odin from using odin as inspector for certain types.

    However: I would like to do it programatically (Asset development). Is it possible with an Attribute or something?
     
  45. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Indeed you cannot reference scene objects from assets. This isn't something we're ever likely to implement, either, as Unity just isn't designed to do that.

    I would be grateful if you could post some replication code with your custom property drawer and the type it draws, so I can see why Odin's backwards compatibility systems apparently didn't work sufficiently well for it.

    As for ways to programatically disable Odin for certain types, there is no attribute to do that, but Odin will not touch anything that already has a custom editor, so you can simply create an empty (IE default) Unity-style custom editor for your class, to force it to draw regularly.

    You can inherit your custom editor script from OdinEditor instead of Unity's Editor - then the default inspector rendered by your editor will be Odin's rather than Unity's.

    It is indeed not possible to serialize Unity objects. Odin can serialize and recreate their contents, but not the object instances themselves. Because of how Unity handles Unity objects, there is no reliable way of doing this, so we simply don't do it at all, to indicate that an alternative should be sought out lest very strange things start happening. You should instead make a non-Unity-object data storage class, and serialize that instead.
     
  46. tyapichu

    tyapichu

    Joined:
    Aug 11, 2013
    Posts:
    11
    Sorry if doubling qouestion. Can i somehow create an editable list of objects whith field of dictionari type?
     
  47. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    I'm afraid I'm not sure what you mean, exactly. Could you clarify a bit?
     
  48. Shnayzr

    Shnayzr

    Joined:
    Aug 31, 2014
    Posts:
    18
    I found a gamebreaking bug (I think)

    I have a ScriptableObject with dictionaries inside. I can easily change the values in the inspector, but the problem is when if I modify the script, the dictionaries reset to 0 values.

    Is this a known bug? If so, is there any workaround? This is the main reason why I bought Odin Inspector.
     
  49. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    188
    Hi, this is a common misunderstanding people have with Odin - values being shown does not necessarily mean they are being serialized and persisted. Please refer to this FAQ answer for further details.
     
  50. tyapichu

    tyapichu

    Joined:
    Aug 11, 2013
    Posts:
    11
    Sorry. I'm trying to understand what can I do whith Odin whithout diving deeply.
    For example. (UPD 2: problem solved)
    Code (CSharp):
    1. public class newSctipt : SerializedMonoBehaviour {
    2.  
    3.     public List<MainClass> mainList; //doesn't show Dictionaris and Interface fields of MainClass and Interface field of SecondClass
    4.     public Dictionary<StateEnum, IInterfaceController> controllerDict; //works correctly
    5.     public Dictionary<StateEnum, SecondClass> secondDict; //works mostly correctly.. doesn't show Interface field while creating new key value which worked in controllerDict
    6. }
    7.  
    8. [Serializable]
    9. public class MainClass
    10. {
    11.     public StateEnum state;
    12.     public string str;
    13.     public List<SecondClass> secondList;
    14.     public int num;
    15.     public Dictionary<StateEnum, string> dict;
    16.     public Dictionary<StateEnum, SecondClass> secondDict;
    17.     public IInterfaceController controller;
    18. }
    19.  
    20. [Serializable]
    21. public class SecondClass
    22. {
    23.     public StateEnum state;
    24.     public string str;
    25.     public IInterfaceController controller;
    26. }
    27.  
    28. public enum StateEnum
    29. {
    30.     State1,
    31.     State2,
    32. }


    UPD 1: I feel a lack of screenshots in documentation. For example it would be great to show and describe Add function of AssetList. May be it doesn't fit enough to describe "A basic generic drawer" but shows Odin basic functions great way.

    UPD 2: I solved my problem by using http://sirenix.net/odininspector/faq?Search=Serializable#faq [OdinSerialize]. I think it would be even better if you add "using Sirenix.Serialization;" line to your code sample in faq. But it's still hard to understand dependenses between serialization atributs.
     
    Last edited: Dec 7, 2017