在 Unity 上以编程方式绑定脚本 属性(编辑模式不是在运行时)
Programatically bind script property on Unity (edit mode not at runtime)
例如,我有 MonoBehaviour
导出 class 调用 Foo
:
class Foo : MonoBehaviour {
public Button lastButton;
public Image []images;
}
[CustomEditor(typeof(Foo))]
class FooEditor : Editor {
public override void OnInspectorGUI()
{
DrawDefaultInspector();
Foo foo = (Foo)target;
if(GUILayout.Button("Bind last Button and all Images")) {
/* how to bind programatically last foo.GetComponentsInChildren<Button>();
to foo.gameObject.scene's
foo.gameObject.GetComponent<Foo>().lastButton
in edit mode's property window
(without have to drag on the editor)?
*/
/* and how to bind programatically all foo.GetComponentsInChildren<Image>();
to foo.gameObject.scene's
foo.gameObject.GetComponent<Foo>().images
in edit mode's property window
(without have to drag all one by one)?
*/
}
}
}
我想自动执行 binding 而无需一个一个地拖动或在运行时执行,我如何以编程方式执行?
我能想到的唯一解决方法(如果没有 API)是保存场景,解析场景 yaml, then update the binding on the yaml file, and force UnityEditor to reload the yaml, but not sure if it's gonna work since loading yaml and rewriting it doesn't give equal value
虽然我仍然不明白 "binding" 的目的,老实说,在我看来,您实际上是在谈论通过编辑器脚本简单地引用所有组件,而不必拖放它们. (也许这里包含了绑定,我只是不知道它在那个名字下?)
首先确保 FooEditor
的整个代码放在名为 Editor
的文件夹中或将其包装在
中
#if UNITY_EDITOR
// any code using UnityEditor namespace
#endif
以便稍后在构建中删除它们。否则你会得到编译器错误,因为 UnityEditor
将不存在于构建中。
那么重要的是始终在编辑器脚本中操作 SerializedProperty
s of the SerializedObject
s。这会将所有标记处理为 dirty,为您保存更改和 Undo/Redo 条目。如果你碰巧直接操纵领域,你将不得不自己处理这些事情......
因此在实际目标组件及其序列化对象之间加载和写回值的关键作用是 SerializedObject.Update
and SerializedObject.ApplyModifiedProperties
- 我喜欢称它为 "shadow clone".
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.UI;
class Foo : MonoBehaviour
{
public Button lastButton;
public Image[] images;
}
#if UNITY_EDITOR
[CustomEditor(typeof(Foo))]
class FooEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
// fetch current values from the real target into the serialized "clone"
serializedObject.Update();
Foo foo = (Foo)target;
if (GUILayout.Button("Bind last Button and all Images"))
{
// get last button field as serialized property
var lastButtonField = serializedObject.FindProperty("lastButton");
// get last Button reference
// pass true to also get eventually inactive children
var buttons = foo.GetComponentsInChildren<Button>(true);
var lastButton = buttons[buttons.Length - 1];
// asign lastButton to the lastButtonField
lastButtonField.objectReferenceValue = lastButton;
// slightly more complex but similar
// get the field as serialized property
var imagesField = serializedObject.FindProperty("images");
// get the images references
// again pass true to also get eventually inactive ones
var images = foo.GetComponentsInChildren<Image>(true);
// now first set the according list size
imagesField.arraySize = images.Length;
// assign all references
for (var i = 0; i < imagesField.arraySize; i++)
{
// serialized property of the element in the field list
var entry = imagesField.GetArrayElementAtIndex(i);
// simply assign the reference like before with the button
entry.objectReferenceValue = images[i];
}
}
// write back changes to the real target
// automatically handles marking as dirty and undo/redo
serializedObject.ApplyModifiedProperties();
}
}
#endif
我希望我没有完全偏离,这就是你 "binding" 所说的。
请注意,如果您想进一步更改您选择的不是目标本身的引用的值,您可以轻松地使用 new SerializedObject(objectReference)
以获得序列化对象,例如
var serializedLastButton = new SerializedObject (lastButton);
或者也
var serializedLastButton = new SerializedObject(lastButtonField.objectReferenceValue);
现在,当再次使用 FindProperty(propertyName)
更改其中的任何字段时,确保再次使用
serializedLastButton.Update();
// make changes
serializedLastButton.ApplyModifiedProperties();
例如,我有 MonoBehaviour
导出 class 调用 Foo
:
class Foo : MonoBehaviour {
public Button lastButton;
public Image []images;
}
[CustomEditor(typeof(Foo))]
class FooEditor : Editor {
public override void OnInspectorGUI()
{
DrawDefaultInspector();
Foo foo = (Foo)target;
if(GUILayout.Button("Bind last Button and all Images")) {
/* how to bind programatically last foo.GetComponentsInChildren<Button>();
to foo.gameObject.scene's
foo.gameObject.GetComponent<Foo>().lastButton
in edit mode's property window
(without have to drag on the editor)?
*/
/* and how to bind programatically all foo.GetComponentsInChildren<Image>();
to foo.gameObject.scene's
foo.gameObject.GetComponent<Foo>().images
in edit mode's property window
(without have to drag all one by one)?
*/
}
}
}
我想自动执行 binding 而无需一个一个地拖动或在运行时执行,我如何以编程方式执行?
我能想到的唯一解决方法(如果没有 API)是保存场景,解析场景 yaml, then update the binding on the yaml file, and force UnityEditor to reload the yaml, but not sure if it's gonna work since loading yaml and rewriting it doesn't give equal value
虽然我仍然不明白 "binding" 的目的,老实说,在我看来,您实际上是在谈论通过编辑器脚本简单地引用所有组件,而不必拖放它们. (也许这里包含了绑定,我只是不知道它在那个名字下?)
首先确保 FooEditor
的整个代码放在名为 Editor
的文件夹中或将其包装在
#if UNITY_EDITOR
// any code using UnityEditor namespace
#endif
以便稍后在构建中删除它们。否则你会得到编译器错误,因为 UnityEditor
将不存在于构建中。
那么重要的是始终在编辑器脚本中操作 SerializedProperty
s of the SerializedObject
s。这会将所有标记处理为 dirty,为您保存更改和 Undo/Redo 条目。如果你碰巧直接操纵领域,你将不得不自己处理这些事情......
因此在实际目标组件及其序列化对象之间加载和写回值的关键作用是 SerializedObject.Update
and SerializedObject.ApplyModifiedProperties
- 我喜欢称它为 "shadow clone".
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.UI;
class Foo : MonoBehaviour
{
public Button lastButton;
public Image[] images;
}
#if UNITY_EDITOR
[CustomEditor(typeof(Foo))]
class FooEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
// fetch current values from the real target into the serialized "clone"
serializedObject.Update();
Foo foo = (Foo)target;
if (GUILayout.Button("Bind last Button and all Images"))
{
// get last button field as serialized property
var lastButtonField = serializedObject.FindProperty("lastButton");
// get last Button reference
// pass true to also get eventually inactive children
var buttons = foo.GetComponentsInChildren<Button>(true);
var lastButton = buttons[buttons.Length - 1];
// asign lastButton to the lastButtonField
lastButtonField.objectReferenceValue = lastButton;
// slightly more complex but similar
// get the field as serialized property
var imagesField = serializedObject.FindProperty("images");
// get the images references
// again pass true to also get eventually inactive ones
var images = foo.GetComponentsInChildren<Image>(true);
// now first set the according list size
imagesField.arraySize = images.Length;
// assign all references
for (var i = 0; i < imagesField.arraySize; i++)
{
// serialized property of the element in the field list
var entry = imagesField.GetArrayElementAtIndex(i);
// simply assign the reference like before with the button
entry.objectReferenceValue = images[i];
}
}
// write back changes to the real target
// automatically handles marking as dirty and undo/redo
serializedObject.ApplyModifiedProperties();
}
}
#endif
我希望我没有完全偏离,这就是你 "binding" 所说的。
请注意,如果您想进一步更改您选择的不是目标本身的引用的值,您可以轻松地使用 new SerializedObject(objectReference)
以获得序列化对象,例如
var serializedLastButton = new SerializedObject (lastButton);
或者也
var serializedLastButton = new SerializedObject(lastButtonField.objectReferenceValue);
现在,当再次使用 FindProperty(propertyName)
更改其中的任何字段时,确保再次使用
serializedLastButton.Update();
// make changes
serializedLastButton.ApplyModifiedProperties();