Skip to main content
Commonmark migration
Source Link

#Preamble

Preamble

#My problem is as follows:

My problem is as follows:

#Code below.

Code below.

#Preamble

#My problem is as follows:

#Code below.

Preamble

My problem is as follows:

Code below.

Tweeted twitter.com/#!/StackGameDev/status/565342727051948035
removed second use of the word "alas" because it was bothering me, fixed a typo that may have led people astray
Source Link

I solved this problem 4 years ago, so I know it can be done; but, alas, I do not have access to that code anymore. I'm trying to build a solution this time with ScriptableObject and SerilizableObject, and I have something that almost works.

A custom inspector utility class to edit it with - the bug is in here:

and sample custom inspector that uses that - the bug is in here:

I solved this problem 4 years ago, so I know it can be done; but, alas, I do not have access to that code anymore. I'm trying to build a solution this time with ScriptableObject and SerilizableObject, and I have something that almost works.

A custom inspector utility class to edit it with:

and sample custom inspector that uses that - the bug is in here:

I solved this problem 4 years ago, so I know it can be done; but, I do not have access to that code anymore. I'm trying to build a solution this time with ScriptableObject and SerilizableObject, and I have something that almost works.

A custom inspector utility class to edit it with - the bug is in here:

and sample custom inspector that uses that

pruned examples down to their essence, so people reviewing this question don't have to wade through as much code
Source Link

[EDIT: based on some feedback, I pruned the example down to the base functionality of the bug we are trying to solve. The description below remains accurate, but the repro has been shortened to:

  1. Add the FXTriggerTimer component to an object
  2. Hit the blue '+' button to add an AnimEvent and a SFXEvent
  3. Save the scene, leave the scene and come back to the scene
  4. Notice that the object has blank FXEvents instead of AnimEvents & SFXEvents
  5. Hit space in a script file and save to trigger a script refresh in Unity
  6. Notice that the object has had AnimEvents & SFXEvents restored

FINALLY: if anybody knows how to solve this problem without using scriptable or serializeable objects, that would be preferred. Thanks a million!]

I apply my FXTriggerTimer MonoBehavior (which inherits from FXTriggerBase) to a test object, and then hit the blue '+' button to add an AnimEvent and a SFXEvent to the trigger. I then save the scene, and reload the scene, and my test object has an array of two null elements that (obviously) don't draw in the inspector. BUT, if I edit a script, any script (e.g. hit space and save), the script refresh will magically repopulate with the correct two items. So I know that all of the pieces are there under the hood, they just need to be restored on scene load.

//Scripts/FXTriggerBase.cs
using UnityEngine;
using System.Collections;

public enum FXEventType {kNone, kAnim,  kSFX,}
    
/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[System.Serializable]
public class FXEvent: UnityEngine.ScriptableObject
{
    [SerializeField]
         public FXEventType  type;
        
    [SerializeField]
         public GameObject   go;
     
    virtual public void Fire()  {   }
};

/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[System.Serializable]
public class AnimEvent : FXEvent
{       
    [SerializeField]
     public string       animName = "";
    public AnimEvent()      {type = FXEventType.kAnim;}    
    public void Fire()      {go.animation.Play(animName);   }
}

/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[System.Serializable]
public class SFXEvent: FXEvent
{
    [SerializeField]
     public AudioClip    clip;
    public SFXEvent()       {type = FXEventType.kSFX;}      
    public void Fire()      {go.audio.PlayOneShot(clip);    }
} 


/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[AddComponentMenu("")]  // prevents users from using this directly
public class FXTriggerBase : MonoBehaviour 
{
    [SerializeField]
     public FXEvent[] events = new FXEvent[]{};
    
    public void Fire()
    {
        for( int e = 0; e < events.Length; e++)
        {
            events[e].go = gameObject;
            events[e].Fire();
        }   
    }
}
//Editor/FXTriggerBaseUtil.cs
using UnityEngine;
using System.Collections;
using UnityEditor;
    
/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
public class FXEditorUtil
{
    SerializedObject[] serial;  
    public bool dirtied = false;
            
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    public void OnSubGUI(this FXEvent fx)
    {
        //having trouble getting polymorphism to work right in the inspector,
        // so this is what works.
        if(fx.type == FXEventType.kAnim)
            OnSubGUI((AnimEvent)fx);
        else if(fx.type == FXEventType.kSFX)
            OnSubGUI((SFXEvent)fx);
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    public void OnSubGUI(this AnimEvent anim)
    {       
        string newAnimName = GUILayout.TextField(anim.animName);
        if(newAnimName != anim.animName)
        {
            AnimEvent anim = (AnimEvent)fx;
            string newAnimName = GUILayout.TextField(anim.animName);
            if(newAnimName != anim.animName)
            {
                anim.animName = newAnimName;
                dirtied = true;
            }
        }
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    public void OnSubGUI(this SFXEvent sfx)
    {
        AudioClip newSFX = (AudioClip)EditorGUILayout.ObjectField(sfx.clip, typeof(AudioClip));
       else if(newSFXfx.type !=== sfxFXEventType.clipkSFX)
        {
            SFXEvent sfx = (SFXEvent)fx;
            AudioClip newSFX = (AudioClip)EditorGUILayout.ObjectField(sfx.clip, typeof(AudioClip));
            if(newSFX != sfx.clip)
            {
                sfx.clip =newSFX;
                dirtied = true;
            }
        }       
    }

    /*-------------------------------------------------------------------------
    *///----------------------------------------------------------------------- 
    public FXEvent OnGUI(FXEvent fx)
    {
        if(fx == null)
            return fx;

        GUILayout.BeginHorizontal();
        FXEvent ret = null;

        //ifdisplay the enum changes, create new FXEvent sub type corresponding to the
       so //we newcan enum,see andwhat returnwe're itediting
        FXEventType newType = (FXEventType) EditorGUILayout.EnumPopup(fx.type, GUILayout.Width(60));
        ifLabel(newType != fx.type)
        {
            switch(newType)
            {
                case FXEventType.kNone:
                    ret = ScriptableObject.CreateInstance<FXEvent>();
                    break;
            
                case FXEventType.kSFX:
                    ret = ScriptableObject.CreateInstance<SFXEvent>();
                    break;
            
                case FXEventType.kAnim:
                    ret = ScriptableObject.CreateInstance<AnimEvent>ToString();
                    break;
                
                default:
                   , Debug.LogError("FXEventGUILayout.OnGUIWidth(60): Magic Error");
                    break;              
            }
            
            ret.type = newType;
            dirtied = true;
        }

        //handle element GUI, if that applies
        if(fx.type != FXEventType.kNone)
        {
            OnSubGUI(fx);
        }
        else
            GUILayout.Label("");

        GUILayout.EndHorizontal();
        return ret;
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    public void OnGUI(FXTriggerBase trigger)
    {
        FXTriggerBase triggerBase = trigger as FXTriggerBase;
        if(triggerBase == null)
        {
            Debug.LogError("FXTriggerBaseInspector.OnGUI() : null item to inspect?");
            return;
        }
        
        //build an array of serialized versions of our array elements, so that they
        // save out properly
        int numEvents = triggerBase.events.Length;
        serial = new SerializedObject[numEvents];
        for(int e = 0; e < numEvents; e++)
        {
            serial[e] = new SerializedObject(triggerBase.events[e]);
        }

        dirtied = false;
                
        //Do inspector GUI of our array elements
        int ret = ArrayGUI("events", trigger.events, trigger.gameObject);

        //handle deletion or addition of elements
        if (ret >= 0 && ret < trigger.events.Length)
            ArrayHandleDelete(ref trigger.events, ret);
        else if (ret == trigger.events.Length)
            ArrayHandleAdd(ref trigger.events);
                
        //if any changes have been registered, save our serialized versions out
        if(dirtied)
        {
            numEvents = serial.Length;
            for(int e = 0; e < numEvents; e++)
            {
                serial[e].ApplyModifiedProperties();
            }
            
            EditorUtility.SetDirty(trigger);
        }
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    int ArrayGUI(string label, FXEvent[] arr, GameObject go)
    {
        int ret = -1;
        Color defColor = GUI.color;
 
        GUILayout.BeginVertical(GUILayout.Width(250));  
        {
            GUILayout.Label(label);

        //blue plus button that populates intsome numObjectsevents for us
        GUILayout.Label("");
        GUI.color = Color.cyan;
        bool add = GUILayout.Button("+", GUILayout.Width(20));
        if (add)
            ret = arr.Length;
        GUI.color = defColor;

        GUILayout.Label(label);

        int numObjects = arr.Length;
        for (int o = 0; o < numObjects; o++)
        {
            GUILayout.BeginHorizontal();
            {
                GUILayout.BeginHorizontal//draw custom GUI for each of the polymorphic fx event types
                FXEvent fx = arr[o];
                FXEvent newFX = OnGUI(fx);
                if(newFX != null)
                {
                    //Draw a red '-' button that will remove this element
                    GUI.color = Color.red;
                    bool hit = GUILayout.Button("-", GUILayout.Width(20));
                    if (hit)
                        ret = o;
                        
                    GUI.color = defColor;
                    
                    //spacer
                    GUILayout.Label("", GUILayout.Width(10));

                    //draw custom GUI for each of the polymorphic fx event types
                    FXEvent fx = arr[o];
                    FXEvent newFX = OnGUI(fx);
                    if(newFX != null)
                    {
                        arr[o] = newFX;
                        dirtied = true;
                        
                        serial[o] = new SerializedObject(arr[o]);
                        ScriptableObject.DestroyImmediate(fx);                      
                    }
                }
                GUILayout.EndHorizontal();
            }

            //finally, at the bottom draw a blue plus button that will 
            GUI.color = Color.cyan;
            bool add = GUILayout.Button("+", GUILayout.WidthEndHorizontal(20));
            if (add)
                ret = arr.Length;
                
      }      GUI.color = defColor;
        }
        GUILayout.EndVertical();
        return ret;
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    void ArrayHandleDelete(ref FXEvent[]  arr, int index)
    {   
        FXEvent fx = arr[index];
        ScriptableObject.DestroyImmediate(fx);
        
        //copy over the element we are deleting, and continue compacting the array
        for (int i = index; i < arr.Length - 1; i++)
        {
            arr[i] = arr[i + 1];
            serial[i] = serial[i+1];
        }

        //remove last element to finalize deletion
        System.Array.Resize(ref arr, arr.Length - 1);
        System.Array.Resize(ref serial, serial.Length - 1);
        dirtied = true;
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    void ArrayHandleAdd(ref FXEvent[]  arr)
    {
        int size = arr.Length;

        //add new element to the end
        System.Array.Resize(ref arr, size + 12);
        System.Array.Resize(ref serial, size + 12);
        
        arr[size] = ScriptableObject.CreateInstance<FXEvent>CreateInstance<AnimEvent>();
        serial[size] = new SerializedObject(arr[size]);
                
        arr[size+1] = ScriptableObject.CreateInstance<SFXEvent>();
        serial[size+1] = new SerializedObject(arr[size+1]);
        
        dirtied = true;
    }
}

and sample custom inspector that uses that - the bug is in here:

I apply my FXTriggerTimer MonoBehavior (which inherits from FXTriggerBase) to a test object, and then add an AnimEvent and a SFXEvent to the trigger. I then save the scene, and reload the scene, and my test object has an array of two null elements that (obviously) don't draw in the inspector. BUT, if I edit a script (e.g. hit space and save), the script refresh will magically repopulate with the correct two items. So I know that all of the pieces are there under the hood, they just need to be restored on scene load.

//Scripts/FXTriggerBase.cs
using UnityEngine;
using System.Collections;

public enum FXEventType {kNone, kAnim,  kSFX,}

/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[System.Serializable]
public class FXEvent: UnityEngine.ScriptableObject
{
    [SerializeField]
     public FXEventType  type;
     
    [SerializeField]
     public GameObject   go;
 
    virtual public void Fire()  {   }
};

/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[System.Serializable]
public class AnimEvent : FXEvent
{       
    [SerializeField]
     public string       animName = "";

    public void Fire()  {go.animation.Play(animName);   }
}

/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[System.Serializable]
public class SFXEvent: FXEvent
{
    [SerializeField]
     public AudioClip    clip;
    
    public void Fire()  {go.audio.PlayOneShot(clip);    }
}

/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[AddComponentMenu("")]  // prevents users from using this directly
public class FXTriggerBase : MonoBehaviour 
{
    [SerializeField]
     public FXEvent[] events = new FXEvent[]{};
    
    public void Fire()
    {
        for( int e = 0; e < events.Length; e++)
        {
            events[e].go = gameObject;
            events[e].Fire();
        }   
    }
}
//Editor/FXTriggerBaseUtil.cs
using UnityEngine;
using System.Collections;
using UnityEditor;
    
/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
public class FXEditorUtil
{
    SerializedObject[] serial;  
    public bool dirtied = false;
            
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    public void OnSubGUI(this FXEvent fx)
    {
        //having trouble getting polymorphism to work right in the inspector,
        // so this is what works.
        if(fx.type == FXEventType.kAnim)
            OnSubGUI((AnimEvent)fx);
        else if(fx.type == FXEventType.kSFX)
            OnSubGUI((SFXEvent)fx);
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    public void OnSubGUI(this AnimEvent anim)
    {       
        string newAnimName = GUILayout.TextField(anim.animName);
        if(newAnimName != anim.animName)
        {
            anim.animName = newAnimName;
            dirtied = true;
        }
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    public void OnSubGUI(this SFXEvent sfx)
    {
        AudioClip newSFX = (AudioClip)EditorGUILayout.ObjectField(sfx.clip, typeof(AudioClip));
        if(newSFX != sfx.clip)
        {
            sfx.clip =newSFX;
            dirtied = true;
        }
    }

    /*-------------------------------------------------------------------------
    *///----------------------------------------------------------------------- 
    public FXEvent OnGUI(FXEvent fx)
    {
        if(fx == null)
            return fx;

        GUILayout.BeginHorizontal();
        FXEvent ret = null;

        //if the enum changes, create new FXEvent sub type corresponding to the
        // new enum, and return it
        FXEventType newType = (FXEventType) EditorGUILayout.EnumPopup(fx.type, GUILayout.Width(60));
        if(newType != fx.type)
        {
            switch(newType)
            {
                case FXEventType.kNone:
                    ret = ScriptableObject.CreateInstance<FXEvent>();
                    break;
            
                case FXEventType.kSFX:
                    ret = ScriptableObject.CreateInstance<SFXEvent>();
                    break;
            
                case FXEventType.kAnim:
                    ret = ScriptableObject.CreateInstance<AnimEvent>();
                    break;
                
                default:
                    Debug.LogError("FXEvent.OnGUI(): Magic Error");
                    break;              
            }
            
            ret.type = newType;
            dirtied = true;
        }

        //handle element GUI, if that applies
        if(fx.type != FXEventType.kNone)
        {
            OnSubGUI(fx);
        }
        else
            GUILayout.Label("");

        GUILayout.EndHorizontal();
        return ret;
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    public void OnGUI(FXTriggerBase trigger)
    {
        FXTriggerBase triggerBase = trigger as FXTriggerBase;
        if(triggerBase == null)
        {
            Debug.LogError("FXTriggerBaseInspector.OnGUI() : null item to inspect?");
            return;
        }
        
        //build an array of serialized versions of our array elements, so that they
        // save out properly
        int numEvents = triggerBase.events.Length;
        serial = new SerializedObject[numEvents];
        for(int e = 0; e < numEvents; e++)
        {
            serial[e] = new SerializedObject(triggerBase.events[e]);
        }

        dirtied = false;
                
        //Do inspector GUI of our array elements
        int ret = ArrayGUI("events", trigger.events, trigger.gameObject);

        //handle deletion or addition of elements
        if(ret >= 0 && ret < trigger.events.Length)
            ArrayHandleDelete(ref trigger.events, ret);
        else if (ret == trigger.events.Length)
            ArrayHandleAdd(ref trigger.events);
                
        //if any changes have been registered, save our serialized versions out
        if(dirtied)
        {
            numEvents = serial.Length;
            for(int e = 0; e < numEvents; e++)
            {
                serial[e].ApplyModifiedProperties();
            }
            
            EditorUtility.SetDirty(trigger);
        }
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    int ArrayGUI(string label, FXEvent[] arr, GameObject go)
    {
        int ret = -1;
        Color defColor = GUI.color;
        GUILayout.BeginVertical(GUILayout.Width(250));  
        {
            GUILayout.Label(label);

            int numObjects = arr.Length;
            for (int o = 0; o < numObjects; o++)
            {
                GUILayout.BeginHorizontal();
                {
                    //Draw a red '-' button that will remove this element
                    GUI.color = Color.red;
                    bool hit = GUILayout.Button("-", GUILayout.Width(20));
                    if (hit)
                        ret = o;
                        
                    GUI.color = defColor;
                    
                    //spacer
                    GUILayout.Label("", GUILayout.Width(10));

                    //draw custom GUI for each of the polymorphic fx event types
                    FXEvent fx = arr[o];
                    FXEvent newFX = OnGUI(fx);
                    if(newFX != null)
                    {
                        arr[o] = newFX;
                        dirtied = true;
                        
                        serial[o] = new SerializedObject(arr[o]);
                        ScriptableObject.DestroyImmediate(fx);                      
                    }
                }
                GUILayout.EndHorizontal();
            }

            //finally, at the bottom draw a blue plus button that will 
            GUI.color = Color.cyan;
            bool add = GUILayout.Button("+", GUILayout.Width(20));
            if (add)
                ret = arr.Length;
                
            GUI.color = defColor;
        }
        GUILayout.EndVertical();
        return ret;
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    void ArrayHandleDelete(ref FXEvent[]  arr, int index)
    {   
        FXEvent fx = arr[index];
        ScriptableObject.DestroyImmediate(fx);
        
        //copy over the element we are deleting, and continue compacting the array
        for (int i = index; i < arr.Length - 1; i++)
        {
            arr[i] = arr[i + 1];
            serial[i] = serial[i+1];
        }

        //remove last element to finalize deletion
        System.Array.Resize(ref arr, arr.Length - 1);
        System.Array.Resize(ref serial, serial.Length - 1);
        dirtied = true;
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    void ArrayHandleAdd(ref FXEvent[]  arr)
    {
        int size = arr.Length;

        //add new element to the end
        System.Array.Resize(ref arr, size + 1);
        System.Array.Resize(ref serial, size + 1);
        
        arr[size] = ScriptableObject.CreateInstance<FXEvent>();
        serial[size] = new SerializedObject(arr[size]);
        
        dirtied = true;
    }
}

and sample custom inspector that uses that:

[EDIT: based on some feedback, I pruned the example down to the base functionality of the bug we are trying to solve. The description below remains accurate, but the repro has been shortened to:

  1. Add the FXTriggerTimer component to an object
  2. Hit the blue '+' button to add an AnimEvent and a SFXEvent
  3. Save the scene, leave the scene and come back to the scene
  4. Notice that the object has blank FXEvents instead of AnimEvents & SFXEvents
  5. Hit space in a script file and save to trigger a script refresh in Unity
  6. Notice that the object has had AnimEvents & SFXEvents restored

FINALLY: if anybody knows how to solve this problem without using scriptable or serializeable objects, that would be preferred. Thanks a million!]

I apply my FXTriggerTimer MonoBehavior (which inherits from FXTriggerBase) to a test object, and then hit the blue '+' button to add an AnimEvent and a SFXEvent to the trigger. I then save the scene, and reload the scene, and my test object has an array of two null elements that (obviously) don't draw in the inspector. BUT, if I edit a script, any script (e.g. hit space and save), the script refresh will magically repopulate with the correct items. So I know that all of the pieces are there under the hood, they just need to be restored on scene load.

//Scripts/FXTriggerBase.cs
using UnityEngine;
using System.Collections;

public enum FXEventType {kNone, kAnim,  kSFX,}
    
/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[System.Serializable]
public class FXEvent: UnityEngine.ScriptableObject
{
    [SerializeField]        public FXEventType  type;       
    [SerializeField]        public GameObject   go;    
    virtual public void Fire()  {   }
};

/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[System.Serializable]
public class AnimEvent : FXEvent
{       
    [SerializeField]    public string       animName = "";
    public AnimEvent()      {type = FXEventType.kAnim;}    
    public void Fire()      {go.animation.Play(animName);   }
}

/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[System.Serializable]
public class SFXEvent: FXEvent
{
    [SerializeField]    public AudioClip    clip;
    public SFXEvent()       {type = FXEventType.kSFX;}      
    public void Fire()      {go.audio.PlayOneShot(clip);    }
} 


/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
[AddComponentMenu("")]  // prevents users from using this directly
public class FXTriggerBase : MonoBehaviour 
{
    [SerializeField]    public FXEvent[] events = new FXEvent[]{};
    
    public void Fire()
    {
        for( int e = 0; e < events.Length; e++)
        {
            events[e].go = gameObject;
            events[e].Fire();
        }   
    }
}
//Editor/FXTriggerBaseUtil.cs
using UnityEngine;
using System.Collections;
using UnityEditor;
    
/*-----------------------------------------------------------------------------
*///---------------------------------------------------------------------------
public class FXEditorUtil
{
    SerializedObject[] serial;  
    public bool dirtied = false;
            
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    public void OnSubGUI(this FXEvent fx)
    {
        //having trouble getting polymorphism to work right in the inspector,
        // so this is what works.
        if(fx.type == FXEventType.kAnim)
        {
            AnimEvent anim = (AnimEvent)fx;
            string newAnimName = GUILayout.TextField(anim.animName);
            if(newAnimName != anim.animName)
            {
                anim.animName = newAnimName;
                dirtied = true;
            }
        }
        else if(fx.type == FXEventType.kSFX)
        {
            SFXEvent sfx = (SFXEvent)fx;
            AudioClip newSFX = (AudioClip)EditorGUILayout.ObjectField(sfx.clip, typeof(AudioClip));
            if(newSFX != sfx.clip)
            {
                sfx.clip =newSFX;
                dirtied = true;
            }
        }       
    }

    /*-------------------------------------------------------------------------
    *///----------------------------------------------------------------------- 
    public FXEvent OnGUI(FXEvent fx)
    {
        if(fx == null)
            return fx;

        GUILayout.BeginHorizontal();
        FXEvent ret = null;

        //display the enum type so we can see what we're editing
        GUILayout.Label(fx.type.ToString(), GUILayout.Width(60));       

        //handle element GUI, if that applies
        if(fx.type != FXEventType.kNone)
            OnSubGUI(fx);
        else
            GUILayout.Label("");

        GUILayout.EndHorizontal();
        return ret;
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    public void OnGUI(FXTriggerBase trigger)
    {
        FXTriggerBase triggerBase = trigger as FXTriggerBase;
        if(triggerBase == null)
        {
            Debug.LogError("FXTriggerBaseInspector.OnGUI() : null item to inspect?");
            return;
        }
        
        //build an array of serialized versions of our array elements, so that they
        // save out properly
        int numEvents = triggerBase.events.Length;
        serial = new SerializedObject[numEvents];
        for(int e = 0; e < numEvents; e++)
        {
            serial[e] = new SerializedObject(triggerBase.events[e]);
        }

        dirtied = false;
                
        //Do inspector GUI of our array elements
        int ret = ArrayGUI("events", trigger.events, trigger.gameObject);

        //handle addition of elements
        if (ret >= 0)
            ArrayHandleAdd(ref trigger.events);
                
        //if any changes have been registered, save our serialized versions out
        if(dirtied)
        {
            numEvents = serial.Length;
            for(int e = 0; e < numEvents; e++)
            {
                serial[e].ApplyModifiedProperties();
            }
            
            EditorUtility.SetDirty(trigger);
        }
    }
    
    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    int ArrayGUI(string label, FXEvent[] arr, GameObject go)
    {
        int ret = -1;
        Color defColor = GUI.color;
 
        GUILayout.BeginVertical(GUILayout.Width(250));  

        //blue plus button that populates some events for us
        GUILayout.Label("");
        GUI.color = Color.cyan;
        bool add = GUILayout.Button("+", GUILayout.Width(20));
        if (add)
            ret = arr.Length;
        GUI.color = defColor;

        GUILayout.Label(label);

        int numObjects = arr.Length;
        for (int o = 0; o < numObjects; o++)
        {
            GUILayout.BeginHorizontal();
            {
                //draw custom GUI for each of the polymorphic fx event types
                FXEvent fx = arr[o];
                FXEvent newFX = OnGUI(fx);
                if(newFX != null)
                {
                    arr[o] = newFX;
                    dirtied = true;
                        
                    serial[o] = new SerializedObject(arr[o]);
                    ScriptableObject.DestroyImmediate(fx);                      
                }
            }
            GUILayout.EndHorizontal();
        }               
        GUILayout.EndVertical();
        return ret;
    }

    /*-------------------------------------------------------------------------
    *///-----------------------------------------------------------------------
    void ArrayHandleAdd(ref FXEvent[]  arr)
    {
        int size = arr.Length;

        //add new element to the end
        System.Array.Resize(ref arr, size + 2);
        System.Array.Resize(ref serial, size + 2);
        
        arr[size] = ScriptableObject.CreateInstance<AnimEvent>();
        serial[size] = new SerializedObject(arr[size]);
                
        arr[size+1] = ScriptableObject.CreateInstance<SFXEvent>();
        serial[size+1] = new SerializedObject(arr[size+1]);
        
        dirtied = true;
    }
}

and sample custom inspector that uses that - the bug is in here:

simplified MonoBehavior use case to direct attention to actual problem code.
Source Link
Loading
Source Link
Loading