我的项目中保存了一个资产,它代表可序列化的可脚本编写对象。该对象的代码很简单:
using UnityEngine;
using System.Collections;
public class TestScriptable : ScriptableObject {
public float gravity = .3f;
public float plinkingDelay = .1f;
public float storedExecutionDelay = .3f;
}
在检查器中更改此对象的值没有问题,并且更改确实会保留并在退出→进入Unity后仍然存在。
我试图模仿检查员的行为Editor Window
。但是,我对所做的任何更改Editor Window
(虽然反映在中Inspector
)都不会持续存在。这是Editor
文件夹中的两个脚本:
第一个(辅助)脚本-该脚本用按钮替换了检查器字段(请参见上图),该按钮调用了我的custom EditorWindow
。
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(TestScriptable))]
public class TestScriptableEditor : Editor {
public override void OnInspectorGUI() {
if (GUILayout.Button("Open TestScriptableEditor"))
TestScriptableEditorWindow.Init();
}
}
第二个(有我的问题)-脚本,我尝试在其中更改资产值:
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class TestScriptableEditorWindow : EditorWindow {
public static TestScriptableEditorWindow testScriptableEditorWindow;
private TestScriptable testScriptable;
[MenuItem("Window/TestTaskIceCat/TestScriptableEditor")]
public static void Init() {
// initialize window, show it, set the properties
testScriptableEditorWindow = GetWindow<TestScriptableEditorWindow>(false, "TestScriptableEditorWindow", true);
testScriptableEditorWindow.Show();
testScriptableEditorWindow.Populate();
}
// initialization of my troubled asset
void Populate() {
Object[] selection = Selection.GetFiltered(typeof(TestScriptable), SelectionMode.Assets);
if (selection.Length > 0) {
if (selection[0] == null)
return;
testScriptable = (TestScriptable)selection[0];
}
}
public void OnGUI() {
if (testScriptable == null) {
/* certain actions if my asset is null */
return;
}
// Here is my tries to change values
testScriptable.gravity = EditorGUILayout.FloatField("Gravity:", testScriptable.gravity);
testScriptable.plinkingDelay = EditorGUILayout.FloatField("Plinking Delay:", testScriptable.plinkingDelay);
testScriptable.storedExecutionDelay = EditorGUILayout.FloatField("Stored Execution Delay:", testScriptable.storedExecutionDelay);
// End of the region of change values
}
void OnSelectionChange() { Populate(); Repaint(); }
void OnEnable() { Populate(); }
void OnFocus() { Populate(); }
}
我的问题是:我在做什么错?可能是什么问题呢?如何解决?我是否在编辑器窗口中错误地加载资产?要不然是啥?任何帮助/想法,将不胜感激。
好吧,一切都是简单而复杂的,并且同时又变得简单。
尽管检查器发生了视觉上的变化-这并不意味着数据实际上已更改。看起来一切正常,但是...我认为这是Unity的缺点
为了正常工作,您应该使用一些东西:
Undo state
被记录下来,让您使用恢复的变化Undo
系统。现在,我们需要做的就是在OnGUI()
方法的底部编写一些代码。
if (GUI.changed) {
// writing changes of the testScriptable into Undo
Undo.RecordObject(testScriptable, "Test Scriptable Editor Modify");
// mark the testScriptable object as "dirty" and save it
EditorUtility.SetDirty(testScriptable);
}
即您的代码将是这样的:
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class TestScriptableEditorWindow : EditorWindow {
public static TestScriptableEditorWindow testScriptableEditorWindow;
private TestScriptable testScriptable;
[MenuItem("Window/TestTaskIceCat/TestScriptableEditor")]
public static void Init() {
// initialize window, show it, set the properties
testScriptableEditorWindow = GetWindow<TestScriptableEditorWindow>(false, "TestScriptableEditorWindow", true);
testScriptableEditorWindow.Show();
testScriptableEditorWindow.Populate();
}
// initialization of troubled asset
void Populate() {
Object[] selection = Selection.GetFiltered(typeof(TestScriptable), SelectionMode.Assets);
if (selection.Length > 0) {
if (selection[0] == null)
return;
testScriptable = (TestScriptable)selection[0];
}
}
public void OnGUI() {
if (testScriptable == null) {
/* certain actions if my asset is null */
return;
}
testScriptable.gravity = EditorGUILayout.FloatField("Gravity:", testScriptable.gravity);
testScriptable.plinkingDelay = EditorGUILayout.FloatField("Plinking Delay:", testScriptable.plinkingDelay);
testScriptable.storedExecutionDelay = EditorGUILayout.FloatField("Stored Execution Delay:", testScriptable.storedExecutionDelay);
// Magic of the data saving
if (GUI.changed) {
// writing changes of the testScriptable into Undo
Undo.RecordObject(testScriptable, "Test Scriptable Editor Modify");
// mark the testScriptable object as "dirty" and save it
EditorUtility.SetDirty(testScriptable);
}
}
void OnSelectionChange() { Populate(); Repaint(); }
void OnEnable() { Populate(); }
void OnFocus() { Populate(); }
}
就这样。这很简单。
现在,故事的复杂部分...
SetDirty
-肯定不错。但是,此功能将在Unity> 5.3的版本中弃用。并且在某些版本中也会将其删除。什么时候?我不知道。除了使用之外,SetDirty
您还可以使用另一种方法:
您应该在两次调用之间执行自定义Editor或EditorWindow中的所有操作:
serializedObject.Update()
// Here is some of your code
serializedObject.ApplyModifiedProperties()
该代码包含:
serializedObject-获取对序列化对象的访问权并获取其属性。SerializedObject与以下项结合使用:
SerializedProperty-从中获取属性serializedObject
。所有数据将为SerializedProperty类型,例如
SerializedProperty myGravity = serializedObject.FindProperty("gravity");
SerializedProperty myPlinkingDelay = serializedObject.FindProperty("plinkingDelay");
...
etc.
SerializedObject.FindProperty-按名称查找序列化的属性。
SerializedProperty
。最后四个就像SetDirty
::它们会将修改后的对象(或场景)标记为“脏”并Undo states
为您创建。
因此,知道了这一点,我们可以获得以下内容:
using UnityEngine;
using UnityEditor;
public class TestScriptableEditorWindow : EditorWindow {
public static TestScriptableEditorWindow testScriptableEditorWindow;
private TestScriptable testScriptable;
// declaring our serializable object, that we are working on
private SerializedObject serializedObj;
[MenuItem("Window/TestTaskIceCat/TestScriptableEditor")]
public static void Init() {
testScriptableEditorWindow = GetWindow<TestScriptableEditorWindow>(false, "TestScriptableEditorWindow", true);
testScriptableEditorWindow.Show();
testScriptableEditorWindow.Populate();
}
// initialization of troubled asset
void Populate() {
Object[] selection = Selection.GetFiltered(typeof(TestScriptable), SelectionMode.Assets);
if (selection.Length > 0) {
if (selection[0] == null)
return;
testScriptable = (TestScriptable)selection[0];
// initialization of the serializedObj, that we are working on
serializedObj = new SerializedObject(testScriptable);
}
}
// our manipulation
public void OnGUI() {
if (testScriptable == null) {
/* certain actions if my asset is null */
return;
}
// Starting our manipulation
// We're doing this before property rendering
serializedObj.Update();
// Gets the property of our asset and скуфеу a field with its value
EditorGUILayout.PropertyField(serializedObj.FindProperty("gravity"), new GUIContent("Gravity"), true);
EditorGUILayout.PropertyField(serializedObj.FindProperty("plinkingDelay"), new GUIContent("Plinking Delay"), true);
EditorGUILayout.PropertyField(serializedObj.FindProperty("storedExecutionDelay"), new GUIContent("Stored Execution Delay"), true);
// Apply changes
serializedObj.ApplyModifiedProperties();
}
void OnSelectionChange() { Populate(); Repaint(); }
void OnEnable() { Populate(); }
void OnFocus() { Populate(); }
}
所以,这很简单,因为您应该只使用
Update
→动作→ ApplyModifiedProperties
。
但它是复杂的,因为你应该做了很多工作,有一大堆财产类:FindProperty
,PropertyField
и SerializedProperty
。
但是,当您了解它的工作原理时-它变得如此简单...
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句