The 3.0 beta comes packed with a ton of exciting new features and improvements. An exhaustive set of patch notes will be released later; meanwhile, here are some of the more noteworthy points.
Optimizations
Through the careful and precise application of black magic throughout the code base and many late nights spent poring over the profiler, Odin has been optimized to be overall faster than ever.
New attributes
Enterprise-only feature: [Searchable] attribute
Enterprise customers will get access to the new, configurable [Searchable] attribute, with custom integrations throughout Odin's inspectors and drawers. Apply it on a Component or ScriptableObject to be able to search the entire object for individual properties at once; on a member to search only that member and its children; on a collection to search that collection; on an EditorWindow to make that window searchable, and so on. This also adds search to various Odin Preferences windows, where applicable.
[OnCollectionChanged] attribute
This attribute is like [OnValueChanged], but customized for collections, providing before/after change events, as well as extra information about the exact change that is being made.
Odin Modules
Odin Modules are small packages of features that can be enabled or disabled based on different conditions, either automatically or manually by the user. This allows us to add conditionally triggered support for Unity packages like DOTS, Unity.Mathematics and Unity.Addressables.
Module: ECS Entity Inspection support This module adds an Entity Component System inspector integration to Odin.
Module: Unity Mathematics support
This small module contains a set of custom drawers to improve the performance, look and functionality of drawing Unity.Mathematics structs in the inspector.
Validators now use the property system
The property system that is at the core of most of what Odin does will now also be powering the validation system, allowing for much more flexibility when making custom validators and validation rules. This means that validation can now make use of the full power of attribute and property processors. While this does come with some performance penalties as more work is now done per validated property, the added power is more than worth it, and the cost is greatly mitigated by overall performance optimizations in the rest of Odin. Additionally, this along with the new property state system means that hidden properties will no longer be validated by the project validator.
Property State system
Properties now have a built-in understanding of the concepts of visibility, being enabled, being expanded, and so on. This means that instead of being stored privately inside inaccessible drawer instances, these states can now be queried and modified from the outside. These are accessed through the InspectorProperty.State member. Custom states can also be defined, and the 3.0 beta exposes custom states for getting and setting the currently selected tab in tab groups, and the expanded state of [DetailedMessageBox]. We also plan to add a state for getting and setting the currently selected page of a collection.
Value and Action Resolvers
Value and Action Resolvers are a new, easy-to-use and extendable API that makes it a breeze to add expression and member reference support to custom drawers and validators. Adding custom arguments is a core part of the system, and it will come with several built-in default values for things like the InspectorProperty or the current value. This means that, globally, almost any string-based invocation or expression used by Odin can now gain access to extra contextual information like the InspectorProperty instance to which it is attached.
Something along these lines now becomes possible:
[CustomValueDrawer("GiveMeCallNextDrawer")]
public int Field;
public int GiveMeCallNextDrawer(Action<GUIContent> callNextDrawer)
{
GUI.color = Color.green;
callNextDrawer(null);
GUI.color = Color.white;
}
Usage of this new API is very simple:
public class ExampleAttribute : Attribute
{
public string ResolvedString;
public string ResolvedAction;
}
public class ExampleAttributeDrawer : OdinAttributeDrawer<ExampleAttribute>
{
private ValueResolver<string> stringResolver;
private ActionResolver actionResolver;
protected override void Initialize()
{
this.stringResolver = ValueResolver.Get<string>(this.Property, this.Attribute.ResolvedString);
this.actionResolver = ActionResolver.Get(this.Property, this.Attribute.ResolvedAction);
}
protected override void DrawPropertyLayout(GUIContent label)
{
if (this.stringResolver.HasError)
this.stringResolver.DrawError();
else GUILayout.Label("The resolved string is: " + this.stringResolver.GetValue());
if (this.actionResolver.HasError)
this.actionResolver.DrawError();
else if (GUILayout.Button("Invoke Resolved Action"))
this.actionResolver.DoActionForAllSelectionIndices();
this.CallNextDrawer(label);
}
}
// ... Usage would look like:
[Example(ResolvedString = "@DateTime.Now.ToString()", ResolvedAction = "@UnityEngine.Debug.Log(\"Action executed for value \" + $value)")]
public string someStr;
Universal expression coverage
Thanks to the new action and value resolvers, all resolved strings now support attribute expressions, including ones such as [CustomValueDrawer], [InlineButton] and [OnValueChanged].
Assignment operator support for expressions
Instead of having to add extra methods to your types, you will now be able to use assignment operators with Odin's expression system.
For example, you'll be able to do something like this:
[InlineButton("@Value = 0", Label = "Set to zero")]
public int Value;
Property query operator for expressions
Expressions now support a new syntactical construct for getting the properties of members in the scope of the expression. This, along with assignment operators, the state system and the various OnX event attributes, offers a new and potent feature synergy.
For example, you'll be able to do something like this:
public string[] StringArray;
// The Value1 int is only shown if the string array is currently expanded in the inspector
[ShowIf("@#(StringArray).State.Expanded")]
public int Value1;
// The Value2 int is only shown if Value1 is visible
[ShowIf("@#(Value1).State.Visible")]
public int Value2;