Derive Macro GodotClass
#[derive(GodotClass)]
{
// Attributes available to this derive:
#[class]
#[base]
#[hint]
#[var]
#[export]
#[export_group]
#[export_subgroup]
#[init]
}
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.
See sidebar on the left for table of contents.
§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(val = ...)]
:
#[derive(GodotClass)]
#[class(init)]
struct MyStruct {
#[init(val = 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(val = (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,
which in turn are backed by the Var
and Export
traits.
§Register properties – #[var]
To create a property, you can use the #[var]
annotation, which supports types implementing Var
.
#[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)]
#[class(init)]
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;
}
}
To create a property without a backing field to store data, you can use PhantomVar
.
This disables autogenerated getters and setters for that field.
§Export properties – #[export]
To export properties to the editor, you can use the #[export]
attribute, which supports types implementing
Export
:
#[derive(GodotClass)]
struct MyStruct {
#[export]
my_field: i64,
}
If you don’t include an additional #[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:
GDScript annotation | Rust attribute |
---|---|
@export | #[export] |
@export_key | #[export(key)] |
@export_key(elem1, ...) | #[export(key = (elem1, ...))] |
@export_flags("elem1", "elem2:val2", ...) @export_enum("elem1", "elem2:val2", ...) | #[export(flags = (elem1, elem2 = val2, ...))] #[export(enum = (elem1, elem2 = val2, ...))] |
As an example of different export attributes:
#[derive(GodotClass)]
struct MyStruct {
// @export
#[export]
float: f64,
// @export_storage
#[export(storage)]
hidden_string: GString,
// @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 syntax such as key = value
can be arbitrary expressions. For example, you can use constants, function calls or
other Rust expressions that are valid in that context.
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,
}
It is possible to group your exported properties inside the Inspector with the #[export_group(name = "...", prefix = "...")]
attribute.
Every exported property after this attribute will be added to the group. Start a new group or use #[export_group(name = "")]
(with an empty name) to break out.
Groups cannot be nested but subgroups can be declared with an #[export_subgroup]
attribute.
GDExtension groups and subgroups follow the same rules as the gdscript ones.
See also in Godot docs: Grouping Exports
const MAX_HEALTH: f64 = 100.0;
#[derive(GodotClass)]
struct MyStruct {
// @export_group("Group 1")
// @export var group_1_field: int
#[export]
#[export_group(name = "Group 1")]
group_1_field: i32,
// @export var group_1_field2: int
#[export]
group_1_field2: i32,
// @export_group("my group", "grouped_")
// @export var grouped_field: int
#[export_group(name = "my group", prefix = "grouped_")]
#[export]
grouped_field: u32,
// @export_subgroup("my subgroup")
// @export var sub_field: int
#[export_subgroup(name = "my subgroup")]
#[export]
sub_field: u32,
// Breaks out of subgroup `"my subgroup"`.
// @export_subgroup("")
// @export var grouped_field2: int
#[export_subgroup(name = "")]
#[export]
grouped_field2: u32,
// @export var ungrouped_field: int
#[export]
ungrouped_field: i64,
}
§Low-level property hints and usage
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. Hint and usage flags are constants in the PropertyHint
and PropertyUsageFlags
enums,
while hint strings are dependent on the hint, property type and context. Using these low-level keys is rarely necessary, as most common
combinations are covered by #[var]
and #[export]
already.
#[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,
}
§Further class customization
§Running code in the editor (tool)
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 behaves similarly to GDScript’s @tool
feature.
Note: As in GDScript, the class must be marked as a tool
to be accessible in the editor (e.g., for use by editor plugins and inspectors).
§Editor plugins
Classes inheriting EditorPlugin
will be automatically instantiated and 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 creating the class which inherits EditorPlugin
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.
§Editor plugins – hot reload interaction
During hot reload, Godot firstly unloads EditorPlugin
s, then changes all alive GDExtension classes instances into their base objects
(classes inheriting Resource
become Resource
, classes inheriting Node
become Node
and so on), then reloads all the classes,
and finally changes said instances back into their proper classes.
EditorPlugin
will be re-added to the editor before the last step (changing instances from base classes to extension classes) is finished,
which might cause issues with loading already cached resources and instantiated nodes.
In such a case, await one frame until extension is properly hot-reloaded (See: godot::task::spawn()
).
§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 display in the editor (e.g. when creating a new node), you can use #[class(internal)]
.
Classes starting with “Editor” are auto-hidden by Godot. They must be marked as internal in godot-rust.
#[derive(GodotClass)]
#[class(base=Node, init, internal)]
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>,
}
§Documentation
register-docs
only.4.3+
only.You can document your functions, classes, members, and signals with the ///
doc comment syntax.
#[derive(GodotClass)]
/// This is an example struct for documentation, inside documentation.
struct DocumentedStruct {
/// This is a class member.
/// You can use markdown formatting such as _italics_.
///
/// @experimental `@experimental` and `@deprecated` attributes are supported.
/// The description for such attribute spans for the whole annotated paragraph.
///
/// This is the rest of a doc description.
#[var]
item: f32,
}
#[godot_api]
impl DocumentedStruct {
/// This provides the item, after adding `0.2`.
#[func]
pub fn produce_item(&self) -> f32 {
self.item + 0.2
}
}