Search Unity

  1. Check out the Unite LA keynote for updates on the Visual Effect Editor, the FPS Sample, ECS, Unity for Film and more! Watch it now!
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  4. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  5. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

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

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

  1. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    Wow you guys never cease to amaze me. Every time I wonder if I can do something with Odin the answer is Yes!
     
    bjarkeck likes this.
  2. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    Ok so I have run into one issue with this. I have a base class where these functions live, and derived classes that expand upon the list of required items. The validation function doesn't change, so I opted to put it in the base class to avoid duplicate code.

    However, when the validate runs it appears to only be running on the base class.

    BASE CLASS (HardwareComponent)
    Code (CSharp):
    1. using UnityEngine;
    2. using Sirenix.OdinInspector;
    3. using System.Collections.Generic;
    4. using System;
    5.  
    6. public abstract class HardwareTemplate : SerializedScriptableObject
    7. {
    8.     public string Name;
    9.     [TextArea]
    10.     public string Description;
    11.     [ShowInInspector]
    12.     [InfoBox("$MissingProperties", InfoMessageType.Error, "MissingRequiredProperties")]
    13.     public List<PhysicalProperty> Properties;
    14.     public GameObject Model;
    15.     public Transform AttachTop;
    16.     public Transform AttachBottom;
    17.     public Transform AttachSide;
    18.  
    19.     protected List<string> requiredProperties;
    20.  
    21.     public HardwareTemplate()
    22.     {
    23.         this.requiredProperties = new List<string>();
    24.         this.requiredProperties.Add("Mass");
    25.     }
    26.  
    27.     private bool MissingRequiredProperties()
    28.     {
    29.         Debug.Log(string.Format("Required Properties: {0}", string.Join(",", requiredProperties.ToArray())));
    30.         Debug.Log(this.GetMissingProperties());
    31.         return !string.IsNullOrEmpty(this.GetMissingProperties());
    32.     }
    33.  
    34.     private string GetMissingProperties()
    35.     {
    36.         if (this.Properties == null || this.Properties.Count == 0)
    37.             return string.Join(",", requiredProperties.ToArray());
    38.  
    39.         List<string> missingProperties = new List<string>();
    40.         foreach (string propertyName in requiredProperties)
    41.         {
    42.             bool found = false;
    43.             foreach (PhysicalProperty prop in this.Properties)
    44.             {
    45.                 if (string.Equals(prop.Name, propertyName, StringComparison.InvariantCultureIgnoreCase))
    46.                 {
    47.                     found = true;
    48.                 }
    49.             }
    50.             if (!found)
    51.             {
    52.                 missingProperties.Add(propertyName);
    53.             }
    54.         }
    55.         if (missingProperties.Count > 0)
    56.         {
    57.             return string.Join(",", missingProperties.ToArray());
    58.         }
    59.  
    60.         return string.Empty;
    61.     }
    62. }
    DERIVED CLASS (LiquidFuelEngineTemplate)
    Code (CSharp):
    1. using UnityEngine;
    2. using Sirenix.OdinInspector;
    3. using StellarTrail;
    4. using System.Collections.Generic;
    5. using System;
    6.  
    7. [CreateAssetMenu(menuName="Template/Liquid Fuel Engine")]
    8. public class LiquidFuelEngineTemplate : HardwareTemplate
    9. {
    10.     public PropellantMixture Propellant;
    11.  
    12.     public LiquidFuelEngineTemplate() : base()
    13.     {
    14.         string[] requiredProperties = { "Diameter", "Specific Impulse SL", "Specific Impulse Vac", "Thrust SL" };
    15.         this.requiredProperties.AddRange(requiredProperties);
    16.     }
    17.  
    18. }
    When I look at the debugs, I am only seeing the required property list from the base class.
     
  3. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    Hmm ok looks like this is more my problem. I thought the constructor of the classes would be called, but it turns out that they aren't.
     
  4. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    221
    Thanks for the code, looks like a fun project :)

    It's likely because you are only populating the requiredProperties list in the constructor. Constructors in classes derived from UnityEngine.Object are weird.

    You could perhaps change it up a bit to something like this:

    Code (CSharp):
    1.  
    2. public class HardwareTemplate : SerializedScriptableObject
    3. {
    4.     private string[] requiredProperties;
    5.  
    6.     private string[] RequiredProperties
    7.     {
    8.         get
    9.         {
    10.             if (this.requiredProperties == null || this.requiredProperties.Length == 0)
    11.             {
    12.                 List<string> requiredProperties = new List<string>();
    13.                 requiredProperties.Add("Mass");
    14.                 foreach (var rp in this.GetAdditionalRequiredProperties())
    15.                 {
    16.                     requiredProperties.Add(rp);
    17.                 }
    18.                 this.requiredProperties = requiredProperties.ToArray();
    19.             }
    20.  
    21.             return this.requiredProperties;
    22.         }
    23.     }
    24.  
    25.     protected virtual IEnumerable<string> GetAdditionalRequiredProperties()
    26.     {
    27.         yield break;
    28.     }
    29. }
    30.  
    31. public class LiquidFuelEngineTemplate : HardwareTemplate
    32. {
    33.     protected override IEnumerable<string> GetAdditionalRequiredProperties()
    34.     {
    35.         yield return "Diameter";
    36.         yield return "Specific Impulse SL";
    37.         yield return "Specific Impulse Vac";
    38.         yield return "Thrust SL";
    39.     }
    40. }
    41.  
    Edit 1: Forgot "Mass"

    Edit 2: If you added [NonSerialized] to the required requiredProperties, Then I think your example would work. Unity does call the constructor before starting to deserialize the object, but it's overridden with the serialized value afterward. If you added the NonSerialized to it, it will completely ignore that property, and the values will stay the same from when it was constructed.
     
    Last edited: Jul 15, 2017
  5. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    221
    Also doesn't this need to be $GetMissingProperties?
     
    Last edited: Jul 15, 2017
  6. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    221
    Yeah exactly! I was too slow :p. A good rule is to stay away from constructors in classes derived from UnityEngine.Object
     
    Last edited: Jul 15, 2017
  7. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    Ahh excellent. Adding the NonSerialized attribute fixes it perfectly!
     
  8. EmeralLotus

    EmeralLotus

    Joined:
    Aug 10, 2012
    Posts:
    1,306
    I'm looking for a runtime serialize and deserialize solution. Is this feature included. If not is there future plans for supporting this functionality.

    Cheers
     
  9. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    170
    Odin does indeed includes a system that can serialize and deserialize your data at runtime. You can read more here.
     
  10. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    Yep it does. Its at least as good as Unity's built in system, and better in some cases. If you just need serialization and don't care about it being human readable, it works well. If however you want human readable and human editable/creatable serialization, then it falls a bit short depending on use case. I'm still trying to determine if it is going to work for me or not.
     
    bjarkeck likes this.
  11. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    221
    If you want some tips on how to improve human readability using Odin, feel free to share some scripts, and requests. Would love to show a few tips as we've also done in the past. It also helps us a lot us to see actual use-cases for Odin, and what people might actually need, instead of us just guessing
     
  12. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    I am planning on sharing something with you guys in regards to this, but I don't have all my ducks in a row quite yet and I want to be comprehensive :)
     
  13. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    170
    It's true, yeah, the node format that's in the editor, as it currently stands, can quickly get very confusing when looked at in the data files, due to it still closely mimicking how the serialized data is structured and written/read. I've pondered creating a new format that's a lot more human readable and editable when saved in Unity's text format (and would also handle merging far more robustly), but something like that is probably a bit into the future still, as we've got a lot of things on our plates. It's definitely a thing I think we ought to have, though.
     
  14. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    Actually the Node format is the closest to being what I am looking for. It isn't perfect and as you say can get very confusing, but it is at least conceivable that it could be edited by hand if you knew what you were doing. By contrast the JSON output is text, but the entire thing is simply encoded as an ascii string making it impossible to hand edit. Also the Node format, while semi-usable, is only available in the editor and not in runtime builds.

    We are looking at our data on disk from the perspective of allowing modders to edit it or create new things outside of the editor, and were hoping to use a more user friendly JSON model. Right now the only option I see is runtime stamping from a separate JSON definition.

    I think the main issue comes from polymorphism. And like I said, I need to dig into it more and get all my thoughts clear so I can communicate it properly.
     
  15. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    170
    Actually I could put in a hard-coded case for the json format so it doesn't output itself as a base64 encoded string, but as a proper json string. It would still just be on one line in the asset file, though, due to how Unity does things.

    However, if you're doing more "manual" serialization/deserialization of stuff to/from your own files, you can definitely achieve perfectly human-readable json with Odin, like demonstrated in this post.
     
  16. stevenatunity

    stevenatunity

    Joined:
    Apr 17, 2015
    Posts:
    92
    Hiya, I've just bought Odin (literally within the last ten minutes!) and I wonder what's the best way to add an image?

    For example, I'll show an Android image and then have my Android settings below it.

    Thanks :)
     
  17. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    221
    Yo Steven, Good to hear, and hope it will save you a lot of time :)

    There are a few ways you can do this.

    You could use the OnInspectorGUI attribute like so, and maybe spice it up with some groups.
    t.png

    Or you could create your own little ImageHeader attribute if it is something you were planning to use a lot.
    tt.png

    Here is the code for the attribute and drawer:

    Code (CSharp):
    1. public class ImageHeaderAttribute : Attribute
    2. {
    3.     public string AssetPath { get; private set; }
    4.  
    5.     public ImageHeaderAttribute(string assetPath)
    6.     {
    7.         this.AssetPath = assetPath;
    8.     }
    9. }
    10.  
    11. [OdinDrawer]
    12. [DrawerPriority(0.2, 0, 0)]
    13. public class ImageHeaderAttributeDrawer : OdinAttributeDrawer<ImageHeaderAttribute>
    14. {
    15.     protected override void DrawPropertyLayout(InspectorProperty property, ImageHeaderAttribute attribute, GUIContent label)
    16.     {
    17.         PropertyContext<Texture> texture;
    18.  
    19.         if (property.Context.Get(this, "texture", out texture))
    20.         {
    21.             texture.Value = AssetDatabase.LoadAssetAtPath<Texture>(attribute.AssetPath);
    22.         }
    23.  
    24.         GUILayout.Label(texture.Value, EditorStyles.centeredGreyMiniLabel);
    25.  
    26.         this.CallNextDrawer(property, label);
    27.     }
    28. }
     
  18. stevenatunity

    stevenatunity

    Joined:
    Apr 17, 2015
    Posts:
    92
    Perfect, thank you :)
     
  19. zsaladin

    zsaladin

    Joined:
    Jan 20, 2015
    Posts:
    11
  20. Froghuto

    Froghuto

    Joined:
    Oct 4, 2012
    Posts:
    44
    Is there a way to find out if a specific FoldoutGroup / TabGroup etc is open/visible/active outside of the normal editor gui context in i.e. OnDrawGizmosSelected()?
     
  21. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    170
    Ah, yeah, I'm afraid that currently Odin's buttons can't take arguments properly. However, this is something that we can add to the todo list, as it should be a relatively straight-forward addition. Hopefully it'll make, not the next patch that we just submitted (1.0.4.0), but the patch after that, 1.0.5.0.

    There isn't currently a built-in way to do that. We do have an upcoming feature that should make it out in a patch soon, namely that of UI context persistence (so which tabs and foldouts in your objects are open is persistent between selections and even editor restarts). This system would provide an API to let you query that data (even when the object is not selected, should you wish!), so you could easily do it once that is out.

    Meanwhile, something more hacky would be in order. You could just have the UI invoke certain methods that can keep track of whether it's open or not. Here's some example code that shows how you could keep track of foldouts in a variable (that you could then access from anywhere):

    Code (CSharp):
    1. using Sirenix.OdinInspector;
    2.  
    3. public class ExampleScript : MonoBehaviour
    4. {
    5.     [OnInspectorGUI, PropertyOrder(int.MinValue)]
    6.     private void ResetSelectedFoldout()
    7.     {
    8.         // Reset at the start of each GUI frame
    9.         this.openedFoldouts = "None";
    10.     }
    11.  
    12.     [ReadOnly, DisplayAsString]
    13.     public string openedFoldouts;
    14.  
    15.     [FoldoutGroup("Foldout One")]
    16.     public string foldoutOne;
    17.  
    18.     [FoldoutGroup("Foldout Two")]
    19.     public string foldoutTwo;
    20.  
    21.     [FoldoutGroup("Foldout Two/Foldout Three")]
    22.     public string foldoutThree;
    23.  
    24.     [OnInspectorGUI, FoldoutGroup("Foldout One")]
    25.     private void UpdateFoldoutOne()
    26.     {
    27.         // Will only run when foldout one is open
    28.         if (this.openedFoldouts == "None")
    29.         {
    30.             this.openedFoldouts = "Foldout One";
    31.         }
    32.         else
    33.         {
    34.             this.openedFoldouts += ", Foldout One";
    35.         }
    36.     }
    37.  
    38.     [OnInspectorGUI, FoldoutGroup("Foldout Two")]
    39.     private void UpdateFoldoutTwo()
    40.     {
    41.         // Will only run when foldout two is open
    42.         if (this.openedFoldouts == "None")
    43.         {
    44.             this.openedFoldouts = "Foldout Two";
    45.         }
    46.         else
    47.         {
    48.             this.openedFoldouts += ", Foldout Two";
    49.         }
    50.     }
    51.  
    52.     [OnInspectorGUI, FoldoutGroup("Foldout Two/Foldout Three")]
    53.     private void UpdateFoldoutThree()
    54.     {
    55.         // Will only run when foldout three is open
    56.         if (this.openedFoldouts == "None")
    57.         {
    58.             this.openedFoldouts = "Foldout Three";
    59.         }
    60.         else
    61.         {
    62.             this.openedFoldouts += ", Foldout Three";
    63.         }
    64.     }
    65. }
     
    Froghuto likes this.
  22. Bamboy

    Bamboy

    Joined:
    Sep 4, 2012
    Posts:
    35
    Hi. How can I suppress this warning? I am using AsyncOperation for my scene transitions.
     
  23. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    170
    Yet another Unity type with weird, undocumented inspector behaviour, it seems. We have a huge set of special cases for dealing with the weirdness of many Unity types in the inspector, and you just found a new one. I've added another special case for this one - thanks.

    Meanwhile, to make it go away, you can do the following: somewhere in an Odin-serialized class, you have a public AsyncOperation field, or a private AsyncOperation field decorated with a [ShowInInspector], [SerializeField] or [OdinSerialize] attribute. You merely have to make sure that this field is not shown in the inspector - for example by removing any [ShowInInspector] attributes and marking it with [NonSerialized].
     
    Bamboy likes this.
  24. Bamboy

    Bamboy

    Joined:
    Sep 4, 2012
    Posts:
    35
    Any plans to let us copy/paste more varieties of values?
    Advanced Inspector on the asset store lets you copy/paste vector values among other things, and it was the main other asset I was looking at getting instead of Odin. Ended up choosing Odin because it seemed more professional.
     
  25. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    170
    You should be able to copy paste practically anything in the inspector just by right-clicking on the property and pressing "Copy" and "Paste" (paste will be greyed out if your clipboard is empty, or if the type of the value in your clipboard is incompatible with the property you'd be pasting to) - certainly you can copy paste Vector3 values. For Odin-serialized values, you can even choose between copying the reference, or doing a shallow or a deep copy (deep copy is the default).
     
    Last edited: Jul 19, 2017
  26. Lysander

    Lysander

    Joined:
    Feb 24, 2013
    Posts:
    1,497
    Hello again. Just wondering if Odin includes, or if you plan on including, any solution for custom object pickers (that selection screen that pops up when trying to select an asset for a reference). I need to be able to limit the scope of the objects being selected based on various criteria, and preferably be able to display them in the list using custom names and/or icons.

    I'm currently using some tools that came with Inventory Pro, but as they were intended to be used in custom inspectors and editor windows I'm getting some weird bugs when trying to force them to apply to the non-Editor side (through attributes/property drawers). Namely, if you're curious, my problem is that property drawers don't seem to do anything for references currently set to 'null'- the custom object picker only shows up if you've already got an object picked, so to speak. Ick.

    So yeah, I'd much prefer to use something with Odin to get the job done, if such a thing exists. Or, if you have any advice on my current object picker problem (maybe null object references not being drawn with property drawers is a common problem that you all have already solved?), I'd be extremely grateful for any help or advice, as my searches have turned up nothing.

    Thanks!
     
    Last edited: Jul 19, 2017
  27. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    170
    The fairly primitive object picker we have currently is sort of a stopgap measure while we're building the Real Deal™. We're in the midst of developing a proper object picker that can do everything from custom logic for which objects to pick, where to pick them from, constructor handling, and so on. It's a big feature, though, and has some very complex UI, so we're taking our time to get it right. Shouldn't be more than a month or two, though, if all goes well.

    Currently, null values do not get drawn by their respective property drawers, but stop at the NullableReferenceDrawer, which handles drawing of all null values. We can't actually use a "standard" property drawer to draw a null value, as there is no value to draw, if that makes sense. The average property drawer would just throw a bunch of exceptions, so to protect from that, we cut things off there.

    The one exception is types derived from UnityEngine.Object, which is what I gather your case is - custom drawers for UnityEngine.Objects than can handle the value being null, wouldn't currently actually be drawn before the value is not null any more. This sort of problem will also be remedied in the future, as we're also going to be doing a rewrite of our property and drawer system internals so that it is more flexible and can handle such cases better.

    For now, if you have a property drawer that you want to override the standard null behaviour in Odin (such as Inventory Pro's custom object picker drawer), you can assign it a higher priority than the NullableReferenceDrawer. Simply apply a [DrawerPriority] attribute with a priority higher than (0, 0, 2000) to it, and that drawer will be invoked before NullableReferenceDrawer, and can handle the drawing on its own. (0, 0, 2001) should do it. And of course, you can always inspect the chain of drawers for any member by applying the [ShowDrawerChain] attribute to it, so you can see which drawers get invoked in which order.

    I hope I understood your problem correctly, and that this was useful. If not, then I'm afraid you'll need to clarify some more.
     
    Lysander likes this.
  28. Lysander

    Lysander

    Joined:
    Feb 24, 2013
    Posts:
    1,497
    Using the DrawerPriority attribute on the property drawer worked beautifully, thank you very much. I look forward to seeing your solution for custom object pickers when it gets here.
     
  29. Bamboy

    Bamboy

    Joined:
    Sep 4, 2012
    Posts:
    35
    Ah. You can't do this with transform values though.
     
  30. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    170
    Ah, indeed not. That's because it is Unity drawing the Transform, not Odin. Odin does not properly support drawing Unity's own types - which is why they are all turned off in the Editor Types preferences by default.

    This was a very conscious choice we made - plugins like Advanced Inspector have had no end of trouble trying to maintain compatibility with all of the various Unity types. We experimented with how to automatically support them all, and ended up acknowledging that it was impossible to do it even remotely reliably without custom-writing support for each type like Transform, Rigidbody, etc. (which is what Advanced Inspector does, and which is also why Advanced Inspector breaks inspectors so often for Unity's types when new Unity patches roll out). We didn't want to accept such an enormous maintenance burden in the long term, so we opted to disable Odin for all Unity's types by default. A necessary tradeoff, we're afraid.
     
  31. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    525
    Hello there,

    I bought Odin a few days ago and I'm really happy with my decision. There are a few things I found on the way that I want to point out, to make the product even better:

    1.) Documentation Typo:
    http://sirenix.net/odininspector/documentation/sirenix/odininspector/showifattribute
    Should have the attributes [ShowIf("ShowProperties")] to be valid.

    2.) Typo:
    upload_2017-7-22_17-4-12.png

    In the Preferences for lists there is an e missing in "Number Of Items Per Page". I would also prefer to have not every word starting with a capital letter (I guess you are using your variable names).

    3.) Indentation for horizontal groups:
    When I use a horizontal group like this:
    upload_2017-7-22_17-9-57.png

    I get an indentation of the group compared to the other parameters:
    upload_2017-7-22_17-10-57.png

    I don't quite understand why, how to disable that, because the standard value for margin left seems to be 0 pixels.

    4.) I don't quite know how to sort buttons in a horizontal group. They strangely have different orders with the same script on different game objects. Here is an example of how I put the buttons:
    upload_2017-7-22_17-14-15.png

    5.) I have the problem that CustomPropertyDrawers from other projects are discarded with Odin. For example the button attribute from Bitstrap. Any idea how I can solve that problem?

    Looking forward in digging deeper into the possibilities Odin has to offer :)
     
  32. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    221
    Hi Johannski,

    Thanks for the heads-up on the typos, and thank you for the screenshots!

    I've marked GeneralDrawerConfig.Instance.NumberOfItemsPrPage as obsolete and it now tells you to use the correctly spelled member instead.

    Have you updated to the latest version 1.0.4.x, that came out a few days ago? We've made a lot of changes and fixes to HorizontalGroup.

    http://sirenix.net/odininspector/releasenotes/1-0-4-0_to_1-0-4-1

    I've been testing cases exactly like yours, trying to get rid of all spacing related issues. It's still bad in some cases, but over all much better. I'll continue to try and improve upon it for sure. Unity's GUI system can be a pain sometimes.

    I tried your example, and it looks fine on my end using the 1.0.4.1 patch. I've also tweaked it a bit with a bunch of suggestions.

    fixed.png

    If you end up in situations where HorizontalGroup doesn't quite align things. A little hack is to specify negative in its PaddingLeft and PaddingRight properties to push things around.

    While methods are not properties in c#, they are properties in our PropertyTree which draw the inspector. So you can use the PropertyOrder attribute like this:

    Code (CSharp):
    1.     [Button, PropertyOrder(1)]
    2.     private void First() { }
    3.  
    4.     [Button, PropertyOrder(2)]
    5.     private void Second() { }
    In the new 1.0.4.0 patch, there will no longer be a gap between your horizontal boxes, so you when you upgrade you'll need to use margin manually to make that gap.

    I played a bit around with your script here as well, showcasing the new HorizontalGroup features :)

    ta.png

    Hope that it is in fact because you are on an older Odin version. Otherwise, I'll need to investigate a bit. In that case what version of Unity are you running?
     
    Last edited: Jul 22, 2017
  33. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    I'm having a weird issue where one ONE specific class in my project, Odin doesn't appear to be working. It is extremely weird. I figure I'm doing something stupid, but comparing the decorations to another class, it seems the same, yet one is working and the other is not.

    I have decorations for showing things in the inspector, as well as to show buttons but none of it is actually working. The inspector for the component is completely blank.

    This is just a skeleton base class, so I'm going to include ALL the code here. There must be something stupid I am doing? (And if history is any teacher I will probably figure out what I did wrong 5 minutes after posting this lol)

    Code (CSharp):
    1. using System;
    2. using Sirenix.OdinInspector;
    3. using UnityEngine;
    4.  
    5. class SimulationModel : SerializedMonoBehaviour, ISimulatedObject
    6. {
    7.     private Action onShutdown;
    8.     [SerializeField]
    9.     [HideInInspector]
    10.     private HardwareTemplate dataTemplate;
    11.     private new Rigidbody rigidbody;
    12.     private bool IsSimulating;
    13.     private MassProperty mass;
    14.  
    15.     public Action OnShutdown
    16.     {
    17.         get
    18.         {
    19.             return this.onShutdown;
    20.         }
    21.         set
    22.         {
    23.             this.onShutdown = value;
    24.         }
    25.     }
    26.     [ShowInInspector]
    27.     public HardwareTemplate DataTemplate
    28.     {
    29.         get
    30.         {
    31.             return this.dataTemplate;
    32.         }
    33.  
    34.         set
    35.         {
    36.             this.dataTemplate = value;
    37.         }
    38.     }
    39.  
    40.     [Button]
    41.     public void Shutdown()
    42.     {
    43.         this.onShutdown.Invoke();
    44.         this.IsSimulating = false;
    45.     }
    46.     [Button]
    47.     public void Startup()
    48.     {
    49.         this.IsSimulating = true;
    50.     }
    51.  
    52.     public void Awake()
    53.     {
    54.         this.rigidbody = this.GetComponent<Rigidbody>();
    55.         this.IsSimulating = false;
    56.         this.mass = this.dataTemplate.GetPropertyNamed("Mass") as MassProperty;
    57.         if (this.mass != null && this.rigidbody != null)
    58.         {
    59.             this.rigidbody.mass = this.mass.Kilogram;
    60.         }
    61.     }
    62.  
    63.     public void FixedUpdate()
    64.     {
    65.         if (!this.IsSimulating)
    66.         {
    67.             return;
    68.         }
    69.  
    70.         // Update Mass
    71.         if (this.mass != null && this.rigidbody != null)
    72.         {
    73.             this.rigidbody.mass = this.mass.Kilogram;
    74.         }
    75.     }
    76. }
    77.  
     
  34. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    221
    Haha, I must be quick then.

    You haven't specified any access modifier for the class and Internal is the default.

    All the generated editors live in a separate assembly, so we can't reference internal classes from there.

    You need to either make it a public class or enable the Odin Inspector manually for the class like so:

    unnamed.png
     
  35. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    Yep like Is aid, stupid mistake :p

    /facepalm
     
    bjarkeck likes this.
  36. WendelinReich

    WendelinReich

    Joined:
    Dec 22, 2011
    Posts:
    185
    Hej Tor, I've been googling run-time serialization for polymorphous data all morning, eventually stumbled on Odin and now this post of yours. You can't believe how exciting this is - it seems almost suspiciously simple. :D

    - So just to clarify, this could de/serialize (to JSON) a list of polymorphous interfaces like the one shown in the Release Trailer video? Basically, all taken care of in SerializationUtility.SerializeValue()?!

    - What are the limitations (apart from the fact that Unity.Object derived types arent covered)? How graceful is error handling, e.g. if a Type isnt found anymore after a game has been updated, or if a type signature doesnt matche because it has changed?

    - What happens if you encounter a reference to a Unity.Object? Is it just set to null (which I can live with) or do you throw an exception?

    - Any ETA for the documentation under here?

    Cheers!
     
  37. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    170
    Well, it is meant to be very simple to use, with the utility classes - they wrap all the regular complexity, and serialize with a default configuration. Serialization code can get a little more involved if you want to do something special, but this is very rarely the case, and SerializationUtility and UnitySerializationUtility should generally be good enough.

    Yes, that is indeed the case.

    One notable limitation is that Odin's serialization (and Odin in general) currently does not work on the Universal Windows Platform, due to it running on .NET Core, which has a very different Reflection API. A lot of Odin would have to be rewritten to support it.

    Finally, when a type goes "missing" or changes, Odin tries a few different things upon deserialization, depending on the case.
    1. We know what type was serialized, but it is not the type we expect to deserialize.

      This happens if, for example, you changed a Vector2 field to a Vector3, so the data contains a Vector2, while we are expecting a Vector3. In this case, we check if the Vector2 type can be cast to the Vector3 type. If it can, we deserialize the original Vector2 value, cast it to a Vector3, and set that.

    2. We don't know what type was serialized, because the type name has been changed, the type has been deleted, or the assembly containing the type is not loaded or accessible.

      In this case, we instead deserialize an instance of the expected type, using the data that was serialized for the former, unknown type. For example, if the saved Vector2 type has been renamed to Vector3, and Vector2 no longer exists, we will deserialize the Vector2's data into a Vector3 instance, and the x and y values will be retained.

      This, as a general rule, takes care of most types being renamed, but things are lost in the case of polymorphism, because the only thing we know in that case is what type we expect to deserialize (from the type of the field we are deserializing into) - we can't actually know which type is actually supposed to go there.
    In general, Odin does its best to preserve your data and make sure nothing is lost. You can also configure this. What I describe is Odin operating in the "resilient" mode, where it tries to reconstruct everything as best it can. You can also enable stricter modes of operation, where Odin will throw exceptions at the first sign of anything going wrong.

    A warning is logged to the console (regardless of your logging level), and upon deserialization, the object will be "fake null", IE, a destroyed Unity object. It is possible to avoid this warning by using the overloads of SerializationUtility.SerializeValue that give a List<UnityEngine.Object> as an out parameter. These internally use an external reference resolver for Unity objects, that populates a list of external references.

    That list will contain all Unity object references that were encountered during serialization. You can optionally log errors there on your own, should you like, or see which objects were accidentally included. Either way, you can then proceed to ignore this list, and pass in an empty or null list upon deserialization, or just not use any of the overloads of SerializationUtility.DeserializeValue that take such a list as argument. The values will then be "true null" upon deserialization.

    I've been so busy with many other things, dealing with issues, answering support tickets and forum questions, and so on, that the writing of the manual has suffered grievously (I am the main person responsible for this - so I am to blame). Writing that section of the manual is going to take a while more, I'm afraid. I'm currently making a concerted effort to get the Drawers in Depth section out as soon as possible.
     
    Lysander and WendelinReich like this.
  38. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    525
    @bjarkeck Wow! Thanks a lot for the superb support! I indeed was on version 1.0.3, updated and love your suggestion with ordering the direction buttons! I really wanted to do that, but wasn't sure how to do it. You just read my mind!

    The padding is working now, but I noticed that the line height is off by two pixels with horizontal groups:
    PixelPadding.png
    (Can be also seen in your screenshot).
    Just a minor thing, but still your asset seems to be on its way to perfection I thought I should point it out :)
     
    bjarkeck likes this.
  39. bjarkeck

    bjarkeck

    Joined:
    Oct 26, 2014
    Posts:
    221
    Yeah trust me, I've seen those two pixels as well haha. And thanks for the review btw, much appreciated!:)
     
  40. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    525
    Sure thing, good software deserves good reviews!

    I've stumbled upon another issue, which I is connected to Odin:
    I have multiple scenes loaded all the time. Normally, it would not be possible to reference a gameobject in another scene in a property. Now I made this small script to easily disable and enable gameobjects:
    Code (CSharp):
    1. // --------------------------------------------------------------------------------------------------------------------
    2. // <copyright file="EditorGameObjectActivation.cs" company="Supyrb">
    3. //   Copyright (c) 2017 Supyrb. All rights reserved.
    4. // </copyright>
    5. // <author>
    6. //   Johannes Deml
    7. //   send@johannesdeml.com
    8. // </author>
    9. // --------------------------------------------------------------------------------------------------------------------
    10.  
    11. using Sirenix.OdinInspector;
    12.  
    13. namespace Supyrb.Marbloid
    14. {
    15.     using UnityEngine;
    16.     using System.Collections;
    17.    
    18.     public class EditorGameObjectActivation : MonoBehaviour
    19.     {
    20. #if UNITY_EDITOR
    21.         [SerializeField]
    22.         private GameObject[] targets;
    23.  
    24.         [Button, HorizontalGroup("Buttons")]
    25.         public void Activate()
    26.         {
    27.             for (int i = 0; i < targets.Length; i++)
    28.             {
    29.                 var target = targets[i];
    30.                 if (target == null)
    31.                 {
    32.                     continue;
    33.                 }
    34.                 UnityEditor.Undo.RecordObject(target, "Activate objects");
    35.                 target.SetActive(true);
    36.             }
    37.         }
    38.  
    39.         [Button, HorizontalGroup("Buttons")]
    40.         public void Deativate()
    41.         {
    42.             for (int i = 0; i < targets.Length; i++)
    43.             {
    44.                 var target = targets[i];
    45.                 if (target == null)
    46.                 {
    47.                     continue;
    48.                 }
    49.                 UnityEditor.Undo.RecordObject(target, "Deactivate objects");
    50.                 target.SetActive(false);
    51.             }
    52.         }
    53. #endif
    54.     }
    55. }
    56.  
    And I was quite puzzled when I noticed that I could indeed add gameobjects from other scenes there. The thing is, that those connections will be lost when restarting unity.
    Just double checked, without Odin it is not possible to reference between different scenes.
     
  41. SuneT

    SuneT

    Joined:
    Feb 29, 2016
    Posts:
    41
    Pimp Your Unity Editor!
    Three months have passed since our release of Odin Inspector, and things are going well!

    But for many, Odin has a lot of untapped potential, so we thought we would try doing a "pimp my editor" day, where we show you exactly what Odin can do.

    If you have Odin, or you are just interested in learning more, feel free to share some of your code. We'll decorate it with attributes from Odin and try to make it as user-friendly as possible. Then we'll give you the pimped code back, along with some screenshots of how it ended up looking.

    Here's an example:


    We'll get to see some use-cases for Odin, and maybe get some exposure, and you'll learn a few new Odin tricks, and get some slick looking editors. If you don't have Odin, you can get a preview of what you could do with it in your project. Win win!

    Upload a script using whatever service you prefer. Screenshots of how it currently looks in your inspector would be much appreciated, and will help us reverse engineer your script if it doesn't compile.
     
  42. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    Got an odd bug for you.

    In Unity 2017.2b (I didn't test other versions), if Odin is installed and you go to Help -> About Unity, the window will com e up blank and the console spammed with errors constantly until you close the window.

    Verified in a blank project with no other assets or files. Window works fine before Odin is installed, doesn't after.

    NotSupportedException: The invoked member is not supported in a dynamic module.
    System.Reflection.Emit.AssemblyBuilder.get_Location () (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection.Emit/AssemblyBuilder.cs:241)
    UnityEditor.VisualStudioIntegration.UnityVSSupport.<CalculateAboutWindowLabel>m__1 (System.Reflection.Assembly a) (at C:/buildslave/unity/build/Editor/Mono/VisualStudioIntegration/UnityVSSupport.cs:346)
    System.Linq.Enumerable.First[Assembly] (IEnumerable`1 source, System.Func`2 predicate, Fallback fallback)
    System.Linq.Enumerable.FirstOrDefault[Assembly] (IEnumerable`1 source, System.Func`2 predicate)
    UnityEditor.VisualStudioIntegration.UnityVSSupport.CalculateAboutWindowLabel () (at C:/buildslave/unity/build/Editor/Mono/VisualStudioIntegration/UnityVSSupport.cs:346)
    UnityEditor.VisualStudioIntegration.UnityVSSupport.GetAboutWindowLabel () (at C:/buildslave/unity/build/Editor/Mono/VisualStudioIntegration/UnityVSSupport.cs:337)
    UnityEditor.AboutWindow.OnGUI () (at C:/buildslave/unity/build/Editor/Mono/GUI/AboutWindow.cs:149)
    System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222)
    Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
    System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232)
    System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115)
    UnityEditor.HostView.Invoke (System.String methodName, System.Object obj) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:283)
    UnityEditor.HostView.Invoke (System.String methodName) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:276)
    UnityEditor.HostView.OldOnGUI () (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:105)
    UnityEngine.Experimental.UIElements.IMGUIContainer.DoOnGUI (UnityEngine.Event evt) (at C:/buildslave/unity/build/Runtime/UIElements/Managed/IMGUIContainer.cs:127)
     
  43. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,018
    Hey there,

    I'm quite interested in Odin. Before I start ask specific questions, I'd like to know what's the roadmap? What should/will Odin look like in the upcoming months/releases? More specific features? More Attributes? Fancier design? :)
     
  44. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    170
    Ah, this one has cropped up again. I'll investigate if we can fix it on our end once more. The issue is that if there are dynamic assemblies loaded in your code (such as the one we emit during serialization/deserialization), before you first open the About window, then the window just implodes. It seems there's something new causing it to happen again in 2017.2; I'll check it out. Thanks for the report!

    So, I can't make any outright promises here, of course, but I can outline our future intentions in terms of "big features", provided all goes well. Emphasis on intentions. All of this is subject to change. These are not promises, but hopes. Timelines may shift, planned features may be delayed, come faster than anticipated, change in nature, or even just be canceled entirely. New features might pop up opportunistically... and so forth. You get the gist.

    Also, in addition to all of the below, you can expect us to continually polish the UI/look of Odin, as well as add more general attributes and tweak existing ones as issues come up, users come with suggestions, or we get neat little ideas.

    • 1.0.5

      - Windows Store support when you remove the serialization system properly.

      - Persistent editor context. So which foldouts are expanded, which tabs are selected, etc, can be persisted between selections, reloads, and even editor restarts.

      - User-extendable dictionary key type support, and the ability to have dictionary keys of any type, except in prefab instances (where key type support will still be limited, albeit extendable).

      - Experimental version of editor-time pre-emitting of serialization formatters for AoT platforms. Essentially, it scans your project, finds all types that need to have custom formatters emitted for them, and creates a formatter .dll for you, that will be included in AoT builds. Using this should result in faster serialization on AoT (IL2CPP) platforms, as it would eliminate most usage of reflection.

    • 1.0.6

      - New Instance Creator window. Out of the box, it will be able to either create new instances while handling things like constructors nicely, or fetch instances from anywhere - the scene, the project, the inspected object itself, or something completely custom and user-defined. If you are resolving an interface, it can find scene components or assets implementing that interface, and so on. It will be highly extendable and configurable.

      - Better asset validator. Extends the validation system so it's not limited to bulk-validating one scene at a time - it should also be able to bulk-validate project assets.

      - If the manual and documentation isn't done yet here, we're probably going to stop doing much but basic support, bugfixing and handling important issues until it is done.

    • 1.1

      - Proper installer for Odin Inspector. Installation right now may be easy, but it has a few drawbacks - namely, everybody gets the same .dll's (hence the obsolete API warnings in newer versions of Unity), there's just one configuration of Odin installed (the full one, with serialization), etc.

      This installer will be able to install Odin into your project in a wide variety of configurations (with/without serialization, etc), can give you different .dll's based on your Unity version, allow you to customize Odin before it gets integrated into the project, and also to customize parts of how it is integrated into the project. It will eventually be able to deliver patches to users faster than the Asset Store usually allows (if they opt in and ask for it). We will likely also use it to do stuff like beta releases of big patches (again opt-in), to verify that they are stable before submitting them to the Asset Store.

      - Property and drawer system internals rebuild. We're going to revamp how parts of our current property and drawer systems work, to allow us to provide many of the features or needs that our users have proven to have. The possibilities this redesign will offer are too numerous to list here, but suffice to say, it will be huge, going forward. Here's some of the sorts of things you can look forward to having or being able to do, though:

      * Lazily evaluated properties that only update when they are inspected, enabling "big data" support. This should also vastly increase editor performance in general.
      * Completely override, customize, or extend how the property system works.
      * Extend which collections the property system supports.
      * Create type "stylesheets" - external attribute sources that "style" other types.
      * Apply attributes directly to types and have them be applied to all members containing that type.
      * Inherit member attributes through the property hierarchy.
      * Completely override or customize how the attribute system works, globally or on a case by case basis.

    • The far future

      - Full Windows Store / Universal Windows Platform support, with serialization and everything. This one is out here because the timeline is highly uncertain. When this comes out depends strongly on how our initial experimental forays into automating this go. It may prove to be a lot easier, or a lot harder, than expected.

      - Ability to license Odin for use and redistribution in other Unity assets.

      - Other things, no doubt - who knows what will happen. It's definitely going to be exciting, though! We have some truly crazy ideas, but many of them, we really need to verify the possibility of before we even start talking about them.

    You'll note I didn't actually include any dates. That's because we ourselves don't actually really know, so making promises or even public guesses would likely be counterproductive. We're a small team - we only have so many resources to dedicate to planning and research of the feasibility of future features.

    In the end, this is just the order in which we hope to do things. However, I can state that I will personally be a little disappointed if 1.1 isn't out within 3-4 months.
     
  45. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    So it is more a Unity bug? Should I report it to them?
     
  46. Tor-Vestergaard

    Tor-Vestergaard

    Joined:
    Mar 20, 2013
    Posts:
    170
    Actually, we should probably create a little demo case and report it ourselves. The fix on their end is very simple, I can even tell them the exact line they have to change, and what they have to change it to. I've just been occupied, so I never got around to reporting it.
     
  47. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    357
    Well we don't have anything nearly so snazzy as that, but there is one inspector we were hoping to clean up a bit more, but I don't know if all of what we want to do is even possible.

    We have this right now:


    And ideally what we would like to do is make it so that in this class inspector only the Name property of each of those classes doesn't show up. Essentially in a perfect world we would make it so that an owning class can control what properties of a class shows up when that class is in turn a property of the owning class. If that makes any sense at all? We'd also make the filename be a popup browser. I think that can be done from some stuff I saw in the Unity docs, I just haven't gotten around to it.

    To further expand on the first bit though. We have a lot of math in our game, and it is extremely important when doing the math to ensure you are using the correct units. To this end we have built custom Property classes that are unit aware. So instead of storing something like Mass as a float or a double, it is stored as a MassProperty, which is a custom class that knows what mass is, and can give it you in say Kilograms or Grams or Pounds, etc. This makes working with it in code cleaner, since you can do something like x = Mass.Kilograms * Units.G and know at a glance what the units are.

    Now these properties are used in two different ways. One way they are used is as shown above where we have a class that has specific properties so they are declared as you would expect.

    public MassProperty Mass;

    But the other way they are used is in some more generic classes where we don't know what properties are going to be needed. In those cases they are stored in a List<PhysicalProperty>. It is in this class that the Name is important. In the former case, though, that above, the Name is not needed and just clutters the display. On that same notion, in some classes it would be nice to hide some of the extra getters on the properties. For example Length might have Meters, Kilometers, Megameters, and AU, but we might not want to always show them all in certain class and again it would be nice to hide the ones we don't need, in that particular inspector.

    Does that make sense?

    If this is something you think you would be able to do, then well first I'd be shocked, but then secondly I'd be glad to pass along a skeleton project for you to do so if you want.
     
  48. julianr

    julianr

    Joined:
    Jun 5, 2014
    Posts:
    1,056
    Hi! How easy is Odin to create a custom list with strings/int's and access that information from another script class?
     
  49. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    525
    I guess my post just got lost in the thread Mambo Jambo, I did find out that cross referencing scenes can be easily enabled and disabled through EditorSceneManager.preventCrossSceneReferences. I guess that Odin sets this value to false?! I would actually like to have this value set to true in order not to break any serialization on the way. Could you maybe add a checkbox to your settings for that? :)
     
  50. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,018
    Thank you very much for the in-depth answer. Looking forward to all the upcoming additions :)