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 ();
}
}
我写了一些代码来向我在 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 ();
}
}