Creating Custom SceneValidators

This tutorial will show you how to create custom SceneValidator by implementing a complete example of a MultipleAudioListenersValidator.

SceneValidators can be used to validate scenes and scene objects. Let's assume you have a specific object that is not allowed to be inside of a scene, using a SceneValidator you can ensure that you are notified immediatelly if such an object is added. In our case we're going to make sure that there is only ever one AudioListener inside the scene.

If you're already familiar with creating other validator types, just look at the sample code and see what's different. All validator types have very similar code and the tutorials for each differ only slightly.

First we create the validator class and add the required boilerplate code. Alternatively, you can also create a new validator by right-clicking in the project folder and navigating to Odin Validator > Create Validator > Scene Validator. This will create a new file for you and automatically add the boilerplate code. Odin tries to predict the correct code by looking at the name you chose for the validator, but you may have to change it a bit to make it work. You should now have something that looks similar to this code:

using Sirenix.OdinInspector.Editor.Validation;

[assembly: RegisterValidator(typeof(MultipleAudioListenersValidator))]

public class MultipleAudioListenersValidator : SceneValidator
{
    protected override void Validate(ValidationResult result)
    {
    }
}
Let's go through it and explain what we're doing.
using Sirenix.OdinInspector.Editor.Validation;

The SceneValidator type is in the Sirenix.OdinInspector.Editor.Validation namespace, so we need to include it.

[assembly: RegisterValidator(typeof(MultipleAudioListenersValidator))]

We register the validator with the RegisterValidator attribute. There is another attribute that can be used to register a validator called RegisterValidationRule. The rest of the code is identical no matter which one you use, but registering a validator as a validation rule can give you additional functionality. To learn more about it, check out the following tutorial. Validators vs Validation Rules

Do not forget to register the validator, or it will not be picked up by the validation system.

public class MultipleAudioListenersValidator : SceneValidator
{
    protected override void Validate(ValidationResult result)
    {
    }
}

We create the Validator class and inherit from SceneValidator. You can also see that we're overriding the Validate method. This method is the bread and butter of any validator and is responsible for executing the validation logic and setting the appropriate validation result.

Let's implement the actuall validation logic now.

using System.Linq;
using Sirenix.OdinInspector.Editor.Validation;

[assembly: RegisterValidator(typeof(MultipleAudioListenersValidator))]

public class MultipleAudioListenersValidator : SceneValidator
{
    protected override void Validate(ValidationResult result)
    {
        var audioListeners = this.FindAllComponentsInSceneOfType<AudioListener>(includeInactive: false).ToList();
        var count = audioListeners.Count();

        if (count > 1)
        {
            ref var warining = ref result.AddWarning($"There are {count} active audio listeners in the scene. Please ensure there is always exactly one active audio listener in the scene.");
        }
    }
}

Let's take a look at the changes made to the boilerplate code.

using System.Linq;

We've added a new using directive in order to be able to use linq.

protected override void Validate(ValidationResult result)
{
    var audioListeners = this.FindAllComponentsInSceneOfType<AudioListener>(includeInactive: false).ToList();
    var count = audioListeners.Count();

    if (count > 1)
    {
        ref var warining = ref result.AddWarning($"There are {count} active audio listeners in the scene. Please ensure there is always exactly one active audio listener in the scene.");
    }
}

This is where the actual validation logic is executed. First we find all AudioListener components in the scene excluding inactive ones. Then we count how many of them where found and if there are more than one, we add a warning to the validation results.

The result object is your main way of interacting with the validation system. Whenever your validator finds an issue you can add a warning / error to it using

  • .AddWarning()
  • .AddError()

Alternatively, you can also use the .Add() method, which takes a ValidatorSeverity argument. This is useful for validation rules (see Validators vs Validation Rules) as it allows you to configure the severity of this validator inside of the editor.

You can also add Buttons, Fixes, Metadata, and Context Menu Items by calling these methods after you have added warning / error.

  • .WithFix() (see Creating Custom Fixes)
  • .WithButton()
  • .WithMetaData()
  • .WithContextClick()
  • .WithSceneGUI()
  • .WithSelectionObject()
  • .EnableRichText()

If you want to draw something in the scene as the result of a warning / error you can do so by using the .WithSceneGUI() method.
All of these method calls can be chained since they use a builder pattern.

That's pretty much all you need to create a custom SceneValidator.
The validation logic can of course be a lot more sophisticated, but the general workflow is always the same.

Validators vs Validation Rules

You already know how to create custom validators and are interested in the difference between Validators and Validation Rules? Then this tutorial is right for you.

Validators vs Validation Rules

Custom Fixes

You want to add a button to your validator that executes a custom fix? Then this tutorial is right for you.

Creating Custom Fixes

Other Validator Types

Are you interested in the other validator types? Check out this overview to see which one is right for your use case.

Validator Types Overview