[UnityEditor] IUtil - Handling custom property issues

qweasfjbv·2025년 9월 20일

UnityEditor

목록 보기
12/12

개요


https://github.com/qweasfjbv/UnityInspectorUtils/issues/8

오픈소스로 공개해둔 InspectorUtil 툴에 이슈가 올라왔습니다.
간단하게 요악하면 다음과 같습니다.

[System.serializeable]
public class NestedClass
{
	public bool isShow;
    [ShowIf(nameof(isShow))] public float value;
}

public class MonoClass : MonoBehaviour
{
	public List<NestedClass> classList = new();
}

ShowIf 어트리뷰트는 파라미터와 이름이 같은 boolean 값이 true이면 Inspector창에서 보이게, false면 안보이도록 하는 기능입니다.

해당 어트리뷰트를 다른 클래스에서 사용한 뒤에, MonoBehaviour 클래스에서 사용하려하면 제대로 나타나지 않는 에러가 있었습니다.

구현


원인은 아래의 코드였습니다.

public static bool GetBoolean(this SerializedProperty property, string attr, string fieldName)
{
	object targetObject = property.serializedObject.targetObject;
	FieldInfo fieldInfo = targetObject.GetType().GetField(
		fieldName,
		BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
	);

	if (fieldInfo == null)
	{
		IUtilDebug.NoFieldError(attr, fieldName);
		return false;
	}

	if (fieldInfo.FieldType != typeof(bool))
	{
		IUtilDebug.TypeError(attr, fieldInfo.FieldType.ToString());
		return false;
	}

	return Convert.ToBoolean(fieldInfo.GetValue(targetObject));
}

[ShofIf(nameof(isShow))] 에서 "isShow" 를 string으로 받아서 해당 boolean값을 찾는 함수입니다.
겉보기엔 크게 문제가 없어 보였지만 위의 property.serializedObject.targetObject 에 문제가 있었습니다.

위의 예시에서 ShowIfNestedClass 에서 쓰였음에도 불구하고, targetObject 는 항상 Monobehaviour 클래스, 즉 MonoClass 를 반환하기 때문에 boolean값을 찾지 못했습니다.

따라서 property의 targetObject가 아닌, propertyPath를 통해 계층 구조를 파악하여 내부 클래스로 접근할 수 있도록 하였습니다.


public static object GetTargetObjectWithProperty(SerializedProperty prop)
{
    string path = prop.propertyPath.Replace(".Array.data[", "[");
    object obj = prop.serializedObject.targetObject;
    string[] elements = path.Split('.')[.. ^ 1];

    // follow paths to track property in same object
    foreach(var element in elements)
    {
        if (element.Contains("["))
        {
            var elementName = element.Substring(0, element.IndexOf("["));
            var index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
            obj = GetFieldValue(obj, elementName, index);
        }
        else
        {
            obj = GetFieldValue(obj, element);
        }

        if (obj == null)
            return null;
    }
}

propertyPath를 elements에 split하여 담습니다.
특히, [..^1] 를 통해 마지막 원소 (변수명) 은 제외해주어야 targetObject까지 도달할 수 있습니다.

위 코드를 통해 targetObject를 제대로 구한뒤에 실행해보면 아래와 같이 제대로 실행되는 모습을 확인할 수 있습니다.

마무리


오픈소스에서 이슈로 피드백을 받고 해당 오류를 수정해보았습니다.
확실히 오픈소스의 장점 중 하나는 제가 찾지 못하는 오류를 발견할 수 있다는 것 같습니다.

0개의 댓글