Custom Editor losing settings on Play

I think the behavior I’m seeing is a bug, but I thought I’d ask it here first in case I’m just missing something. When writing my custom editors, all of my changes are being discarded when I hit the “play” button for any object I need ‘type’ I need to instantiate. I’ll keep the code simple:

First, I have a class called HiddenObject:

using UnityEngine;
using System.Collections;

public class HiddenObject {
    public GameObject hiddenObj { get; set; }
}

Next, here’s the script I’ll attach to an object in my Scene:

using UnityEngine;
using System.Collections;

public class Inventory : MonoBehaviour {

    public HiddenObject hiddenObject = new HiddenObject();
}

Finally, here’s my custom editor script:

using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor(typeof(Inventory))]
public class InventoryEditor : Editor {
    private Inventory _inv;

    void Awake()
    {
        _inv = (Inventory)target;
    }

    public override void OnInspectorGUI()
    {        
        _inv.hiddenObject.hiddenObj = (GameObject)EditorGUILayout.ObjectField("Hidden Object", _inv.hiddenObject.hiddenObj, typeof(GameObject), true);
        if(GUI.changed)
             EditorUtility.SetDirty(_inv);    
    }	
}

When I click on my Object in the Scene Hierarchy I can see the Custom Inspector displaying properly and I can click on the Game Object Field and select something or drag a game object to it. This all works correctly.

The moment I click the “Play” button, it’s all nulled out though. Clicking stop doesn’t bring back my changes, everything has been nulled out.

I’m certain this has to do with the fact that I’m having to instantiate the HiddenObject (new HiddenObject()) but not exactly sure how else I’m supposed to do it. Am I missing something, or is this a bug?

Ok, I figured this out - it was a combination of two things. First (as Adamcbrz pointed out) I was missing [System.Serializable] in my custom class. The second thing I had wrong is that I didn’t need to get; set; any of the variables in my class. Here’s the final code that works. Hopefully this will help some of you:

Custom class:

using UnityEngine;
using System.Collections;

[System.Serializable]
public class HiddenObject {
    public GameObject hiddenObj;
}

The Inventory Script:

using UnityEngine;
using System.Collections;

public class Inventory : MonoBehaviour {

    public HiddenObject hiddenObject;
}

Custom Editor Script

using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor(typeof(Inventory))]
public class InventoryEditor : Editor {
    private Inventory _inv;

    void Awake()
    {
        _inv = (Inventory)target;
    }

    public override void OnInspectorGUI()
    {        
        _inv.hiddenObject.hiddenObj = (GameObject)EditorGUILayout.ObjectField("Hidden Object", _inv.hiddenObject.hiddenObj, typeof(GameObject), true);
        if(GUI.changed)
             EditorUtility.SetDirty(_inv);    
    }   
}

Tim,

Look into http://unity3d.com/support/documentation/ScriptReference/Serializable.html
For custom class you need to add this. I am not sure if you need this for both classes.

Hope this helps

I know this is a very old post but I would like to share what it worked for me.
I made this method (you should put this inside the class you want to use it):

private void ChangeCheck<T>(Func<T> function, ref T field, string reason)
{
    // https://docs.unity3d.com/ScriptReference/Undo.RecordObject.html
    EditorGUI.BeginChangeCheck();
    T value = function();
    if (EditorGUI.EndChangeCheck())
    {
        Undo.RecordObject(target, reason);
        field = value;
    }
}

Whenever the code (custom field) executed in func is changed the values are stored. In the link inside the method, explains why I did that.

So, you could use this as:

ChangeCheck(() => (GameObject)EditorGUILayout.ObjectField("Hidden Object", _inv.hiddenObject.hiddenObj, typeof(GameObject), true), ref _inv.hiddenObject.hiddenObj, "Change Hidden Object");

I’ve been working on an editor script and am coming up on the same issues. I’ve thoroughly looked through the formums and unityanswers but haven’t figured it out. I’ve tried all of the Serializable stuff on just every variable in the problem but no luck.

DESTRUCTIONOBJECT.js

@SerializeField

var destroyedVersion : GameObject;

var destroyOn : DestroyMode;

enum DestroyMode{Collision, Play, Custom};

var collisionForce : float;

var nonRigidBodyCollision : boolean;

var impactForceMagnitude : float;

function Start ()

{

if(destroyOn == DestroyMode.Play)

	Destroy();

}

function OnCollisionStay(col : Collision)

{

impactForceMagnitude = col.impactForceSum.sqrMagnitude;

if(destroyOn == DestroyMode.Collision)

if(nonRigidBodyCollision)
  
	Destroy();
  
else if(!nonRigidBodyCollision)
  
	if(col.rigidbody != null)
  
		if(impactForceMagnitude > collisionForce)
  
			Destroy();

}

function Destroy ()

{

Instantiate(destroyedVersion, transform.position, this.transform.rotation);

}


DESTRUCTIONOBJECTINSPECTOR.js

@CustomEditor(DestructionObject)

class DestructionEditor extends Editor

{

var shatterObject : GameObject;  

var destroyMode : DestroyMode = DestroyMode.Collision;  

var collisionForce : float;  

var nonRigidBodyCollision : boolean;  

function OnEnable ()  

{  

	//shatterObject = target.shatterObject;  

	destroyMode = target.destroyOn;  

	collisionForce = target.collisionForce;  

	nonRigidBodyCollision = target.nonRigidBodyCollision;  

}  

function OnInspectorGUI ()  

{	  

	EditorGUILayout.BeginVertical();  

	EditorGUILayout.BeginHorizontal();  

		shatterObject = EditorGUILayout.ObjectField("Shattered Version: ", shatterObject, GameObject);  

	EditorGUILayout.EndHorizontal();  

	EditorGUILayout.BeginHorizontal();  

		destroyMode = EditorGUILayout.EnumPopup("Destroy On:", destroyMode);  

	EditorGUILayout.EndHorizontal();  

		if(destroyMode == DestroyMode.Collision)  

		{  

			EditorGUILayout.BeginHorizontal();  

			collisionForce = EditorGUILayout.FloatField("Minimum Collision Force", collisionForce);  

			EditorGUILayout.EndHorizontal();  

			EditorGUILayout.BeginHorizontal();  

			nonRigidBodyCollision= EditorGUILayout.Toggle("Non-Rigidbody Collision?", nonRigidBodyCollision);  

			EditorGUILayout.EndHorizontal();  

			EditorGUILayout.LabelField("Impact Force: ", target.impactForceMagnitude.ToString());  

		}  

		else if(destroyMode == DestroyMode.Custom)  

		{  

			EditorGUILayout.LabelField("Call the 'Destroy' function to shatter this object.");  

			EditorGUILayout.LabelField("Example: ");  

			EditorGUILayout.LabelField("var destructionObject : DestructionObject; ");  

			EditorGUILayout.LabelField("function Start()");  

			EditorGUILayout.LabelField("destructionObject.Destroy();");  

		}  

	EditorGUILayout.EndVertical();  

	target.destroyedVersion = shatterObject;  

	target.destroyOn = destroyMode;  

	target.collisionForce = collisionForce;  

	target.nonRigidBodyCollision = nonRigidBodyCollision;  

	if(GUI.changed)  

    	EditorUtility.SetDirty(target);  

}  

}

I was having the exact same issue, but it was happening on strings, floats, and well, everything. I fixed it by removing the script from the object and then putting it back in. My best guess is somehow the data saved went all wrong when I added the custom editor to my script, but who knows…