DBurn
DBurn

Reputation: 23

Custom inspector reverting values back to previous values on Play in Unity

So in my game I have an object that I need to move smoothly from Vector3 fromPosition to Vector3 toPosition at speed float speed, and then go back to where it started. All very simple, but to try and make life easier when setting up levels I decided to make a custom inspector for this script with buttons that allow me to set the target positions to the current position of the object, so I can just move it to where it needs to be and click a button, rather that typing in all the coordinates. Thought I had it all working but then started seeing some very strange behaviour, after playing around it seems to be as follows: The first time a button is used all is well. Every time the button is used after that, the values change properly in the inspector, but upon hitting Play the values of toPosition and fromPosition are reverted to what they were the first time the button was used. (They don't revert back again on Stop). However if I type the values in manually, it works perfectly. Very strange, does anyone have any idea what might be happening here? The code for the script and custom inspector are bellow.

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class MovingGrapple : MonoBehaviour
{
    public Vector3 fromPosition;
    public Vector3 toPosition;
    public float speed;

    Rigidbody thisBody;
    Grapple player;
    // Start is called before the first frame update
    void Start()
    {
        thisBody = GetComponent<Rigidbody>();
        player = GameObject.Find("Head").GetComponent<Grapple>();
    }


    private void FixedUpdate()
    {
        thisBody.MovePosition(Vector3.MoveTowards(transform.position, toPosition, Time.fixedDeltaTime * speed));
        if(transform.position == toPosition)
        {
            transform.position = fromPosition;
            if (player.activeTarget != null && GetComponentsInChildren<Transform>().Contains(player.activeTarget.transform))
            {
                player.BreakGrapple();
                GameObject.Destroy(player.activeTarget);
            }
        }
    }

    public void SetFromPosition()
    {
        fromPosition = transform.position;
    }

    public void SetToPosition()
    {
        toPosition = transform.position;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(MovingGrapple))]
public class MovingGrappleInspector : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        MovingGrapple myTarget = (MovingGrapple)target;

        if (GUILayout.Button("Set From position."))
        {
            myTarget.SetFromPosition();
        }
        if (GUILayout.Button("Set To position."))
        {
            myTarget.SetToPosition();
        }
    }
}

Thanks.

Upvotes: 2

Views: 3622

Answers (1)

derHugo
derHugo

Reputation: 90590

This will not only happen if you press play .. your changes are never saved!

If possible you should never mix editor scripts with direct access to the target except you know exactly what you're doing!

You would especially need to "manually" mark your changed object as dirty. Otherwise the changes are only temporary until your object is deserialized again (Enter/Exit PlayMode or reload of the Scene or asset).

You could before the changes add a Undo.RecordObject

if (GUILayout.Button("Set From position."))
{
    Undo.RecordObject(myTarget, "SetFromPosition");
    myTarget.SetFromPosition();      
}
if (GUILayout.Button("Set To position."))
{
     Undo.RecordObject(myTarget, "SetToPosition");
     myTarget.SetToPosition();
}

Also (sounds unlikely in your use-case but)

Important: To correctly handle instances where objectToUndo is an instance of a Prefab, PrefabUtility.RecordPrefabInstancePropertyModifications must be called after RecordObject.


In general rather always go through SerializedProperty where possible like e.g.

[CustomEditor(typeof(MovingGrapple))]
public class MovingGrappleInspector : Editor
{
    private SerializedProperty fromPosition;
    private SerializedProperty toPosition;

    private MovingGrapple myTarget

    private void OnEnable()
    {
        fromPosition = serializedObject.FindProperty("fromPosition");
        toPosition = serializedObject.FindProperty("toPosition");

        myTarget = (MovingGrapple)target;
    }


    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        // This loads the current real values into the serialized properties
        serializedObject.Update();

        if (GUILayout.Button("Set From position."))
        {
            // Now go through the SerializedProperty
            fromPosition.vector3Value = myTarget.transform.position;
        }
        if (GUILayout.Button("Set To position."))
        {
            toPosition.vector3Value = myTarget.transform.position;
        }

        // This writes back any changes properties into the actual component
        // This also automatically handles all marking the scene and assets dirty -> saving
        // And also handles proper Undo/Redo
        serializedObject.ApplyModifiedProperties();
    }
}

Upvotes: 2

Related Questions