Community Made Tools

Have you made any useful utilities with Odin?

Login and submit your creations here

Required On Scene Attribute

Authored by Lucas
Shared 30-01-2020

This is a simple attribute to require a field to de present (valid) only when it is on a scene context. If the field is in a prefab, or in prefab mode, it will be considered valid even if it is null.

Usage

public class SomeScript : MonoBehaviour
{
    [RequiredOnScene]
    public int SomeInt;
}

RequiredOnSceneAttribute.cs

using System;

namespace Odin
{
    [AttributeUsage(AttributeTargets.Field)]
    public class RequiredOnSceneAttribute : Attribute
    {
        // ReSharper disable once UnassignedField.Global
        public string ErrorMessage;
    }
}

RequiredOnSceneAttributeValidator.cs

using System;
using System.Reflection;
using Odin.Editor;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.Validation;
using UnityEditor.Experimental.SceneManagement;
using UnityEngine;
using Object = UnityEngine.Object;

[assembly: RegisterValidator(typeof(RequiredOnSceneAttributeValidator))]

namespace Odin.Editor
{
    public class RequiredOnSceneAttributeValidator : AttributeValidator<RequiredOnSceneAttribute>
    {
        private StringMemberHelper stringHelper;

        public override void Initialize(MemberInfo member, Type memberValueType)
        {
            if (this.Attribute.ErrorMessage != null)
            {
                this.stringHelper = new StringMemberHelper(member.ReflectedType, false, this.Attribute.ErrorMessage);
            }
        }
        
        protected override void Validate(object parentInstance, object memberValue, MemberInfo member, ValidationResult result)
        {
            if (!(parentInstance is MonoBehaviour)) return;
            
            MonoBehaviour parent = (MonoBehaviour) parentInstance;
            GameObject sourceObject = parent.gameObject;
            
            bool isInPrefabMode = PrefabStageUtility.GetPrefabStage(sourceObject) != null;
            bool memberValid = IsValid(memberValue);
            var scene = sourceObject.scene;
            bool validScene = scene.IsValid() && scene.isLoaded;
            bool valid = isInPrefabMode || memberValid || !validScene;

            if (valid) return;
            result.ResultType = ValidationResultType.Error;
            result.Message = stringHelper != null ? stringHelper.GetString(parentInstance) : member.Name+" is required on scene!";
        }
        
        private static bool IsValid(object memberValue)
        {
            switch (memberValue)
            {
                case null:
                case string value when string.IsNullOrEmpty(value):
                case Object o when o == null:
                    return false;
                default:
                    return true;
            }
        }
    }
}