[Solved] Custom editor resets after Play?
Hello all,
Asking for your help and guidance on this one.
I am making a custom editor to instantiate and destroy multiple objects using two GUI buttons. In edit mode, all is working fine, I can instantiate multiple prefabs, and then destroy them, one by one, from last instantiated to first, but as soon as I press "Play" and the "Stop", I am not able to destroy any previously instantiated prefabs (instantiated before play-mode). I can instantiate new prefabs and then destroy them, but as for those prefabs that were instantiated before I hit play - they stay unaffected.
Now, I am not that much worried that this happens in play mode, but I definitely want to continue from the point before play mode.
Whenever a prefab is instantiated, I add it to a Stack and keep a track of the size. After I hit play - stop, it seems to reset to 0.
After browsing numerous posts about a similar problem and checking Unity's API, I do realize I need to figure out a way to save the custom editor settings and variables by serializing them or making them dirty, I just do not know how to accomplish this. I've tried what was suggested in those posts, but nothing seems to be working in my case (or I'm doing something wrong).
I have a Monobehaviour script and an Editor script. I'll post the snippets for a single type of the object, as other parts are the same.
Here is the first one:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable, ExecuteInEditMode]
public class ObjectControl: MonoBehaviour {
[SerializeField, HideInInspector]
public Stack<GameObject> undoStack = new Stack<GameObject>();
public GameObject instance;
public int undoStackSize;
public int objectSelectionIndex = 0;
public void placeObject()
{
switch (objectSelectionIndex)
{
case 1:
Debug.Log("Just received a number 1 from the editor");
GameObject object_A = Resources.Load("Prefabs/Object_A") as GameObject;
instance = Instantiate(object_A, this.transform.position, this.transform.rotation, this.transform);
undoStack.Push(instance);
undoStackSize = undoStack.Count;
break;
case 4:
Debug.Log("Just received a number 4 from the editor, deleting the object");
if (undoStack.Count > 0)
{
GameObject objToUndo = undoStack.Pop();
DestroyImmediate(objToUndo);
undoStackSize--;
}
else
{
Debug.Log("Stack is empty! Stack size is: " + undoStack.Count);
}
break;
}
}
And the Editor script:
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
[CustomEditor(typeof(ObjectControl)), CanEditMultipleObjects]
public class ObjectControlEditor : Editor
{
int objectSelectionToolbar = 0;
int numberOfPossibleUndo;
bool chooseOption = false;
bool objectSelectionFoldout = false;
public ObjectControl scriptTarget;
public void Awake()
{
scriptTarget = (ObjectControl)target;
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
GUI.changed = false;
chooseOption = EditorGUILayout.Foldout(chooseOption, "Choose a segment to add:");
if (chooseOption)
{
EditorGUILayout.BeginVertical();
stationSelectionFoldout = GUILayout.Toggle(stationSelectionFoldout, "" + (stationSelectionFoldout ? "▼ Object selection ▼" : "► Object selection ◄"), "Button", GUILayout.MaxWidth(Screen.width), GUILayout.Height(25));
if (objectSelectionFoldout)
{
GUILayout.Space(5); //Space before a text box
GUILayout.Box("Select lenght of the station:");
GUILayout.Space(5); //Space after a text box and before a toolbox
string[] objectSelectionToolbarOptions = new string[] { "Object A", "Object B", "Object C" };
stationSelectionToolbar = GUILayout.Toolbar(objectSelectionToolbar, objectSelectionToolbarOptions, GUILayout.MinWidth(Screen.width), GUILayout.Height(50));
GUILayout.Space(5);
RollerCoasterBuilder scriptTarget = (RollerCoasterBuilder)target;
numberOfPossibleUndo = scriptTarget.undoStackSize;
switch (objectSelectionToolbar)
{
case 0:
GUILayout.BeginHorizontal();
if (GUILayout.Button("Place selected object", GUILayout.Height(30)))
{
scriptTarget.objectSelectionIndex = 1;
scriptTarget.PlaceObject();
}
GUILayout.Space(5);
if (GUILayout.Button("Undo" + "(" + numberOfPossibleUndo + ")", GUILayout.Height(30)))
{
scriptTarget.objectSelectionIndex = 4;
scriptTarget.PlaceObject();
}
GUILayout.EndHorizontal();
break;
}
}
}
if (GUI.changed)
{
EditorUtility.SetDirty(target);
}
}
Any ideas or suggestions are welcome. Thanks in advance ;)
Answer by jdean300 · Feb 22, 2017 at 07:05 PM
Stacks cannot be serialized. You need to use a List<GameObject>
.
Answer by UnityCoach · Feb 22, 2017 at 07:27 PM
There's already a whole Undo system for Custom Editors that you can use.
Hi UnityCoach,
I have read through this page a lot of times but I'm still unable to get my head around how to implement this in my code. Could you maybe advise or point to the correct direction, as I am lost here.
Thanks
Sure, in a nutshell, most operations are caught by the undo system, with a few exceptions that you must handle, like adding a component where gameObjectToAddComponentTo.AddComponent<$$anonymous$$yComponent>();
becomes Undo.AddComponent<$$anonymous$$yComponent>(gameObjectToAddComponentTo);
. $$anonymous$$ost cases are given in the docs.
Now, the undo system will gather all operations that happened in the context of a click and group them in one. You can still control this further like :
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName ("Add Special Items"); // don't add "Undo" at the beginning or it'll print "Undo Undo"
Undo.CollapseUndoOperations (undoGroup);
You can also record all object (component) properties
Undo.RecordObject (_component, "Set Component Properties");
Answer by Rickenbaker · Feb 23, 2017 at 09:49 AM
Thanks for everyone for their input,
I was finally able to implement Undo/Redo functionality in my Editor script.
I've also decided to ditch the Stack and carry on with removing child objects, one by one, from last to first, as can be seen in this post: http://stackoverflow.com/questions/32402833/simple-way-to-delete-the-last-child-of-a-gameobject/32402959
No more problems after play-mode :)
Have a nice day!