본문 바로가기
개발/Unity

Unity) 값 변경시 Method 호출 (Attribute - Change Call)

by 테샤르 2024. 9. 4.

값 변경시 Method 호출 (Attribute - Change Call)

 

값이 변경될때 해당 Method를 호출하는 기능이다.

 

< 사용방법 >

[System.Serializable]
public class ScreenChangeAction
{
    [Utill.OnChangedCall("RatioCalc")]
    [SerializeField]
    private int width;

    public void RatioCalc()
    {
        // This method will be called whenever 'width' changes in the inspector
        Debug.Log("RatioCalc method called. Width changed to: " + width);
    }
}
반응형

 

 < Attribute >

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

namespace Utill
{
    // Custom attribute to mark a field with a method to call when it changes
    public class OnChangedCallAttribute : PropertyAttribute
    {
        public string methodName;

        public OnChangedCallAttribute(string methodNameNoArguments)
        {
            methodName = methodNameNoArguments;
        }
    }

    // Custom property drawer to handle the attribute
    [CustomPropertyDrawer(typeof(OnChangedCallAttribute))]
    public class OnChangedCallAttributePropertyDrawer : PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            // Start listening for changes to the property
            EditorGUI.BeginChangeCheck();
            EditorGUI.PropertyField(position, property, label);

            // If no change, exit early
            if (!EditorGUI.EndChangeCheck()) return;

            // Apply modified properties to ensure changes are committed
            property.serializedObject.ApplyModifiedProperties();

            // Get the target object and the method name from the attribute
            var targetObject = property.serializedObject.targetObject;
            var callAttribute = (OnChangedCallAttribute)attribute;
            var methodName = callAttribute.methodName;

            // Resolve the parent object if the field is part of a nested structure or list
            object parentObject = GetParentObjectOfProperty(property, targetObject);

            // Get the method info and invoke it if found
            var methodInfo = parentObject.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            if (methodInfo != null && methodInfo.GetParameters().Length == 0)
            {
                methodInfo.Invoke(parentObject, null);
            }
            else
            {
                Debug.LogError($"OnChangedCall error: No method named '{methodName}' taking no arguments found in {parentObject.GetType().Name}");
            }
        }

        // Helper method to resolve the parent object of a serialized property
        private object GetParentObjectOfProperty(SerializedProperty prop, object targetObject)
        {
            var path = prop.propertyPath.Replace(".Array.data[", "[");
            var elements = path.Split('.');

            foreach (var element in elements.Take(elements.Length - 1))
            {
                if (element.Contains("["))
                {
                    var elementName = element.Substring(0, element.IndexOf("["));
                    var index = int.Parse(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
                    targetObject = GetValue(targetObject, elementName, index);
                }
                else
                {
                    targetObject = GetValue(targetObject, element);
                }
            }

            return targetObject;
        }

        private object GetValue(object source, string name)
        {
            if (source == null)
                return null;

            var type = source.GetType();
            var field = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            if (field == null)
            {
                var property = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                return property?.GetValue(source, null);
            }

            return field.GetValue(source);
        }

        private object GetValue(object source, string name, int index)
        {
            var enumerable = GetValue(source, name) as System.Collections.IEnumerable;
            var enumerator = enumerable?.GetEnumerator();

            for (int i = 0; i <= index; i++)
            {
                if (!enumerator.MoveNext()) return null;
            }

            return enumerator.Current;
        }
    }
}

 

 

사진

 

★☆

 

반응형

댓글