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

Odin puts your Unity workflow on steroids, making it easy to build powerful and advanced user-friendly editors for you and your entire team.

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



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

Sign up for the mailing list
>> View on Asset Store <<
More Info

What makes Odin so great, you ask?

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

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

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

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

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

Sign up for the mailing list
>> View on Asset Store <<
More Info

Serialize Anything!

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

Oh, and in case you were wondering, it has:

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

Dictionaries

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

Note that while the serializer can serialize all dictionaries, the inspector is limited to only draw dictionaries with a primitive key type such as string, int, byte etc. The Value type can be anything you want.

  • Support all value types
  • Fully serialized just by inheriting from SerializedMonoBehaviour

Input Validation

Odin lets you easily write custom validation logic to ensure and give sensible feedback for invalid input.

  • Write custom input validation logic
  • Prevent invalid values from being set
  • Give designers useful feedback in the inspector
  • Provide custom UI messages to properties
  • Use build in attributes such as SceneObjectsOnly and AssetsOnly

…and Scene Validation

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

Powerful Lists

All arrays and lists implementing Microsofts’ IList interface are drawn by our powerful list drawer.

  • Drag and drop, insert and delete individual items
  • Cross-list and even cross-window item dragging
  • Paging for lists with many items
  • Nested list drawing
  • Fully customizable through settings and / or using attributes.

Inline Editor

Member References

Customizable Layouts

Arranging, labeling, hiding and grouping your properties helps the user to faster find what they are looking for.

  • Group properties into Tab Groups, Foldout Groups, Toggle Groups, Horizontal Groups, Button Groups and more
  • Hide or show properties based on your custom conditions
  • Custom property orderering
  • Mark properties readonly or disabled
  • Subscribe to on value changed
  • Hide or rename labels entirely
  • Display custom messages based on values
  • Easily draw custom GUI before or after properties

And much much more!..

Color Palettes

Define project-based color palettes for your hole team to share. Color properties with a ColorPalette attribute will then show a color palette right next to the color picker in one line.

  • Works with all Unity’s Color types
  • Palettes are stored as an asset, making the entire team share the same color palettes
  • Easily create new custom color palettes

Value Dropdowns

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

With Odin, the ease of dropdown selection is no longer limited to just enums. Simply reference a list and it will now give you a dropdown with values to choose from.

  • Create dropdowns for any type
  • Works with static and instance methods, fields and properties
  • Specify name for items that does not implement a proper ToString()

Asset Lists

Finding the right assets can sometimes be a hurdle in big projects with lots of files. The AssetList attribute replaces the default list drawer, with a toggle-list of all relevant assets relevant to your use case. The user can then toggle relevant assets in and out of your list.

  • Only shows assets assignable to your list type
  • Works on all asset types, including components within assets
  • Auto populate mode that automatically includes all relevant assets
  • Easily create new assets that meets your constraints
  • Filter the asset list with filters such as, asset folder location, gameobject tags, gameobject layers, name prefixes etc…

But most importantly! Odin is fully customizable

We make it easy to configure how you want the inspector to act in your project, and trivial to create new configurations for your own custom drawer types.

  • Customize the inspector to suit your needs and preferences
  • Create integrated configurations for your own drawers

To-Do List for Odin:

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

Sign up for the mailing list
>> View on Asset Store <<
More Info

  • Your friends at Devdog and Sirenix.
4 Likes

Are you going to Unite in Amsterdam next week? How about meeting up with Sirenix - the developers of Odin?

Let us know if you’ll be there - it’d be great to have a chat :slight_smile:

The latest version of Odin is out, and here’s the changelog :slight_smile:

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

Odin Inspector & Serializer v. 1.0.3.0 changelog
Highlights:

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

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

Fixes:

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

Changes:

  • Added the missing optional order parameter to HorizontalGroup

  • HorizontalGroupAttribute now has an option for a title.

  • Preferences in Window > Odin Inspector > Preferences > Drawers > General are now stored as EditorPrefs, and are therefore local to the computer.

  • Preferences in Window > Odin Inspector > Preferences > Drawers > Color Palettes are now stored as EditorPrefs, and are therefore local to the computer. This only applies to display preferences - Color Palettes themselves are still saved to the asset and synced over source control.

  • SirenixEditorGUI.BeginBox now alternates between a light and a dark box background based on its z-index.

  • Renamed SirenixEditorGUI.VerticalLineSeperator to SirenixEditorGUI.VerticalLineSeparator and HorizontalLineSeperator to HorizontalLineSeparator.

  • The Serialization Warning message for SerializedMonoBehaviour is no longer shown in the demo examples. Hopefully, this helps new users to not accidentally click the button without reading the warning first.

  • All number fields now show a small handle, which enables you to change the value by dragging. This is useful when the number field does not have a label which also has this behavior, such as in the case of list elements.

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

    This feature can be disabled in the Odin Preferences Window.

Additions:

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

  • Added the TitleGroup attribute which groups properties vertically together with a title, an optional subtitle, and an optional horizontal line.

    This can serve as an alternative to using the PropertyOrder attribute.

  • Added a very rudimentary System.Type drawer, so you can see and change type instances in the inspector.

  • Added SirenixEditorFields.LayerMaskField, which is now used to draw LayerMasks in the inspector.

  • The size of buttons can now be set via the Button attribute.

  • Added PropertyChildren.Recurse, which returns an IEnumerable that recursively yields all children of a property depth first.

  • Added an option to hide the add button for lists in the inspector via ListDrawerSettings attribute. This can be used to create custom add buttons for lists.

  • Added the DictionaryDrawerSettings attribute, which enables you to change the display mode of the dictionary. More options will be added to this attribute in the future.

  • Added SplitVertical to RectExtensions.

  • Added property drawer for char properties.

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

  • Added inspector drawer for guids, and added SirenixEditorFields.GuidField. Additionally, SirenixEditorGUI.DynamicPrimitiveField now supports Guids.

First off, thank you for this asset, it really is cool, and I’m just starting to identify how to use it best.

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

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

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

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

Heya, good to see someone using the new forum thread. :smile:

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

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

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

public class MyComponent : SerializedMonoBehaviour
{
    [SerializeField]
    [HideReferenceObjectPicker]
    private CoreActionSubAction[] actionsToTrigger;

}

public abstract class CoreActionSubAction
{
    [ToggleLeft, LabelText("$GetInspectorTitle")]
    [HorizontalGroup]
    public bool Enabled;

    private string GetInspectorTitle()
    {
        return this.GetType().GetNiceName().SplitPascalCase();
    }
}

public class TriggerAnimation : CoreActionSubAction
{
    [HorizontalGroup]
    [ToggleLeft]
    [EnableIf("Enabled")]
    public bool WaitForAnimation;
}

3130274--237119--2.png

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

    [SerializeField]
    [HideReferenceObjectPicker]
    [ListDrawerSettings(HideAddButton = true, OnTitleBarGUI = "DrawNewAddButton")]
    [DisableContextMenu(disableForMember: false, disableCollectionElements: true)] // Prevents people from right-clicking and changing the value to null or another type.
    private CoreActionSubAction[] actionsToTrigger;

    private void DrawNewAddButton()
    {
        if (SirenixEditorGUI.ToolbarButton(EditorIcons.Plus))
        {
            GenericMenu menu = new GenericMenu();

            var existingTypes = this.actionsToTrigger
                .Where(x => x != null)
                .Select(x => x.GetType())
                .ToHashSet();

            var coreActionTypes = typeof(CoreActionSubAction).Assembly
                .GetTypes()
                .Where(x =>
                    x.IsClass &&
                    !x.IsAbstract &&
                    !x.IsGenericTypeDefinition &&
                    x.GetConstructor(Type.EmptyTypes) != null &&
                    typeof(CoreActionSubAction).IsAssignableFrom(x));

            foreach (var type in coreActionTypes)
            {
                if (existingTypes.Contains(type))
                {
                    menu.AddDisabledItem(new GUIContent(type.GetNiceName().SplitPascalCase()));
                }
                else
                {
                    var localType = type;
                    menu.AddItem(new GUIContent(type.GetNiceName().SplitPascalCase()), false, () =>
                    {
                        this.actionsToTrigger = this.actionsToTrigger.Append((CoreActionSubAction)Activator.CreateInstance(localType)).ToArray();
                    });
                }
            }

            menu.ShowAsContext();
        }
    }

3130274--237120--3.png

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

There could still be added duplicate types by the user, by for instance ctrl+dragging element instances in order to copy them. If you also want to catch those cases, you could either make a warning messages using the [ValidateInput] attribute. Or you could simply remove all duplicate entries whenever the list has changed:

    [OnValueChanged("RemoveDuplicatesByType", includeChildren: true)] 
    public CoreActionSubAction[] actionsToTrigger;

    private void RemoveDuplicatesByType()
    {
        if (this.actionsToTrigger == null) return;

        var newArr = this.actionsToTrigger
            .Where(x => x != null)
            .GroupBy(x => x.GetType())
            .Select(x => x.First())
            .ToArray();

        if (newArr.Length != this.actionsToTrigger.Length)
        {
            var typesRemoved = this.actionsToTrigger
                .Except(newArr)
                .Select(x => x == null ? "Null" : x.GetType().Name)
                .ToArray();

            Debug.LogWarning("The already contains the action: " + string.Join(", ", typesRemoved));
            this.actionsToTrigger = newArr;
        }
    }
1 Like

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

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

Thank you again, that response alone was worth the price, let alone the asset!

1 Like

Hey Vostok,

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

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

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

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

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

I have the following setup:

[SerializeField] private List<Settings> _settings;

[Serializable]
private class Settings
{
    [Range(0,1)]
    public float EndT;
    public bool Enabled;
}

What I would like to be able to do is to have a [ValidateInput] check if the list items all have a bigger EndT value than the preivous one (so they are always “ordered”).
But if I put a [ValidateInput] on the _settings field it is not triggered when I change the child class EndT value. And if I put it on the EndT field I have no reference to the _settings in the validate input method.
So is this possible in the current version? =)

No, ValidateInput doesn’t currently catch it when child values are changed - adding an option for that can be done, though. I’ll make a note of it.

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

[SerializeField]
[OnInspectorGUI("SettingsMessageGUI", append: false)]
private List<Settings> _settings;

#if UNITY_EDITOR

private string ValidateSettings()
{
    for (int i = 0; i < _settings.Count; i++)
    {
        if (_settings[i] == null)
        {
            return "EndT value of all settings must be in ascending order - violated at index " + i + " with value " + _settings[i].EndT;
        }
    }

    if (_settings.Count > 0)
    {
        float lastT = _settings[0].EndT;

        for (int i = 1; i < _settings.Count; i++)
        {
            if (_settings[i].EndT <= lastT)
            {
                return "EndT value of all settings must be in ascending order - violated at index " + i + " with value " + _settings[i].EndT;
            }

            lastT = _settings[i].EndT;
        }
    }

    return null;
}

private void SettingsMessageGUI()
{
    string errorMessage = ValidateSettings();

    if (errorMessage != null)
    {
        // This error box will be registered in the scene validator, so it all works with that
        Sirenix.Utilities.Editor.SirenixEditorGUI.ErrorMessageBox(errorMessage);
    }
}

#endif

[Serializable]
private class Settings
{
    [Range(0, 1)]
    public float EndT;
    public bool Enabled;
}

It runs the validation every frame, which is a bit wasteful, but that shouldn’t be a problem unless you start having thousands of items in the list.

1 Like

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

Have you seen this before? What information can I provide?

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

If this doesn’t sound like your issue, we’d like to know as much as possible of the following:

  • Which version of Unity are you on? (And if 2017.1, are you using the new experimental .NET backend?)

  • Which version of Odin are you using?

  • Which version of macOS are you running?

  • Does deleting the GeneratedOdinEditors.dll files in the Sirenix/Assemblies/Editor folder, and/or also deleting the Library folder (to force trigger a full asset reimport) have any effect on the issue?

  • Does this happen when you import Odin into an empty project, or does it only happen in your project?

  • If it only happens in your project, can you isolate which part of your project may be causing Odin to crash?

  • And finally, if you can isolate it, could you send a zip of the part of the project that causes the issue to tor@sirenix.net, to help in tracking down the issue?

According to Unity Tech, it is being caused by a race condition in the editor upon startup.

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

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

Huh, that’s interesting. Yes, please do send it to me at tor@sirenix.net. I’ll make sure to add it to our current bug report regarding .dll’s causing crashes in 2017.1.

Sent!

Or rather, NOT sent because your mail service kicked it back because the ZIP file contains a DLL in it. Presumably your mail service is trying to virus scan it and rejecting it :stuck_out_tongue:

tor@sirenix.net: host 127.0.0.1[127.0.0.1] said: 554 5.7.0 Reject,
id=62830-19 - BANNED: .exe,.dll,Sirenix.OdinInspector.Attributes.dll (in
reply to end of DATA command)

1 Like

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

  • It’s not 100% obvious if you allow for serializing references to other MonoBehaviours that implement a certain interface. It looks like you allow users to create instances of objects that implement it that are then managed by the MonoBehaviour but references are important to me.
  • Can Odin detected implementations of interfaces inside all assemblies within the project? Most of what I work on is written externally and brought in assemblies so this is an important feature to me that I need some clarity on.
  • A lot of the work I do is open source with emphasis on community recruitment and contribution. Is it possible to link to and distribute a compiled assembly of just the Metadata (attributes)? I realize as a developer selling your asset you don’t want to have it all out there but a major part of Odin itself appears to be interpreting the metadata on fields and properties. So the question is it possible to compile the Attributes into their own assembly and still have the drawers/inspector function correctly? Secondly, will an assembly like this containing only the Attribute types be allowed in a public repository?
  • I realize you face the same issues with Interface serialization as the developers of competing solutions and myself. An inheritance of a custom MonoBehaviour implementation must be made. I understand why but I have a question related to the 3rd question. If you plan to support opensource collaboration how can/should we go about externalizing the SerializedMonoBehaviour, or whatever you called it, into its own seperate assembly away from the core of Odin. I think allowing for the distribution of these small assemblies as depedencies can foster Odin’s use in open source project while also protecting the core of what is offered because the metadata/attributes and the MonoBehaviour implementation alone doesn’t do anything without the drawers/custominspectors.

I realize these are a lot of questions for a potential sale, I hope they don’t come off in the wrong way either. I fully think a solution like Odin should be acquired by Unity and implemented. That solves basically everything from my point of view and I think we both agree with default inspector serialization and functionality is terrible but thankfully extendable. I just have some rather specific opensource related needs and it’s why I’ve depended on the shaky half-working GladBehaviour library for so long.

@jorisshh ,

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

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

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

Could this be done?

I’ll see what I can do - hopefully Odin will prove satisfactory!

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

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

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

So, this one is a little tricky.

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

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

So you could safely have a script like this:

public class YourMonoBehaviour : 
#if ODIN_INSPECTOR
   SerializedMonoBehaviour
#else
   MonoBehaviour
#endif
{
}

You could, of course, also just distribute two assemblies - one that works with Odin present, and one that works without. Regardless, you can still distribute the attributes .dll and “use” that part of Odin freely. They just won’t do anything if Odin isn’t present in the project.

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

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

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

Hello,

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

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

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

Example:

 [MinMaxSlider("EditorGetMinSliderValue", EditorGetMaxSliderValue)]
public Vector2 MinMaxValueSlider;

public int EditorGetMinSliderValue()
{
       return -10;
}

public int EditorGetMaxSliderValue()
{
       return 10;
}

I was also wondering if you could explain how I might hide the item highlighted in red so that it is not shown as part of the inspector. I essentially want to see the min/max int values but not the classname.MinMax in the box above it.

Here is the code:

    public class MinMax
    {
        public int minValue;
        public int maxValue;
    }
    //public List<MinMax> LevelRange = new List<MinMax>();
    [DictionaryDrawerSettings(DisplayMode = DictionaryDisplayOptions.ExpandedFoldout), HideReferenceObjectPicker]
    public Dictionary<string, MinMax> LevelRange = new Dictionary<string, MinMax>();
1 Like

We may choose to add this in the future - I’ll add it to the todo. For now, you can use a basic custom drawer like this:

using Sirenix.Utilities;
using System;
using System.Reflection;
using UnityEngine;

#if UNITY_EDITOR

using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities.Editor;

#endif

public class MyMonoBehaviour : MonoBehaviour
{
    [FancyMinMax("Min", "Max")]
    public Vector2 thingy;

    public Vector2 limits;

    public float Min()
    {
        return limits.x;
    }

    public float Max()
    {
        return limits.y;
    }
}

#if UNITY_EDITOR

[OdinDrawer]
public class FancyMinMaxDrawer : OdinAttributeDrawer<FancyMinMaxAttribute, Vector2>
{
    private class Context
    {
        public MethodInfo MinMethod;
        public MethodInfo MaxMethod;
        public string MinErrorMessage;
        public string MaxErrorMessage;
    }

    protected override void DrawPropertyLayout(IPropertyValueEntry<Vector2> entry, FancyMinMaxAttribute attribute, GUIContent label)
    {
        PropertyContext<Context> context;

        if (entry.Context.Get(this, "context", out context))
        {
            context.Value = new Context();

            context.Value.MinMethod = entry
                                     .Property
                                     .ParentType
                                     .FindMember()
                                     .IsMethod()
                                     .IsNamed(attribute.MinMethod)
                                     .HasReturnType<float>()
                                     .HasNoParameters()
                                     .GetMember<MethodInfo>(out context.Value.MinErrorMessage);

            context.Value.MaxMethod = entry
                                     .Property
                                     .ParentType
                                     .FindMember()
                                     .IsMethod()
                                     .IsNamed(attribute.MaxMethod)
                                     .HasReturnType<float>()
                                     .HasNoParameters()
                                     .GetMember<MethodInfo>(out context.Value.MaxErrorMessage);
        }

        Vector2 limits = new Vector2(float.MinValue, float.MaxValue);

        if (context.Value.MinErrorMessage != null)
        {
            SirenixEditorGUI.ErrorMessageBox(context.Value.MinErrorMessage);
        }
        else
        {
            limits.x = (float)context.Value.MinMethod.Invoke(entry.Property.ParentValues[0], null);
        }

        if (context.Value.MaxErrorMessage != null)
        {
            SirenixEditorGUI.ErrorMessageBox(context.Value.MaxErrorMessage);
        }
        else
        {
            limits.y = (float)context.Value.MaxMethod.Invoke(entry.Property.ParentValues[0], null);
        }

        var value = SirenixEditorFields.MinMaxSlider(label, entry.SmartValue, limits, attribute.ShowFields);

        value.x = Mathf.RoundToInt(value.x);
        value.y = Mathf.RoundToInt(value.y);

        entry.SmartValue = value;
    }
}

#endif

public class FancyMinMaxAttribute : Attribute
{
    public string MinMethod { get; private set; }
    public string MaxMethod { get; private set; }
    public bool ShowFields { get; private set; }

    public FancyMinMaxAttribute(string minMethod, string maxMethod, bool showFields = false)
    {
        this.MinMethod = minMethod;
        this.MaxMethod = maxMethod;
        this.ShowFields = showFields;
    }
}

It has some basic code for ensuring only whole numbers are set, as well.

1 Like