检查器插件

检查器面板支持以插件的形式来创建自定义小工具编辑属性。尽管可以用它来修改内置类型的检查器小工具,但它在需要处理自定义数据类型和资源时尤其有用。你不但可以为特定的属性或者整个对象设计自定义控件,还可以为特定数据类型设计单独的控件。

这份指南会介绍如何使用 EditorInspectorPluginEditorProperty 类来为整数类型创建自定义的界面,将默认的行为替换为一个按了以后就会生成 0 到 99 之间随机数的按钮。

../../../_images/inspector_plugin_example.png

左图为默认行为,右图为最终结果。

创建你的插件

从创建新的空插件开始。

参见

如何创建新插件请参阅 制作插件

让我们假设你的插件文件夹叫做 my_inspector_plugin。那么此时你新建的 addons/my_inspector_plugin 文件夹中就有两个文件:plugin.cfgplugin.gd

和之前一样,plugin.gd 是一个扩展了 EditorPlugin 的脚本,你需要在 _enter_tree_exit_tree 方法中加入新的代码。要创建自己的检查器插件,你必须加载对应的脚本,然后创建并调用 add_inspector_plugin() 来添加实例。禁用插件时,你应该调用 remove_inspector_plugin() 将该实例移除。

备注

Here, you are loading a script and not a packed scene. Therefore you should use new() instead of instantiate().

GDScriptC#

  1. # plugin.gd
  2. @tool
  3. extends EditorPlugin
  4. var plugin
  5. func _enter_tree():
  6. plugin = preload("res://addons/my_inspector_plugin/my_inspector_plugin.gd").new()
  7. add_inspector_plugin(plugin)
  8. func _exit_tree():
  9. remove_inspector_plugin(plugin)
  1. // Plugin.cs
  2. #if TOOLS
  3. using Godot;
  4. [Tool]
  5. public partial class Plugin : EditorPlugin
  6. {
  7. private MyInspectorPlugin _plugin;
  8. public override void _EnterTree()
  9. {
  10. _plugin = new MyInspectorPlugin();
  11. AddInspectorPlugin(_plugin);
  12. }
  13. public override void _ExitTree()
  14. {
  15. RemoveInspectorPlugin(_plugin);
  16. }
  17. }
  18. #endif

与检查器交互

要和检查器面板交互,你的 my_inspector_plugin.gd 脚本必须继承自 EditorInspectorPlugin 类。这个类提供了不少虚方法,可以用来控制检查器对属性的处理。

脚本必须实现 can_handle() 方法才能生效。这个函数会针对每个被编辑的 Object 对象调用,插件想要处理该对象或其属性的话,就必须返回 true

备注

要处理附加在该对象上的 Resource 也同样如此。

You can implement four other methods to add controls to the inspector at specific positions. The _parse_begin() and _parse_end() methods are called only once at the beginning and the end of parsing for each object, respectively. They can add controls at the top or bottom of the inspector layout by calling add_custom_control().

As the editor parses the object, it calls the _parse_category() and _parse_property() methods. There, in addition to add_custom_control(), you can call both add_property_editor() and add_property_editor_for_multiple_properties(). Use these last two methods to specifically add EditorProperty-based controls.

GDScriptC#

  1. # my_inspector_plugin.gd
  2. extends EditorInspectorPlugin
  3. var RandomIntEditor = preload("res://addons/my_inspector_plugin/random_int_editor.gd")
  4. func _can_handle(object):
  5. # We support all objects in this example.
  6. return true
  7. func _parse_property(object, type, name, hint_type, hint_string, usage_flags, wide):
  8. # We handle properties of type integer.
  9. if type == TYPE_INT:
  10. # Create an instance of the custom property editor and register
  11. # it to a specific property path.
  12. add_property_editor(name, RandomIntEditor.new())
  13. # Inform the editor to remove the default property editor for
  14. # this property type.
  15. return true
  16. else:
  17. return false
  1. // MyInspectorPlugin.cs
  2. #if TOOLS
  3. using Godot;
  4. public partial class MyInspectorPlugin : EditorInspectorPlugin
  5. {
  6. public override bool _CanHandle(GodotObject @object)
  7. {
  8. // We support all objects in this example.
  9. return true;
  10. }
  11. public override bool _ParseProperty(GodotObject @object, Variant.Type type,
  12. string name, PropertyHint hintType, string hintString,
  13. PropertyUsageFlags usageFlags, bool wide)
  14. {
  15. // We handle properties of type integer.
  16. if (type == Variant.Type.Int)
  17. {
  18. // Create an instance of the custom property editor and register
  19. // it to a specific property path.
  20. AddPropertyEditor(name, new RandomIntEditor());
  21. // Inform the editor to remove the default property editor for
  22. // this property type.
  23. return true;
  24. }
  25. return false;
  26. }
  27. }
  28. #endif

添加编辑属性的界面

EditorProperty 是一种特殊的 Control,可以与检查器面板所编辑的对象进行交互。它本身不显示任何内容,但可以放入其他控件节点,甚至是复杂的场景。

扩展 EditorProperty 的脚本有三个必不可少的部分:

  1. 必须定义 _init() 方法,设置控件节点的结构。

  2. 应该实现 _update_property(),处理外部对数据的更改。

  3. 必须在某处使用 emit_changed 触发信号,告知检查器本控件对属性进行了修改。

显示自定义小工具的方法有两种。可以只用默认的 add_child() 方法可以把它显示到属性名称的右边,在 add_child() 之后再调用 set_bottom_editor() 就可以把它显示到名称的下边。

GDScriptC#

  1. # random_int_editor.gd
  2. extends EditorProperty
  3. # The main control for editing the property.
  4. var property_control = Button.new()
  5. # An internal value of the property.
  6. var current_value = 0
  7. # A guard against internal changes when the property is updated.
  8. var updating = false
  9. func _init():
  10. # Add the control as a direct child of EditorProperty node.
  11. add_child(property_control)
  12. # Make sure the control is able to retain the focus.
  13. add_focusable(property_control)
  14. # Setup the initial state and connect to the signal to track changes.
  15. refresh_control_text()
  16. property_control.pressed.connect(_on_button_pressed)
  17. func _on_button_pressed():
  18. # Ignore the signal if the property is currently being updated.
  19. if (updating):
  20. return
  21. # Generate a new random integer between 0 and 99.
  22. current_value = randi() % 100
  23. refresh_control_text()
  24. emit_changed(get_edited_property(), current_value)
  25. func _update_property():
  26. # Read the current value from the property.
  27. var new_value = get_edited_object()[get_edited_property()]
  28. if (new_value == current_value):
  29. return
  30. # Update the control with the new value.
  31. updating = true
  32. current_value = new_value
  33. refresh_control_text()
  34. updating = false
  35. func refresh_control_text():
  36. property_control.text = "Value: " + str(current_value)
  1. // RandomIntEditor.cs
  2. #if TOOLS
  3. using Godot;
  4. public partial class RandomIntEditor : EditorProperty
  5. {
  6. // The main control for editing the property.
  7. private Button _propertyControl = new Button();
  8. // An internal value of the property.
  9. private int _currentValue = 0;
  10. // A guard against internal changes when the property is updated.
  11. private bool _updating = false;
  12. public RandomIntEditor()
  13. {
  14. // Add the control as a direct child of EditorProperty node.
  15. AddChild(_propertyControl);
  16. // Make sure the control is able to retain the focus.
  17. AddFocusable(_propertyControl);
  18. // Setup the initial state and connect to the signal to track changes.
  19. RefreshControlText();
  20. _propertyControl.Pressed += OnButtonPressed;
  21. }
  22. private void OnButtonPressed()
  23. {
  24. // Ignore the signal if the property is currently being updated.
  25. if (_updating)
  26. {
  27. return;
  28. }
  29. // Generate a new random integer between 0 and 99.
  30. _currentValue = (int)GD.Randi() % 100;
  31. RefreshControlText();
  32. EmitChanged(GetEditedProperty(), _currentValue);
  33. }
  34. public override void _UpdateProperty()
  35. {
  36. // Read the current value from the property.
  37. var newValue = (int)GetEditedObject().Get(GetEditedProperty());
  38. if (newValue == _currentValue)
  39. {
  40. return;
  41. }
  42. // Update the control with the new value.
  43. _updating = true;
  44. _currentValue = newValue;
  45. RefreshControlText();
  46. _updating = false;
  47. }
  48. private void RefreshControlText()
  49. {
  50. _propertyControl.Text = $"Value: {_currentValue}";
  51. }
  52. }
  53. #endif

使用上面的示例代码,可以实现用自定义的小工具替代整数默认的 SpinBox 控件,点击 Button 后生成随机值。