Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
屬性面板外掛程式
屬性面板面板支援以外掛程式的形式來建立自訂小工具編輯屬性。儘管可以用它來修改內建型別的屬性面板小工具,但它在需要處理自訂資料型別和資源時尤其有用。你不但可以為特定的屬性或者整個物件設計自訂控制項,還可以為特定資料型別設計單獨的控制項。
這份指南會介紹如何使用 EditorInspectorPlugin 和 EditorProperty 類來為整數型別建立自訂的介面,將預設的行為替換為一個按了以後就會生成 0 到 99 之間亂數的按鈕。

左圖為預設行為,右圖為最終結果。
設定 Python
從建立新的空外掛程式開始。
也參考
如何建立新外掛程式請參閱 製作外掛程式。
讓我們假設你的外掛程式資料夾叫做 my_inspector_plugin
。那麼此時你新建的 addons/my_inspector_plugin
資料夾中就有兩個檔案:plugin.cfg
和 plugin.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()
.
# 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 也同樣如此。
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.
# 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