본문 바로가기
개발/Unity

Unity) Insepctor Custom (조건 처리 / 숨기기 / 비활성화)

by 테샤르 2022. 8. 22.

 Insepctor Custom (조건 처리 / 숨기기 / 비활성화)

Insepctor의 bool 값을 통해서 활성화되는 Field를 만드는것을 찾다가 알게되어서 포스팅하게됬다.

Is Use라는 필드를 활성화화면 Show Test라는 필드가 나오는것을 테스트했다.

불필요한 항목은 미노출 시키게 되면 사용하기도 편하고 사이드 이슈도 덜 생긴다.

기본값을 셋팅해준다던지 그런것도 따로 안해도된다. 정보를 더 노출하면 복잡성만 추가될 뿐이다.

 

※참고로 Array 형태나 Seralize Class 는 모두 정상동작하지 않고 단일 필드에 대해서만 가능하다.

그 상황이 필요하면 Editor Class를 상속받아서 따로 구현필요함

 

 

반응형

 

사용방법과 예시는 다음과 같다.

<필드 보이고/숨기기>

public bool showHideList = false; 
[ShowIf(ActionOnConditionFail.DontDraw, ConditionOperator.And, nameof(showHideList))]
public string aField = "item 1";

<필드 활성화 처리>

public bool enableDisableList = false;

[ShowIf(ActionOnConditionFail.JustDisable, ConditionOperator.And, 
nameof(enableDisableList))]
public string anotherField = "item 2";

<기본값 설정>

[ShowIf(ActionOnConditionFail.JustDisable, ConditionOperator.And,nameof(CalculateIsEnabled))]
public string yetAnotherField = "one more";    public 
bool CalculateIsEnabled()    
{
    return true;    
}

<여러개의 조건 처리>

public bool condition1;    
public bool condition2;    
[ShowIf(ActionOnConditionFail.JustDisable, ConditionOperator.And, nameof(condition1), 
nameof(condition2))]    
public string oneLastField= "last field";

 

스택오버플로우의 코드에서 

 

 

 

<ShowIfAttribute>

 

using System;
using UnityEngine;
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class ShowIfAttribute : PropertyAttribute
{
    public enum ConditionOperator
    {
        And,
        Or,
    }

    public enum ActionOnConditionFail
    {

        DontDraw,
        JustDisable,
    }

    public ActionOnConditionFail Action { get; private set; }
    public ConditionOperator Operator { get; private set; }
    public string[] Conditions { get; private set; }

    public ShowIfAttribute(ActionOnConditionFail action, ConditionOperator conditionOperator, params string[] conditions)
    {
        Action = action;
        Operator = conditionOperator;
        Conditions = conditions;
    }

 

 

<Editor 에 포함될 ShowIfAttributeDrawer>

using System.Reflection;
using UnityEditor;
using System.Collections.Generic;
using System;
using System.Linq;
using UnityEngine;

[CustomPropertyDrawer(typeof(ShowIfAttribute), true)]
public class ShowIfAttributeDrawer : PropertyDrawer
{

    #region Reflection helpers.
    private static MethodInfo GetMethod(object target, string methodName)
    {
        return GetAllMethods(target, m => m.Name.Equals(methodName, StringComparison.InvariantCulture)).FirstOrDefault();
    }

    private static FieldInfo GetField(object target, string fieldName)
    {
        return GetAllFields(target, f => f.Name.Equals(fieldName, StringComparison.InvariantCulture)).FirstOrDefault();
    }

    private static PropertyInfo GetProperty(object target, string propertyName)
    {
        return GetAllPropertyInfo(target, f => f.Name.Equals(propertyName, StringComparison.InvariantCulture)).FirstOrDefault();
    }

    private static IEnumerable<FieldInfo> GetAllFields(object target, Func<FieldInfo, bool> predicate)
    {
        List<Type> types = new List<Type>()
        {
            target.GetType()
        };

        while (types.Last().BaseType != null)
        {
            types.Add(types.Last().BaseType);
        }

        for (int i = types.Count - 1; i >= 0; i--)
        {
            IEnumerable<FieldInfo> fieldInfos = types[i]
                .GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly )
                .Where(predicate);

            foreach (var fieldInfo in fieldInfos)
            {
                yield return fieldInfo;
            }
        }

    }

    private static IEnumerable<MethodInfo> GetAllMethods(object target, Func<MethodInfo, bool> predicate)
    {
        IEnumerable<MethodInfo> methodInfos = target.GetType()
            .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
            .Where(predicate);

        return methodInfos;
    }

    private static IEnumerable<PropertyInfo> GetAllPropertyInfo(object target, Func<PropertyInfo, bool> predicate)
    {
        IEnumerable<PropertyInfo> propertyInfos = target.GetType()
            .GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
            .Where(predicate);

        return propertyInfos;
    }
    #endregion

    private bool MeetsConditions(SerializedProperty property)
    {
        var showIfAttribute = attribute as ShowIfAttribute;
        var target = property.serializedObject.targetObject;
        List<bool> conditionValues = new List<bool>();

        foreach (var condition in showIfAttribute.Conditions)
        {
            FieldInfo conditionField = GetField(target, condition);         //Method 에서 해당 조건 찾음
            if (conditionField != null &&
                conditionField.FieldType == typeof(bool))
            {
                conditionValues.Add((bool)conditionField.GetValue(target));
            }

            MethodInfo conditionMethod = GetMethod(target, condition);      //Method 에서 해당 조건 찾음
            if (conditionMethod != null &&
                conditionMethod.ReturnType == typeof(bool) &&
                conditionMethod.GetParameters().Length == 0)
            {
                conditionValues.Add((bool)conditionMethod.Invoke(target, null));
            }

            PropertyInfo conditionProperty = GetProperty(target, condition);      //Property 에서 해당 조건 찾음
            if (conditionProperty != null &&
                conditionProperty.PropertyType == typeof(bool) &&
                conditionProperty.GetIndexParameters().Length == 0)
            {
                var method = conditionProperty.GetMethod;
                conditionValues.Add((bool)method.Invoke(target, null));
            }

        }

        if (conditionValues.Count > 0)
        {
            bool met;
            if (showIfAttribute.Operator == ShowIfAttribute.ConditionOperator.And)
            {
                met = true;
                foreach (var value in conditionValues)
                    met = met && value;
            }
            else
            {
                met = false;
                foreach (var value in conditionValues)
                    met = met || value;
            }

            return met;
        }
        else
        {
            Debug.LogError("Invalid boolean condition fields or methods used!");
            return true;
        }
    }
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        // Calcluate the property height, if we don't meet the condition and the draw mode is DontDraw, then height will be 0.
        bool meetsCondition = MeetsConditions(property);
        var showIfAttribute = attribute as ShowIfAttribute;
       

        if (!meetsCondition && showIfAttribute.Action == ShowIfAttribute.ActionOnConditionFail.DontDraw)
            return 0;

        if (property.isExpanded)
            return EditorGUI.GetPropertyHeight(property);
        else
            return base.GetPropertyHeight(property, label);
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        bool meetsCondition = MeetsConditions(property);
        // Early out, if conditions met, draw and go.

        SerializedProperty arraySizeProp = property.FindPropertyRelative("Array.size");

        Debug.Log($"[GetPropertyHeight] {property.propertyPath} : {meetsCondition}");
        if (meetsCondition)
        {
            EditorGUI.PropertyField(position, property, label, true);
            return;
        }
        
        var showIfAttribute = attribute as ShowIfAttribute;
        if (showIfAttribute.Action == ShowIfAttribute.ActionOnConditionFail.DontDraw)
        {
            return;
        }
        else if (showIfAttribute.Action == ShowIfAttribute.ActionOnConditionFail.JustDisable)
        {
            EditorGUI.BeginDisabledGroup(true);
            EditorGUI.PropertyField(position, property, label, true);
            EditorGUI.EndDisabledGroup();
        }
    }
}

 

원본에서 확장될때에 대한 PropertyHeight를 추가로 계산해줬다.

해당기능은 다른곳에서 유용하게 잘 사용될것 같다.

 

<또 안되는 예시>

public class Test : MonoBehaviour
{
    [SerializeField]
    private InfoClass m_Info = null;

}

[System.Serializable]
public class InfoClass
{
    public enum InfoType 
    { 
        None,
        Use,
    }

    [SerializeField]
    private InfoType m_InfoType;

    [ShowIf(ShowIfAttribute.ActionOnConditionFail.JustDisable, ShowIfAttribute.ConditionOperator.And, nameof(IsShow))]
    [SerializeField]
    private List<int> m_List;

    private bool IsShow => m_InfoType == InfoType.Use;
}

 

 

원본 : 스택오버플로우 링크

 

How to enable/disable a List in Unity inspector using a bool?

I have a ScriptableObject script called Level, inside the level script I have a List of GameObjects and a bool variable called introduceNewEnemies. What i'm trying to do is that: I want to enable ...

stackoverflow.com

 

 

★★★★

 

반응형

댓글