自定义检查器将值恢复为 Play in Unity 之前的值
Custom inspector reverting values back to previous values on Play in Unity
所以在我的游戏中我有一个对象,我需要以 float speed
的速度从 Vector3 fromPosition
平滑地移动到 Vector3 toPosition
,然后回到它开始的地方。一切都非常简单,但为了在设置关卡时尝试让生活更轻松,我决定为这个脚本制作一个自定义检查器,带有允许我将目标位置设置为对象当前位置的按钮,这样我就可以将它移动到在需要的地方单击按钮,而不是输入所有坐标。以为我已经完成了所有工作,但后来开始看到一些非常奇怪的行为,在尝试之后似乎如下所示:第一次使用按钮时一切正常。此后每次使用按钮时,检查器中的值都会正确更改,但在点击播放时 toPosition
和 fromPosition
的值将恢复为第一次使用按钮时的值。 (他们不会在停止时再次恢复)。但是,如果我手动输入值,它会完美运行。很奇怪,有人知道这里会发生什么吗?脚本和自定义检查器的代码如下。
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();
}
}
}
谢谢。
这不仅会在您按下播放时发生..您的更改从未保存!
如果可能,您永远不要将编辑器脚本与直接访问 target
混合使用,除非您确切地知道自己在做什么!
您尤其需要“手动”将更改的对象标记为脏。否则,更改只是暂时的,直到您的对象再次反序列化(Enter/Exit PlayMode 或重新加载场景或资产)。
您可以在更改之前添加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();
}
另外(在您的用例中听起来不太可能但是)
Important: To correctly handle instances where objectToUndo is an instance of a Prefab, PrefabUtility.RecordPrefabInstancePropertyModifications
must be called after RecordObject
.
一般来说,尽可能总是通过 SerializedProperty
,例如
[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();
}
}
所以在我的游戏中我有一个对象,我需要以 float speed
的速度从 Vector3 fromPosition
平滑地移动到 Vector3 toPosition
,然后回到它开始的地方。一切都非常简单,但为了在设置关卡时尝试让生活更轻松,我决定为这个脚本制作一个自定义检查器,带有允许我将目标位置设置为对象当前位置的按钮,这样我就可以将它移动到在需要的地方单击按钮,而不是输入所有坐标。以为我已经完成了所有工作,但后来开始看到一些非常奇怪的行为,在尝试之后似乎如下所示:第一次使用按钮时一切正常。此后每次使用按钮时,检查器中的值都会正确更改,但在点击播放时 toPosition
和 fromPosition
的值将恢复为第一次使用按钮时的值。 (他们不会在停止时再次恢复)。但是,如果我手动输入值,它会完美运行。很奇怪,有人知道这里会发生什么吗?脚本和自定义检查器的代码如下。
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();
}
}
}
谢谢。
这不仅会在您按下播放时发生..您的更改从未保存!
如果可能,您永远不要将编辑器脚本与直接访问 target
混合使用,除非您确切地知道自己在做什么!
您尤其需要“手动”将更改的对象标记为脏。否则,更改只是暂时的,直到您的对象再次反序列化(Enter/Exit PlayMode 或重新加载场景或资产)。
您可以在更改之前添加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();
}
另外(在您的用例中听起来不太可能但是)
Important: To correctly handle instances where objectToUndo is an instance of a Prefab,
PrefabUtility.RecordPrefabInstancePropertyModifications
must be called afterRecordObject
.
一般来说,尽可能总是通过 SerializedProperty
,例如
[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();
}
}