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;
}
원본 : 스택오버플로우 링크
★★★★☆
반응형
댓글