Derive Macro godot::prelude::GodotClass
#[derive(GodotClass)]
{
// Attributes available to this derive:
#[class]
#[base]
#[hint]
#[var]
#[export]
#[init]
#[signal]
}
Expand description
Derive macro for GodotClass
on structs.
You should use this macro; manual implementations of the GodotClass
trait are not encouraged.
This is typically used in combination with #[godot_api]
, which can implement custom functions and constants,
as well as override virtual methods.
See also book chapter Registering classes.
Table of contents:
- Construction
- Inheritance
- Properties and exports
- Signals
- Further class customization
- Further field customization
§Construction
If you don’t override init()
manually (within a #[godot_api]
block), gdext can generate a default constructor for you.
This constructor is made available to Godot and lets you call MyStruct.new()
from GDScript. To enable it, annotate your
struct with #[class(init)]
:
#[derive(GodotClass)]
#[class(init)]
struct MyStruct {
// ...
}
The generated init
function will initialize each struct field (except the field of type Base<T>
, if any)
using Default::default()
. To assign some other value, annotate the field with #[init(default = ...)]
:
#[derive(GodotClass)]
#[class(init)]
struct MyStruct {
#[init(default = 42)]
my_field: i64
}
The given value can be any Rust expression that can be evaluated in the scope where you write
the attribute. However, due to limitations in the parser, some complex expressions must be
surrounded by parentheses. This is the case if the expression includes a ,
that is not
inside any pair of (...)
, [...]
or {...}
(even if it is, for example, inside <...>
or
|...|
). A contrived example:
#[init(default = (HashMap::<i64, i64>::new()))]
// ^ parentheses needed due to this comma
my_field: HashMap<i64, i64>,
You can also disable construction from GDScript. This needs to be explicit via #[class(no_init)]
.
Simply omitting the init
/no_init
keys and not overriding your own constructor will cause a compile error.
#[derive(GodotClass)]
#[class(no_init)]
struct MyStruct {
// ...
}
§Inheritance
Unlike C++, Rust doesn’t really have inheritance, but the GDExtension API lets us “inherit” from a Godot-provided engine class.
By default, classes created with this library inherit from RefCounted
, like GDScript.
To specify a different class to inherit from, add #[class(base = Base)]
as an annotation on
your struct
:
#[derive(GodotClass)]
#[class(init, base=Node2D)]
struct MyStruct {
// ...
}
If you need a reference to the base class, you can add a field of type Base<T>
. The derive macro will pick this up and wire
your object accordingly. You can access it through self.base()
and self.base_mut()
methods.
#[derive(GodotClass)]
#[class(init, base=Node2D)]
struct MyStruct {
base: Base<Node2D>,
}
§Properties and exports
See also book chapter Registering properties.
In GDScript, there is a distinction between
properties
(fields with a get
or set
declaration) and
exports
(fields annotated with @export
). In the gdext API, these two concepts are represented with #[var]
and #[export]
attributes respectively.
§Property registration
To create a property, you can use the #[var]
annotation:
#[derive(GodotClass)]
struct MyStruct {
#[var]
my_field: i64,
}
This makes the field accessible in GDScript using my_struct.my_field
syntax. Additionally, it
generates a trivial getter and setter named get_my_field
and set_my_field
, respectively.
These are pub
in Rust, since they’re exposed from GDScript anyway.
If you want to implement your own getter and/or setter, write those as a function on your Rust
type, expose it using #[func]
, and annotate the field with
#[var(get = ..., set = ...)]
:
#[derive(GodotClass)]
struct MyStruct {
#[var(get = get_my_field, set = set_my_field)]
my_field: i64,
}
#[godot_api]
impl MyStruct {
#[func]
pub fn get_my_field(&self) -> i64 {
self.my_field
}
#[func]
pub fn set_my_field(&mut self, value: i64) {
self.my_field = value;
}
}
If you specify only get
, no setter is generated, making the field read-only. If you specify
only set
, no getter is generated, making the field write-only (rarely useful). To add a
generated getter or setter in these cases anyway, use get
or set
without a value:
#[derive(GodotClass)]
struct MyStruct {
// Default getter, custom setter.
#[var(get, set = set_my_field)]
my_field: i64,
}
#[godot_api]
impl MyStruct {
#[func]
pub fn set_my_field(&mut self, value: i64) {
self.my_field = value;
}
}
§Property exports
For exporting properties to the editor, you can use the #[export]
attribute:
#[derive(GodotClass)]
struct MyStruct {
#[export]
my_field: i64,
}
If you don’t also include a #[var]
attribute, then a default one will be generated.
#[export]
also supports all of GDScript’s annotations, in a slightly different format. The format is
translated from an annotation by following these four rules:
@export
becomes#[export]
@export_{name}
becomes#[export(name)]
@export_{name}(elem1, ...)
becomes#[export(name = (elem1, ...))]
@export_{flags/enum}("elem1", "elem2:key2", ...)
becomes#[export(flags/enum = (elem1, elem2 = key2, ...))]
As an example of some different export attributes:
#[derive(GodotClass)]
struct MyStruct {
// @export
#[export]
float: f64,
// @export_range(0.0, 10.0, or_greater)
#[export(range = (0.0, 10.0, or_greater))]
range_f64: f64,
// @export_file
#[export(file)]
file: GString,
// @export_file("*.gd")
#[export(file = "*.gd")]
gdscript_file: GString,
// @export_flags_3d_physics
#[export(flags_3d_physics)]
physics: u32,
// @export_exp_easing
#[export(exp_easing)]
ease: f64,
// @export_enum("One", "Two", "Ten:10", "Twelve:12", "Thirteen")
#[export(enum = (One, Two, Ten = 10, Twelve = 12, Thirteen))]
exported_enum: i64,
// @export_flags("A:1", "B:2", "AB:3")
#[export(flags = (A = 1, B = 2, AB = 3))]
flags: u32,
}
Most values in expressions like key = value
, can be an arbitrary expression that evaluates to the
right value. Meaning you can use constants or variables, as well as any other rust syntax you’d like in
the export attributes.
const MAX_HEALTH: f64 = 100.0;
#[derive(GodotClass)]
struct MyStruct {
#[export(range = (0.0, MAX_HEALTH))]
health: f64,
#[export(flags = (A = 0b0001, B = 0b0010, C = 0b0100, D = 0b1000))]
flags: u32,
}
You can specify custom property hints, hint strings, and usage flags in a #[var]
attribute using the
hint
, hint_string
, and usage_flags
keys in the attribute. These are constants in the PropertyHint
and PropertyUsageFlags
enums, respectively.
#[derive(GodotClass)]
struct MyStruct {
// Treated as an enum with two values: "One" and "Two"
// Displayed in the editor
// Treated as read-only by the editor
#[var(
hint = ENUM,
hint_string = "One,Two",
usage_flags = [EDITOR, READ_ONLY]
)]
my_field: i64,
}
§Signals
The #[signal]
attribute is quite limited at the moment. The functions it decorates (the signals) can accept parameters.
It will be fundamentally reworked.
#[derive(GodotClass)]
struct MyClass {}
#[godot_api]
impl MyClass {
#[signal]
fn some_signal();
#[signal]
fn some_signal_with_parameters(my_parameter: Gd<Node>);
}
§Further class customization
§Running code in the editor
If you annotate a class with #[class(tool)]
, its lifecycle methods (ready()
, process()
etc.) will be invoked in the editor. This
is useful for writing custom editor plugins, as opposed to classes running simply in-game.
See ExtensionLibrary::editor_run_behavior()
for more information and further customization.
This is very similar to GDScript’s @tool
feature.
§Editor plugins
If you annotate a class with #[class(editor_plugin)]
, it will be turned into an editor plugin. The
class must then inherit from EditorPlugin
, and an instance of that class will be automatically added
to the editor when launched.
See Godot’s documentation of editor plugins
for more information about editor plugins. But note that you do not need to create and enable the plugin
through Godot’s Create New Plugin
menu for it to work, simply annotating the class with editor_plugin
automatically enables it when the library is loaded.
This should usually be combined with #[class(tool)]
so that the code you write will actually run in the
editor.
§Class renaming
You may want to have structs with the same name. With Rust, this is allowed using mod
. However, in GDScript
there are no modules, namespaces, or any such disambiguation. Therefore, you need to change the names before they
can get to Godot. You can use the rename
key while defining your GodotClass
for this.
mod animal {
#[derive(GodotClass)]
#[class(init, rename=AnimalToad)]
pub struct Toad {}
}
mod npc {
#[derive(GodotClass)]
#[class(init, rename=NpcToad)]
pub struct Toad {}
}
These classes will appear in the Godot editor and GDScript as “AnimalToad” or “NpcToad”.
§Class hiding
If you want to register a class with Godot, but not have it show up in the editor then you can use #[class(hidden)]
.
#[derive(GodotClass)]
#[class(base=Node, init, hidden)]
pub struct Foo {}
Even though this class is a Node
and it has an init function, it still won’t show up in the editor as a node you can add to a scene
because we have added a hidden
key to the class. This will also prevent it from showing up in documentation.
§Further field customization
§Fine-grained inference hints
The derive macro is relatively smart about recognizing Base<T>
and OnReady<T>
types, and works also if those are qualified.
However, there may be situations where you need to help it out – for example, if you have a type alias for Base<T>
, or use an unrelated
my_module::Base<T>
with a different meaning.
In this case, you can manually override the behavior with the #[hint]
attribute. It takes multiple standalone keys:
base
andno_base
onready
andno_onready
use godot::classes::Node;
// There's no reason to do this, but for the sake of example:
type Super<T> = godot::obj::Base<T>;
type Base<T> = godot::obj::Gd<T>;
#[derive(godot::register::GodotClass)]
#[class(base=Node)]
struct MyStruct {
#[hint(base)]
base: Super<Node>,
#[hint(no_base)]
unbase: Base<Node>,
}