Unity 编辑器脚本:如何使用同一个按钮执行两个不同的操作?

Unity editor scripting : how to execute two different actions with the same button?

我写了一些代码来向我在 Unity 中的自定义编辑器添加一个按钮。我想要的是以下内容。当我单击一次该按钮时,会添加一个组件,当我再次单击该按钮时,该组件会被删除。在下面的简单代码中,我只是尝试在单击按钮时打印“添加”或“删除”。我注意到变量 toggleRigidBody 取值 true 但紧接着取值 false。即使我从未在代码中明确更改它,它也不会保持“真实”。 “else if”永远不会被解雇。我不知道为什么。

using UnityEditor;
using UnityEngine;
using SS3D.Engine.Inventory;

[CustomEditor(typeof(ContainerController))]
public class ContainerControllerEditor : Editor
{
    private bool toggleRigidBody = false;

    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();
        ContainerController containerController = (ContainerController)target;
        Debug.Log(toggleRigidBody);

        bool buttonPressed = GUILayout.Button("AddAttachedContainer");

        if (buttonPressed && toggleRigidBody == false)
        {
            Debug.Log("Add");
            toggleRigidBody = true;         
        }

        else if (buttonPressed && toggleRigidBody == true)
        {
            Debug.Log("Remove");
            toggleRigidBody = false;        
        }
    }
}

我的代码仅在单击按钮时打印“添加”。这里发生了什么?

编辑器对象在显示时创建,不显示时销毁,因此为了让数据持久保存,您需要将值存储在其他地方。所以这绝对是正在发生的事情。使变量值在会话之间保持不变的最简单方法是使用 EditorPrefs 来保存变量值。这是最简单的方法,因此您可以使用它来保存 toggleRigidBody 值。

https://docs.unity3d.com/ScriptReference/EditorPrefs.SetBool.html

这里的主要问题是编辑器实例是在单击对象并加载检查器时创建的。然后一旦对象失去焦点并且不再显示该对象的检查器,它就会被销毁。

=> 你的标志 toggleRigidBody 不持久!

您更想做的是序列化对象内部的标志,或者更好:序列化引用本身。

你这样

  • 已经可以访问脚本中的引用以备运行时需要
  • 在编辑器中包含引用,以便 a) 检查它是否存在以及 b) 能够直接删除它

所以让你的class喜欢

public class ContainerController : MonoBehaviour
{
    // Store the reference in a field
    [SerializeField] private Rigidbody _rigidbody;

    ...
}

编辑器可能看起来像

[CustomEditor(typeof(ContainerController))]
public class ContainerControllerEditor : Editor
{
    private SerializedProperty _rigidbody;

    ContainerController containerController;

    private void OnEnable()
    {
        _rigidbody = serializedObject.FindProperty("_rigidbody");
        containerController = (ContainerController)target;
    }

    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();
        
        // Loads all current serialized values from the target into the serialized properties
        serializedObject.Update();

        // If the _rigidbody field is not assigned
        // try GetComponent as fallback
        if(!_rigidbody.objectReferenceValue) _rigidbody.objectReferenceValue = containerController.GetComponent<Rigidbody>();

        // simply put everything that belongs to one button click inside one if block
        // this is easier to maintain and read
        // Of course alternatively you could also simply have two completely different buttons to display 
        // depending on the value of "_rigidbody.objectReferenceValue"
        if(GUILayout.Button(_rigidbody.objectReferenceValue ? "Remove Rigidbody" : "Add Rigidbody")
        {
            // Is there a Rigidbody?
            if(_rigidbody.objectReferenceValue)
            {
                // Yes -> destroy it
                // There are two different destroy methods depending whether you are 
                // in Play mode or Edit mode
                if(Application.isPlaying)
                {
                    Destroy(_rigidbody.objectReferenceValue);
                }
                else
                {
                    DestroyImmediate(_rigidbody.objectReferenceValue);
                }
            }
            // Otherwise the field is currently not set and no component was found using GetComponent
            else
            {
                // Add the component via the ObjectFactory
                // this enabled undo/redo and marks the scene dirty etc
                // and assign it to the serialized property
                _rigidbody.objectReferenceValue = ObjectFactory.AddComponent<Rigidbody>(target.gameObject);
            }
        }

        // Writes back all modified properties to the target and takes care of Undo/Redo and marking dirty
        serializedObject.ApplyModifiedProperties ();
    }
}