检查器插件
检查器面板允许您通过插件,创建自定义控件来编辑属性。
这在处理自定义数据类型和resources时非常有用,尽管您也可以使用此功能来更改内置类型的检查器控件。 您可以为特定属性、整个对象,甚至与特定数据类型关联的独立控件设计自定义控件。
更多信息,请参见 docs.godotengine.org。
Godot文档中的示例,该示例会将整数输入替换为一个按键,该按键生成一个随机值。
The example
之前(整数输入):
之后(按键):
在终端中与 Cargo.toml
相同目录下添加此依赖:
cargo add rand
创建 addon.rs
文件并在 lib.rs
中导入它:
#![allow(unused)] fn main() { // file: lib.rs mod addon; }
在文件开头添加以下导入:
#![allow(unused)] fn main() { use godot::classes::{ Button, EditorInspectorPlugin, EditorPlugin, EditorProperty, IEditorInspectorPlugin, IEditorPlugin, IEditorProperty, }; use godot::global; use godot::prelude::*; use rand::Rng; }
Since Rust is a statically typed language, we will proceed in reverse order unlike in Godot documentation, to avoid encountering errors unnecessarily.
添加属性编辑器
To begin with, let's define the editor for properties:
#![allow(unused)] fn main() { #[derive(GodotClass)] #[class(tool, init, base=EditorProperty)] struct RandomIntEditor { base: Base<EditorProperty>, button: Option<Gd<Button>>, } }
After that, we need to add an implementation for the trait IEditorProperty
:
#![allow(unused)] fn main() { #[godot_api] impl IEditorProperty for RandomIntEditor { fn enter_tree(&mut self) { // Create button element. let mut button = Button::new_alloc(); // Add handler for this button, handle_press will be define in another impl. button.connect("pressed", self.base().callable("handle_press")); button.set_text("Randomize"); // Save pointer to the button into struct. self.button = Some(button.clone()); self.base_mut().add_child(button.upcast()); } fn exit_tree(&mut self) { // Remove element from inspector when this plugin unmount: if let Some(button) = self.button.take() { self.base_mut().remove_child(button.upcast()); } else { // Log error if button disappeared before godot_error!("Button wasn't found in exit_tree"); } } } }
Let's add a handler for the button:
#![allow(unused)] fn main() { #[godot_api] impl RandomIntEditor { #[func] fn handle_press(&mut self) { // Update value by button click: // - Take property name, randomize number. // - Send property name and random number to Godot engine to update value. // - Update button text. let property_name = self.base().get_edited_property(); let num = rand::thread_rng().gen_range(0..100); godot_print!("Randomize! {num} for {property_name}"); self.base_mut() .emit_changed(property_name, num.to_variant()); if let Some(mut button) = self.button.clone() { let text = format!("Randomize: {num}"); button.set_text(&text); } else { // Print error of something went wrong godot_error!("Button wasn't found in handle_press"); } } } }
添加检查器插件
Now we need to connect this editor to fields with an integer type.
To do this, we need to create an EditorInspectorPlugin
.
#![allow(unused)] fn main() { #[derive(GodotClass)] #[class(tool, init, base=EditorInspectorPlugin)] struct RandomInspectorPlugin { base: Base<EditorInspectorPlugin>, } }
To add a property editor (which we implemented earlier), you need to implement the IEditorInspectorPlugin
trait:
#![allow(unused)] fn main() { #[godot_api] impl IEditorInspectorPlugin for RandomInspectorPlugin { fn parse_property( &mut self, _object: Gd<Object>, // object that is being inspected value_type: VariantType, name: GString, _hint_type: global::PropertyHint, _hit_string: GString, _flags: global::PropertyUsageFlags, _wide: bool, ) -> bool { if value_type == VariantType::INT { self.base_mut() .add_property_editor(name, RandomIntEditor::new_alloc().upcast()); return true; } false } // This method says Godot that this plugin handle the object if it returns true fn can_handle(&self, object: Gd<Object>) -> bool { // This plugin handle only Node2D and object that extends it object.is_class("Node2D") } } }
If parse_property
returns true
, the editor plugin will be created and replace the current
representation; if not, it's necessary to return false
.
This allows you to control where and how processing is done by this plugin.
添加编辑器插件
Only one thing left to do: define the editor plugin that will kick off all this magic!
This can be a generic EditorPlugin
or a more specific InspectorEditorPlugin
, depending
on what you want to achieve.
#![allow(unused)] fn main() { #[derive(GodotClass)] #[class(tool, init, editor_plugin, base=EditorPlugin)] struct RustEditorPlugin { base: Base<EditorPlugin>, random_inspector: Gd<RandomInspectorPlugin>, } }
#![allow(unused)] fn main() { #[godot_api] impl IEditorPlugin for RustEditorPlugin { fn enter_tree(&mut self) { // Create our inspector plugin and save it. let plugin = RandomInspectorPlugin::new_gd(); self.random_inspector = plugin.clone(); self.base_mut().add_inspector_plugin(plugin.upcast()); } fn exit_tree(&mut self) { // Remove inspector plugin when editor plugin leaves scene tree. let plugin = self.random_inspector.clone(); self.base_mut().remove_inspector_plugin(plugin.upcast()); } } }
Sometimes after compilation, you may encounter errors or panic. Most likely, all you need to do is simply restart the Godot Editor.
Example error:
Initialize godot-rust (API v4.2.stable.official, runtime v4.2.2.stable.official)
ERROR: Cannot get class 'RandomInspectorPlugin'.
at: (core/object/class_db.cpp:392)
ERROR: Cannot get class 'RandomInspectorPlugin'.
at: (core/object/class_db.cpp:392)