Плагины инспектора
Панель инспектора позволяет создавать пользовательские виджеты для редактирования свойств с помощью плагинов. Это может быть полезно при работе с пользовательскими типами данных и ресурсами, хотя вы также можете использовать эту функцию для изменения виджетов инспектора для встроенных типов. Вы можете создавать пользовательские элементы управления для конкретных свойств, целых объектов и даже отдельные элементы управления, связанные с определёнными типами данных.
В этом руководстве объясняется, как использовать классы EditorInspectorPlugin и EditorProperty для создания пользовательского интерфейса для целых чисел, заменяя поведение по умолчанию кнопкой, которая генерирует случайные значения от 0 до 99.
Поведение по умолчанию слева и конечный результат справа.
Настройка вашего плагина
Чтобы начать, создайте новый пустой плагин.
См. также
Инструкции по настройке нового плагина см. в руководстве Создание плагинов.
Предположим, вы назвали папку плагина my_inspector_plugin. В этом случае у вас должна появиться новая папка addons/my_inspector_plugin, содержащая два файла: plugin.cfg и plugin.gd.
Как и прежде, plugin.gd — это скрипт, расширяющий EditorPlugin, и вам необходимо добавить новый код для его методов _enter_tree и _exit_tree. Чтобы настроить плагин-инспектор, необходимо загрузить его скрипт, а затем создать и добавить экземпляр, вызвав add_inspector_plugin(). Если плагин отключён, необходимо удалить добавленный экземпляр, вызвав remove_inspector_plugin().
Примечание
Здесь вы загружаете скрипт, а не упакованную сцену. Поэтому следует использовать new() вместо instantiate().
# plugin.gd
@tool
extends EditorPlugin
var plugin
func _enter_tree():
plugin = preload("res://addons/my_inspector_plugin/my_inspector_plugin.gd").new()
add_inspector_plugin(plugin)
func _exit_tree():
remove_inspector_plugin(plugin)
// Plugin.cs
#if TOOLS
using Godot;
[Tool]
public partial class Plugin : EditorPlugin
{
private MyInspectorPlugin _plugin;
public override void _EnterTree()
{
_plugin = new MyInspectorPlugin();
AddInspectorPlugin(_plugin);
}
public override void _ExitTree()
{
RemoveInspectorPlugin(_plugin);
}
}
#endif
Взаимодействие с инспектором
Для взаимодействия с панелью инспектора ваш скрипт my_inspector_plugin.gd должен расширять класс EditorInspectorPlugin. Этот класс предоставляет несколько виртуальных методов, влияющих на обработку свойств инспектором.
Чтобы скрипт хоть как-то действовал, он должен реализовывать метод _can_handle(). Эта функция вызывается для каждого отредактированного объекта Object и должна возвращать true, если плагин должен обрабатывать объект или его свойства.
Примечание
Это включает в себя любой Resource, прикрепленный к объекту.
Вы можете реализовать четыре других метода для добавления элементов управления в инспектор в определённых позициях. Методы _parse_begin() и _parse_end() вызываются только один раз в начале и в конце разбора каждого объекта соответственно. Они позволяют добавлять элементы управления в верхнюю или нижнюю часть макета инспектора, вызывая add_custom_control().
При разборе объекта редактор вызывает методы _parse_category() и _parse_property(). Помимо add_custom_control(), здесь можно вызывать add_property_editor() и add_property_editor_for_multiple_properties(). Используйте эти два последних метода для добавления элементов управления на основе EditorProperty.
# my_inspector_plugin.gd
extends EditorInspectorPlugin
var RandomIntEditor = preload("res://addons/my_inspector_plugin/random_int_editor.gd")
func _can_handle(object):
# We support all objects in this example.
return true
func _parse_property(object, type, name, hint_type, hint_string, usage_flags, wide):
# We handle properties of type integer.
if type == TYPE_INT:
# Create an instance of the custom property editor and register
# it to a specific property path.
add_property_editor(name, RandomIntEditor.new())
# Inform the editor to remove the default property editor for
# this property type.
return true
else:
return false
// MyInspectorPlugin.cs
#if TOOLS
using Godot;
public partial class MyInspectorPlugin : EditorInspectorPlugin
{
public override bool _CanHandle(GodotObject @object)
{
// We support all objects in this example.
return true;
}
public override bool _ParseProperty(GodotObject @object, Variant.Type type,
string name, PropertyHint hintType, string hintString,
PropertyUsageFlags usageFlags, bool wide)
{
// We handle properties of type integer.
if (type == Variant.Type.Int)
{
// Create an instance of the custom property editor and register
// it to a specific property path.
AddPropertyEditor(name, new RandomIntEditor());
// Inform the editor to remove the default property editor for
// this property type.
return true;
}
return false;
}
}
#endif
Добавление интерфейса для редактирования свойств
Класс EditorProperty — это особый тип Control, который может взаимодействовать с редактируемыми объектами в доке инспектора. Он ничего не отображает, но может содержать любые другие элементы управления, включая сложные сцены.
Скрипт, расширяющий EditorProperty, состоит из трех основных частей:
Для настройки структуры узлов управления необходимо определить метод
_init().Вам следует реализовать
_update_property()для обработки изменений данных извне.В какой-то момент должен быть выдан сигнал, информирующий инспектора о том, что элемент управления изменил свойство с помощью
emit_changed.
Вы можете отобразить свой виджет двумя способами. Используйте только метод add_child() по умолчанию, чтобы отобразить его справа от имени свойства, и используйте add_child(), а затем set_bottom_editor(), чтобы разместить его под именем.
# random_int_editor.gd
extends EditorProperty
# The main control for editing the property.
var property_control = Button.new()
# An internal value of the property.
var current_value = 0
# A guard against internal changes when the property is updated.
var updating = false
func _init():
# Add the control as a direct child of EditorProperty node.
add_child(property_control)
# Make sure the control is able to retain the focus.
add_focusable(property_control)
# Setup the initial state and connect to the signal to track changes.
refresh_control_text()
property_control.pressed.connect(_on_button_pressed)
func _on_button_pressed():
# Ignore the signal if the property is currently being updated.
if (updating):
return
# Generate a new random integer between 0 and 99.
current_value = randi() % 100
refresh_control_text()
emit_changed(get_edited_property(), current_value)
func _update_property():
# Read the current value from the property.
var new_value = get_edited_object()[get_edited_property()]
if (new_value == current_value):
return
# Update the control with the new value.
updating = true
current_value = new_value
refresh_control_text()
updating = false
func refresh_control_text():
property_control.text = "Value: " + str(current_value)
// RandomIntEditor.cs
#if TOOLS
using Godot;
public partial class RandomIntEditor : EditorProperty
{
// The main control for editing the property.
private Button _propertyControl = new Button();
// An internal value of the property.
private int _currentValue = 0;
// A guard against internal changes when the property is updated.
private bool _updating = false;
public RandomIntEditor()
{
// Add the control as a direct child of EditorProperty node.
AddChild(_propertyControl);
// Make sure the control is able to retain the focus.
AddFocusable(_propertyControl);
// Setup the initial state and connect to the signal to track changes.
RefreshControlText();
_propertyControl.Pressed += OnButtonPressed;
}
private void OnButtonPressed()
{
// Ignore the signal if the property is currently being updated.
if (_updating)
{
return;
}
// Generate a new random integer between 0 and 99.
_currentValue = (int)GD.Randi() % 100;
RefreshControlText();
EmitChanged(GetEditedProperty(), _currentValue);
}
public override void _UpdateProperty()
{
// Read the current value from the property.
var newValue = (int)GetEditedObject().Get(GetEditedProperty());
if (newValue == _currentValue)
{
return;
}
// Update the control with the new value.
_updating = true;
_currentValue = newValue;
RefreshControlText();
_updating = false;
}
private void RefreshControlText()
{
_propertyControl.Text = $"Value: {_currentValue}";
}
}
#endif
Используя приведенный выше пример кода, вы сможете создать пользовательский виджет, который заменит элемент управления по умолчанию SpinBox для целых чисел на Button, который генерирует случайные значения.