Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

why PropertyDrawer Call OnGUI before array add new element?

Discussion in 'Immediate Mode GUI (IMGUI)' started by bbsuuo, Jan 21, 2020.

  1. bbsuuo

    bbsuuo

    Joined:
    Jul 21, 2016
    Posts:
    16
    i want Serializable some class without MonoBehaviour, but get some program

    some class it's fire ,but some class have error:
    System.InvalidOperationException: Operation is not valid due to the current state of the object


    and i check my scripts, the program is constructed element before Ongui,so array count is Zero,

    Isn't the array always constructed before OnGUI

    i try Debug.Log((property.serializedObject.targetObject as MachineBehaviour).handlers.Count);

    when i add element and some class value will one , some class value is zero



    Code (CSharp):
    1. using StateMachine;
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System.Reflection;
    6. using UnityEngine;
    7.  
    8. [System.Serializable]
    9. public class StatePropertyHandler
    10. {
    11.     public string stateType;
    12.     public List<StateFieldHandler> stateFieldHandler = new List<StateFieldHandler>();
    13.  
    14.  
    15.  
    16.     public void Serializable(Type stateType) {
    17.         if (stateType  == null || this.stateType == stateType.Name) return;
    18.         this.stateType = stateType.Name;
    19.         stateFieldHandler = new List<StateFieldHandler>();
    20.         var fields = StatePropertyUtility.GetAttributeField(stateType);
    21.  
    22.         for (int i = 0; i < fields.Count; i++)
    23.         {
    24.             StateFieldHandler handler = new StateFieldHandler(fields[i]);
    25.             stateFieldHandler.Add(handler);
    26.         }
    27.  
    28.         // stateFieldHandler = new List<StateFieldHandler>();
    29.         //重新赋值stateFieldHandler
    30.     }
    31.  
    32.  
    33.  
    34.  
    35.  
    36.     [NonSerialized]
    37.     public MachineBehaviour machine;
    38.     public void DeSerializable(State target) {
    39.         machine = target.machine as MachineBehaviour;
    40.  
    41.         //赋值给state
    42.         for (int i = 0; i < stateFieldHandler.Count; i++) {
    43.             var handler = stateFieldHandler[i];
    44.             handler.DeSerializableState(target);
    45.         }
    46.  
    47.     }
    48.  
    49. }
    50.  
    51. [System.Serializable]
    52. public class StateFieldHandler
    53. {
    54.     public string fieldName;
    55.     public string fieldType;
    56.    
    57.  
    58.     [NonSerialized]
    59.     private FieldInfo targetField;
    60.    public FieldInfo TargetField { get {
    61.             if (targetField == null) {
    62.                 targetField = GetTargetType(fieldType);
    63.             }
    64.             return targetField;
    65.         }    
    66.     }
    67.  
    68.     public bool boolValue;
    69.     public float floatValue;
    70.     public int intValue;
    71.     public string stringValue;
    72.     public Vector3 vector3Value;
    73.     public Color colorValue;
    74.     public AnimationCurve animationCurveValue;
    75.     public Transform transformValue;
    76.  
    77.  
    78.     public FieldInfo GetTargetType(string orginType) {
    79.      
    80.         var thisFields =  this.GetType().GetFields(BindingFlags.Default | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    81.         for (int i = 0; i < thisFields.Length; i++) {
    82.             if (thisFields[i].Name == "fieldName" || thisFields[i].Name == "fieldType") {
    83.                 continue;
    84.             }
    85.  
    86.             if (thisFields[i].FieldType.Name == orginType) {
    87.                 return thisFields[i];
    88.             }
    89.         }
    90.         return null;
    91.     }
    92.  
    93.     public StateFieldHandler(FieldInfoHandler fieldInfoHandler) {
    94.         SetDefaultValue(fieldInfoHandler);
    95.     }
    96.  
    97.     protected void SetDefaultValue(FieldInfoHandler fieldInfoHandler) {
    98.      
    99.      
    100.         this.fieldName = fieldInfoHandler.info.Name;
    101.         this.fieldType = fieldInfoHandler.info.FieldType.Name;
    102.         FieldInfo targetField = GetTargetType(fieldType);
    103.         if (targetField == null) {
    104.             Debug.LogError("Not Support OrginType :" + fieldType);
    105.             return;
    106.         }
    107.         targetField.SetValue(this,fieldInfoHandler.defaultValue);
    108.     }
    109.  
    110.  
    111.  
    112.     protected object GetValue() {      
    113.         return TargetField.GetValue(this);
    114.     }
    115.  
    116.     public FieldInfo GetStateField(State target) {
    117.         var infos =  target.GetType().GetFields(BindingFlags.Default | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    118.         for (int i = 0; i < infos.Length; i++) {
    119.             if (infos[i].Name == fieldName) {
    120.                 return infos[i];
    121.             }
    122.         }
    123.         return null;
    124.     }
    125.  
    126.     public void DeSerializableState(State target) {
    127.         if (TargetField == null) {
    128.             Debug.LogError("Not Support OrginType :" + fieldType);
    129.             return;
    130.         }
    131.         object value = GetValue();
    132.         if (value != null) {
    133.             FieldInfo stateField = GetStateField(target);
    134.             if (stateField != null)
    135.             {
    136.                 stateField.SetValue(target, value);
    137.             }
    138.             else {
    139.                 Debug.LogError("not find field from state:" + fieldName+"|"+fieldType);
    140.             }
    141.             //TargetField.SetValue(target,value);
    142.             //从 Target里找同名的Field
    143.  
    144.         }
    145.     }
    146. }
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5. using System;
    6. using StateMachine;
    7. using UnityEditor.SceneManagement;
    8. using System.Linq;
    9. using System.Reflection;
    10.  
    11. [CustomPropertyDrawer(typeof(StatePropertyHandler))]
    12. public class StatePropertyHandlerEditor : PropertyDrawer {
    13.     //public List<StatePropertyHandlerDraw> drawers = new List<StatePropertyHandlerDraw>();
    14.     public Dictionary<string, StatePropertyHandlerDraw> drawers = new Dictionary<string, StatePropertyHandlerDraw>();
    15.  
    16.     public static  UnityEngine.Object targetObject;
    17.  
    18.     public StatePropertyHandlerDraw Register(string fullPath, SerializedProperty property) {
    19.         StatePropertyHandlerDraw drawer = new StatePropertyHandlerDraw(property);
    20.         drawers.Add(fullPath, drawer);
    21.         return drawer;
    22.     }
    23.  
    24.  
    25.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    26.     {
    27.         var drawer = drawers.TryGet(property.serializedObject.targetObject.GetInstanceID() + property.propertyPath);
    28.         if (drawer == null) {
    29.             //drawer = Register(property.propertyPath,property);
    30.             return 15;
    31.         }
    32.        
    33.         return drawer.GetHeight();
    34.  
    35.     }
    36.  
    37.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    38.     {
    39.   [I][B][U]      Debug.Log((property.serializedObject.targetObject as MachineBehaviour).handlers.Count);[/U][/B][/I]
    40.         var drawer = drawers.TryGet(property.serializedObject.targetObject.GetInstanceID() + property.propertyPath);
    41.         if (drawer == null)
    42.         {
    43.             drawer = Register(property.serializedObject.targetObject.GetInstanceID() + property.propertyPath, property);
    44.         }
    45.         else
    46.         {
    47.             drawer.EditorOnGUI(position, property, label);
    48.         }
    49.     }
    50.  
    51.  
    52.  
    53.  
    54.  
    55.  
    56. }
    57.  
    58.  
    59. public class StatePropertyHandlerDraw {
    60.  
    61.     private static Type[] allTypes;
    62.     private static string[] stateContent;
    63.  
    64.  
    65.     private static GUIStyle redFont;
    66.     private static GUIStyle greenFont;
    67.  
    68.     public string propertyRealPath;
    69.  
    70.     public StatePropertyHandler target;
    71.     private int selectStateIndex;
    72.     private int lastSelelctStateIndex;
    73.  
    74.  
    75.     SerializedProperty property;
    76.    
    77.  
    78.     float height;
    79.  
    80.     bool enable = true;
    81.     public StatePropertyHandlerDraw(SerializedProperty property) {
    82.  
    83.         Init(property);
    84.     }
    85.  
    86.     private bool init = false;
    87.     public void Init(SerializedProperty property) {
    88.         try
    89.         {
    90.             target = GetParent(property) as StatePropertyHandler;
    91.         }
    92.         catch (Exception e) {
    93.             Debug.LogError(e);
    94.         }
    95.         if (allTypes == null || stateContent == null)
    96.         {
    97.             allTypes = StatePropertyUtility.GetExecutingAssemblyTypesName<State>();
    98.             stateContent = new string[allTypes.Length];
    99.         }
    100.         if (target == null || allTypes == null) return;
    101.  
    102.  
    103.         for (int i = 0; i < allTypes.Length; i++)
    104.         {
    105.             string typeName = allTypes[i].Name;
    106.             stateContent[i] = allTypes[i].Name;
    107.             if (typeName == target.stateType)
    108.             {
    109.                 lastSelelctStateIndex = selectStateIndex = i;
    110.             }
    111.         }
    112.         Undo.undoRedoPerformed += UndoRedoCallBack;
    113.         SetHeight();
    114.        
    115.     }
    116.  
    117.  
    118.     public void UndoRedoCallBack()
    119.     {
    120.  
    121.  
    122.  
    123.         if (!Application.isPlaying)
    124.         {
    125.             if (allTypes[selectStateIndex].Name != target.stateType)
    126.             {
    127.                 var currentIndex = 0;
    128.                 for (int i = 0; i < allTypes.Length; i++)
    129.                 {
    130.                     string typeName = allTypes[i].Name;
    131.                     stateContent[i] = allTypes[i].Name;
    132.                     if (typeName == target.stateType)
    133.                     {
    134.                         currentIndex = i;
    135.                     }
    136.                 }
    137.  
    138.                 if (selectStateIndex != currentIndex)
    139.                 {
    140.                     selectStateIndex = lastSelelctStateIndex = currentIndex;
    141.                 }
    142.                 SetHeight();
    143.             }
    144.         }
    145.  
    146.     }
    147.  
    148.     public void EditorOnGUI(Rect position, SerializedProperty property, GUIContent label) {
    149.         if (target == null)
    150.         {
    151.             Init(property);
    152.             EditorGUI.LabelField(position, "UKNOW ERROR");
    153.             return;
    154.         }
    155.         GUI.Box(position, "");
    156.         EditorGUI.BeginChangeCheck();
    157.         Rect baseRect = position; baseRect.height = EditorGUIUtility.singleLineHeight;
    158.         baseRect.y += 5;
    159.  
    160.  
    161.         bool notHaveState = target == null || target.stateFieldHandler.Count == 0;
    162.        
    163.  
    164.         enable = EditorGUI.ToggleLeft(baseRect, target.stateType, enable);
    165.  
    166.         if (enable)
    167.         {
    168.            
    169.  
    170.             EditorGUI.BeginProperty(position, label, property);
    171.    
    172.  
    173.             baseRect.y += 20;
    174.             Undo.RecordObject(property.serializedObject.targetObject, "Change State");
    175.  
    176.  
    177.            
    178.             selectStateIndex = EditorGUI.Popup(baseRect, target.stateType, selectStateIndex, stateContent);
    179.             if (lastSelelctStateIndex != selectStateIndex)
    180.             {
    181.                 target.Serializable(allTypes[selectStateIndex]);
    182.                 SetHeight();
    183.                 //DoSomeThing
    184.                 lastSelelctStateIndex = selectStateIndex;
    185.  
    186.             }
    187.  
    188.  
    189.             if (notHaveState)
    190.             {
    191.                 position.y += 5;
    192.                 baseRect.y += EditorGUIUtility.singleLineHeight;
    193.                 EditorGUI.LabelField(baseRect, "Not Support State Type Or State Not Have Attribute!, Please ReSelect");
    194.                 return;
    195.             }
    196.             else
    197.             {
    198.  
    199.  
    200.                 for (int i = 0; i < target.stateFieldHandler.Count; i++)
    201.                 {
    202.                     Rect singeRect = baseRect;
    203.                     singeRect.y = baseRect.y + (20f * (i + 1));
    204.                     DrawSingleField(singeRect, target.stateFieldHandler[i], property);
    205.                 }
    206.  
    207.  
    208.  
    209.  
    210.             }
    211.  
    212.    
    213.      
    214.         EditorGUI.EndProperty();
    215.         }
    216.  
    217.         if (EditorGUI.EndChangeCheck())
    218.         {
    219.             SetHeight();
    220.             property.serializedObject.ApplyModifiedProperties();
    221.             EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
    222.  
    223.         }
    224.     }
    225.  
    226.  
    227.  
    228.  
    229.  
    230.  
    231.     public void DrawSingleField(Rect rect, StateFieldHandler handler, SerializedProperty property)
    232.     {
    233.         //GUI.Box(rect, "",GUI.skin.textField);
    234.  
    235.         if (handler.TargetField == null)
    236.         {
    237.             EditorGUI.LabelField(rect, "Not Support this Type:" + handler.fieldType);
    238.             return;
    239.         }
    240.  
    241.         DrawFieldByType(rect, handler, property);
    242.     }
    243.  
    244.     public void DrawFieldByType(Rect rect, StateFieldHandler handler, SerializedProperty property)
    245.     {
    246.         if (handler.TargetField == null) {
    247.             EditorGUI.LabelField(rect, "Not Support this Type:" + handler.fieldType);
    248.         }
    249.         Type fieldType = handler.TargetField.FieldType;
    250.         GUIContent content = EditorGUIUtility.ObjectContent(null, fieldType);
    251.         Undo.RecordObject(property.serializedObject.targetObject, "Field Change" + handler.TargetField);
    252.         //EditorGUI.ObjectField(rect,content,null,fieldType,false);
    253.         content.text = handler.fieldName;
    254.         if (fieldType == typeof(string))
    255.         {
    256.             handler.stringValue = EditorGUI.TextField(rect, content, handler.stringValue);
    257.         }
    258.         else if (fieldType == typeof(int))
    259.         {
    260.             handler.intValue = EditorGUI.IntField(rect, content, handler.intValue);
    261.         }
    262.         else if (fieldType == typeof(bool))
    263.         {
    264.             handler.boolValue = EditorGUI.Toggle(rect, content, handler.boolValue);
    265.         }
    266.         else if (fieldType == typeof(float))
    267.         {
    268.             handler.floatValue = EditorGUI.FloatField(rect, content, handler.floatValue);
    269.         }
    270.         else if (fieldType == typeof(Vector3))
    271.         {
    272.             handler.vector3Value = EditorGUI.Vector3Field(rect, content, handler.vector3Value);
    273.         }
    274.         else if (fieldType == typeof(Color))
    275.         {
    276.             handler.colorValue = EditorGUI.ColorField(rect, content, handler.colorValue);
    277.         }
    278.         else if (fieldType == typeof(AnimationCurve))
    279.         {
    280.             if (handler.animationCurveValue == null) { handler.animationCurveValue = AnimationCurve.Linear(1, 1, 1, 1); }
    281.             handler.animationCurveValue = EditorGUI.CurveField(rect, content, handler.animationCurveValue);
    282.         }
    283.         else if (fieldType == typeof(Transform))
    284.         {
    285.             handler.transformValue = EditorGUI.ObjectField(rect, content
    286.                 , handler.transformValue, typeof(Transform), true) as Transform;
    287.         }
    288.         else
    289.         {
    290.             EditorGUI.LabelField(rect, "Not Support this Type:" + handler.fieldType);
    291.         }
    292.  
    293.  
    294.  
    295.     }
    296.  
    297.  
    298.     private void SetHeight()
    299.     {
    300.         int count = target == null ? 15 : target.stateFieldHandler.Count;
    301.  
    302.  
    303.         height = 60 + count * 20;
    304.      
    305.     }
    306.  
    307.     public float GetHeight()
    308.     {
    309.         if (target == null) {
    310.             return 15;
    311.         }
    312.         if (height == 0) {
    313.             SetHeight();
    314.         }
    315.  
    316.         if (!enable)
    317.         {
    318.             return 45;
    319.         }
    320.         return height;
    321.     }
    322.  
    323.  
    324.  
    325.  
    326.     public object GetParent(SerializedProperty prop)
    327.     {
    328.  
    329.         var path = prop.propertyPath.Replace(".Array.data[", "[");
    330.         object obj = prop.serializedObject.targetObject;
    331.         var elements = path.Split('.');
    332.         //foreach (var element in elements.Take(elements.Length - 1))
    333.         for (int i = 0; i < elements.Length; i++)
    334.         {
    335.             var element = elements[i];
    336.          
    337.             if (element.Contains("["))
    338.             {
    339.                 var elementName = element.Substring(0, element.IndexOf("["));
    340.                 var index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
    341.              
    342.                 obj = GetValue(obj, elementName, index);
    343.             }
    344.             else
    345.             {
    346.                 obj = GetValue(obj, element);
    347.             }
    348.         }
    349.         return obj;
    350.     }
    351.  
    352.     public object GetValue(object source, string name)
    353.     {
    354.         if (source == null)
    355.             return null;
    356.         var type = source.GetType();
    357.         var f = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    358.         if (f == null)
    359.         {
    360.             var p = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
    361.             if (p == null)
    362.                 return null;
    363.             return p.GetValue(source, null);
    364.         }
    365.         return f.GetValue(source);
    366.     }
    367.  
    368.     public object GetValue(object source, string name, int index)
    369.     {
    370.        
    371.         var enumerable = GetValue(source, name) as IEnumerable;
    372.         var enm = enumerable.GetEnumerator();//as IList<StateFieldHandler>;
    373.                          
    374.         while (index-- >= 0)
    375.         {
    376.        
    377.           bool next=   enm.MoveNext();  
    378.         }
    379.  
    380.        
    381.         return enm.Current;
    382.     }
    383.  
    384. }
    385.  
    386.  
     

    Attached Files:

  2. bbsuuo

    bbsuuo

    Joined:
    Jul 21, 2016
    Posts:
    16
    i wait a frame when OnGUI ,it's work , but i still feel this's strange error