From 813769406d6fe5309e4e3e213648427945c5066c Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Wed, 10 Jan 2024 00:16:02 +0100 Subject: [PATCH] Tutorials about registration of classes, functions, properties, signals and constants First draft; some parts can be added later. --- ReadMe.md | 2 +- src/SUMMARY.md | 6 + src/intro/hello-world.md | 9 +- src/register/classes.md | 195 +++++++++++++++++++++++++++++++++ src/register/constants.md | 47 ++++++++ src/register/functions.md | 219 +++++++++++++++++++++++++++++++++++++ src/register/index.md | 49 +++++++++ src/register/properties.md | 140 ++++++++++++++++++++++++ src/register/signals.md | 22 ++++ 9 files changed, 680 insertions(+), 9 deletions(-) create mode 100644 src/register/classes.md create mode 100644 src/register/constants.md create mode 100644 src/register/functions.md create mode 100644 src/register/index.md create mode 100644 src/register/properties.md create mode 100644 src/register/signals.md diff --git a/ReadMe.md b/ReadMe.md index 519a6dc..ef9a64e 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -3,7 +3,7 @@ The godot-rust book is a user guide for **gdext**, the Rust bindings to Godot 4. The book is still work-in-progress, and contributions are very welcome. -An online version of the book is available at [godot-rust.github.io/book][book-web]. +An online version of the book is available at [godot-rust.github.io/book][book-web]. For the gdnative book, check out [gdnative-book]. The book is built with [mdBook] and the plugins [mdbook-toc] and [mdbook-admonish]. To install them and build the book locally, you can run: diff --git a/src/SUMMARY.md b/src/SUMMARY.md index a85b04f..ead2112 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -12,6 +12,12 @@ - [Setup](intro/setup.md) - [Hello World](intro/hello-world.md) - [Managing objects in Rust](intro/objects.md) +- [Registering Rust symbols](register/index.md) + - [Classes](register/classes.md) + - [Functions](register/functions.md) + - [Properties](register/properties.md) + - [Signals](register/signals.md) + - [Constants](register/constants.md) - [Toolchain](toolchain/index.md) - [Compatibility and stability](toolchain/compatibility.md) - [Selecting a Godot version](toolchain/godot-version.md) diff --git a/src/intro/hello-world.md b/src/intro/hello-world.md index 8bc7cf6..d450c0b 100644 --- a/src/intro/hello-world.md +++ b/src/intro/hello-world.md @@ -262,13 +262,6 @@ Let's break this down. 2. The `#[derive]` attribute registers `Player` as a class in the Godot engine. See [API docs][api-derive-godotclass] for details about `#[derive(GodotClass)]`. - ```admonish info title="Auto-registration" - `#[derive(GodotClass)]` _automatically_ registers the class -- you don't need an explicit - `add_class()` registration call, or a `.gdns` file as it was the case with GDNative. - - Before Godot 4.2, you will need to restart the Godot editor for it to take effect. - ``` - 3. The optional `#[class]` attribute configures how the class is registered. In this case, we specify that `Player` inherits Godot's `Sprite2D` class. If you don't specify the `base` key, the base class will implicitly be `RefCounted`, just as if you omitted the `extends` keyword in GDScript. @@ -441,7 +434,7 @@ API attributes typically follow the GDScript keyword names: `class`, `func`, `si That's it for the _Hello World_ tutorial! The following chapters will go into more detail about the various features that gdext provides. -[api-derive-godotclass]: https://godot-rust.github.io/docs/gdext/master/godot/prelude/derive.GodotClass.html +[api-derive-godotclass]: https://godot-rust.github.io/docs/gdext/master/godot/register/derive.GodotClass.html [api-engine]: https://godot-rust.github.io/docs/gdext/master/godot/engine/index.html [api-extensionlibrary]: https://godot-rust.github.io/docs/gdext/master/godot/prelude/trait.ExtensionLibrary.html [api-godot]: https://godot-rust.github.io/docs/gdext/master/godot/index.html diff --git a/src/register/classes.md b/src/register/classes.md new file mode 100644 index 0000000..df970f7 --- /dev/null +++ b/src/register/classes.md @@ -0,0 +1,195 @@ + + +# Registering classes + +Classes are the backbone of data modeling in Godot. If you want to build complex user-defined types in a type-safe way, you won't get around +classes. Arrays, dictionaries and simple types only get you so far, and overusing them defeats the purpose of using a statically typed language. + +Rust makes class registration straightforward. As mentioned before, Rust syntax is used as a baseline, with gdext-specific additions. + + +See also [GDScript reference for classes][godot-gdscript-classes]. + + +## Table of contents + + + + +## Defining a Rust struct + +In Rust, Godot classes are represented by structs. Structs are defined as usual and can contain any number of fields. To register them with +Godot, you need to derive the `GodotClass` trait. + +```admonish info title="GodotClass trait" +The `GodotClass` trait marks all classes known in Godot. It is already implemented for engine classes, for example `Node` or `Resource`. +If you want to register your own classes, you need to implement `GodotClass` as well. + +`#[derive(GodotClass)]` streamlines this process and takes care of all the boilerplate. +See [API docs][api-derive-godotclass] for detailed information. +``` + +Let's define a simple class named `Monster`: + +```rs +#[derive(GodotClass)] +struct Monster { + name: String, + hitpoints: i32, +} +``` + +That's it. Immediately after compiling, this class becomes available in Godot through hot reloading (before Godot 4.2, after restart). +It won't be very useful yet, but the above definition is enough to register `Monster` in the engine. + +```admonish info title="Auto-registration" +`#[derive(GodotClass)]` _automatically_ registers the class -- you don't need an explicit `add_class()` registration call +or a central list mentioning all classes. + +The proc-macro internally registers the class in such a list at startup time. +``` + + +## Selecting a base class + +By default, the base class of a Rust class is `RefCounted`. This is consistent with GDScript when you omit the `extends` keyword. + +`RefCounted` is quite useful for data bundles. As implied by the name, it allows sharing instances tracked by a reference counter; +as such, you don't need to worry about memory management. `Resource` is a subclass of `RefCounted` and is useful for data that needs to be +serialized to the filesystem. + +However, if you want your class to be part of the scene tree, you need to use `Node` (or one of its derived classes) as a base class. + +Here, we use a more concrete node type, `Node3D`. This is done by specifying `#[class(base=Node3D)]` on the struct definition: + +```rs +#[derive(GodotClass)] +#[class(base=Node3D)] +struct Monster { + name: String, + hitpoints: i32, +} +``` + + +## The base field + +Since Rust does not have inheritance, we need to use composition to achieve the same effect. gdext provides a `Base` type, which lets us +store the instance of the Godot superclass (base class) as a field in our `Monster` class. + +```rs +#[derive(GodotClass)] +#[class(base=Node3D)] +struct Monster { + name: String, + hitpoints: i32, + + #[base] + base: Base, +} +``` + +The `#[base]` attribute is currently necessary (as of January 2024), but will likely disappear in the future. + +The important part is the `Base` type. `T` must match the base class you specified in the `#[class(base=...)]` attribute. +You can also use the associated type `Self::Base` for `T`. + +When you declare a base field in your struct, you can access the `Node` API through provided methods `self.base()` and `self.base_mut()`, but +more on this later. + + +## Default constructor + +The constructor of any `GodotClass` object is called `init`. It is necessary to instantiate the object in Godot -- which is used by the scene +tree or when you write `Monster.new()` in GDScript. + +There are two options to define the constructor: let gdext generate it or define it manually. + + +### Library-generated + +You can use `#[class(init)]` to generate a constructor for you. This is limited to simple cases, and it calls `Default::default()` for each field. + +```rs +#[derive(GodotClass)] +#[class(init, base=Node3D)] +struct Monster { + name: String, + hitpoints: i32, + + #[base] + base: Base, +} +``` + +The above definition enables `Monster.new()` in GDScript, but any instance created like this would have an empty string as `name` and +`0` as `hitpoints`. + + +### Manually defined + +```admonish info title="Interface traits" +Each engine class comes with an associated trait, which has the same name but is prefixed with the letter `I`, for "Interface". +The trait has no required functions, but you can override any functions to customize the behavior towards Godot. + +Any `impl` block for the trait must be annotated with the `#[godot_api]` attribute macro. +``` + +```admonish info title="godot_api macro" +The attribute proc-macro `#[godot_api]` is applied to `impl` blocks and marks their items for registration. +It takes no arguments. + +See [API docs][api-godot-api] for detailed information. +``` + +In our case, the `Node3D` comes with the `INode3D` trait. + +We can provide a manually-defined constructor by overriding the trait's associated function `init`: + +```rs +#[derive(GodotClass)] +#[class(base=Node3D)] // No init here, since we define it ourselves. +struct Monster { + name: String, + hitpoints: i32, + + #[base] + base: Base, +} + +#[godot_api] +impl INode3D for Monster { + fn init(base: Base) -> Self { + Self { + name: "Nomster".to_string(), + hitpoints: 100, + base, + } + } +} +``` + +As you can see, the `init` function takes a `Base` as its one and only parameter. This is the base class instance, which is typically +just forwarded to its corresponding field in the struct, here `base`. + +The `init` method always returns `Self`. You may notice that this is currently the only way to construct a `Monster` instance. As soon as your +struct contains a base field, you can no longer provide your own constructor, as you can't provide a value for that field. This is by design and +ensures that _if_ you need access to the base, that base comes from Godot directly. + +However, fear not: you can still provide all sorts of constructors, they just need to go through dedicated functions that internally call `init`. +More on this topic in the next chapter. + + +## Conclusion + +You learned how to define a Rust class and register it with Godot. You now know how to select a base class and how to define a constructor. +The next chapter will allow you to implement logic by providing functions. + +[api-derive-godotclass]: https://godot-rust.github.io/docs/gdext/master/godot/register/derive.GodotClass.html +[api-godot-api]: https://godot-rust.github.io/docs/gdext/master/godot/register/attr.godot_api.html +[godot-gdscript-classes]: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#classes diff --git a/src/register/constants.md b/src/register/constants.md new file mode 100644 index 0000000..32fd26b --- /dev/null +++ b/src/register/constants.md @@ -0,0 +1,47 @@ + + +# Registering constants + +Constants can be used to share fixed values from Rust code to the Godot engine. + +See also [GDScript reference for constants][godot-gdscript-constants]. + + +## Constant declaration + +Constants are declared as `const` items in Rust, inside the inherent `impl` block of a class. + +The attribute `#[constant]` makes it available to Godot. + +```rs +#[godot_api] +impl Monster { + #[constant] + const DEFAULT_HP: i32 = 100; + + #[func] + fn from_name_hp(name: GString, hitpoints: i32) -> Gd { ... } +} +``` + +Usage in GDScript would look as follows: + +```php +var nom = Monster.from_name_hp("Nomster", Monster.DEFAULT_HP) +var orc = Monster.from_name_hp("Orc", 200) +``` + +(This particular example might be better suited for default parameters once they are implemented, but it illustrates the point.) + + +## Statics + +`static` fields can currently not be registered as constants. + + +[godot-gdscript-constants]: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#constants diff --git a/src/register/functions.md b/src/register/functions.md new file mode 100644 index 0000000..a617183 --- /dev/null +++ b/src/register/functions.md @@ -0,0 +1,219 @@ + + +# Registering functions + +Functions are essential in any programming language to execute logic. The gdext library allows you to register functions, so that they can +be called from the Godot engine and GDScript. + +Registration of functions happens always inside `impl` blocks that are annotated with `#[godot_api]`. + +See also [GDScript reference for functions][godot-gdscript-functions]. + + +## Table of contents + + + + +## Godot special functions + +The functions provided by the interface trait (beginning with `I`) are called _Godot special functions_. + +You already saw a function being registered: the `init` constructor, which takes a base object and returns an instance of `Self`. +Godot additionally provides several functions for you to override, to hook into the lifecycle of your object. + +Here is a small selection of lifecycle methods. For a complete list, see [`INode3D` docs][api-inode3d]. + +```rs +#[godot_api] +impl INode3D for Monster { + // Instantiate the object. + fn init(base: Base) -> Self { ... } + + // Called when the node is ready in the scene tree. + fn ready(&mut self) { ... } + + // Called every frame. + fn process(&mut self, delta: f64) { ... } + + // Called every physics frame. + fn physics_process(&mut self, delta: f64) { ... } + + // String representation of the object. + fn to_string(&self) -> GString { ... } + + // Handle user input. + fn input(&mut self, event: Gd) { ... } + + // Handle lifecycle notifications. + fn on_notification(&mut self, what: Node3DNotification) { ... } +} +``` + +As you see, some methods take `&mut self` and some take `&self`, depending on whether they typically mutate the object or not. Some also have +return values, which are passed back into the engine. For example, the `GString` returned from `to_string()` is used if you print an object +in GDScript. + +So let's implement `to_string()`, here again showing the class definition for quick reference. + +```rs +#[derive(GodotClass)] +#[class(base=Node3D)] +struct Monster { + name: String, + hitpoints: i32, + + #[base] + base: Base, +} + +#[godot_api] +impl INode3D for Monster { + ... // init etc. + + fn to_string(&self) -> GString { + let Self { name, hitpoints, .. } = &self; + format!("Monster(name={name}, hp={hitpoints})").into() + } +} +``` + + +## User-defined functions + + +### Methods + +Besides Godot special functions, you can register your own functions. You need to declare them inside an inherent `impl` block, also annotated +with `#[godot_api]`. + +Each function needs a `#[func]` attribute to register it with Godot. You can omit `#[func]` as well, but functions defined like that are only +visible to Rust code. + +Let's add two methods to our `Monster` class: one that deals damage to the monster, and one that returns its name. + +```rs +#[godot_api] +impl Monster { + #[func] + fn damage(&mut self, amount: i32) { + self.hitpoints -= amount; + } + + #[func] + fn get_name(&self) -> GString { + self.name.clone() + } +} +``` + +The above methods are now available in GDScript. You can call them as follows: + +```php +var monster = Monster.new() +# ... +monster.damage(10) +print("A monster called ", monster.get_name()) +``` + +As you see, the Rust types are automatically mapped to their GDScript counterparts. In this case, `i32` becomes `int` and `GString` becomes +`String`. Sometimes there are multiple possible mappings, e.g. Rust `u16` would also be mapped to `int` in GDScript. + + +### Associated functions and constructors + +In addition to **methods** (taking `&self` or `&mut self`), you can also register **associated functions** (without a receiver). In GDScript, +the latter are known as "static functions". + +Associated functions are often useful for user-defined constructors. + +Previously, we talked about the default constructor `init` not being very useful, as it leaves the `Monster` in an incorrect state. +Let's provide a more suitable constructor, which accepts name and hitpoints as parameters. + +```rs +// Default constructor from before. +#[godot_api] +impl INode3D for Monster { + fn init(base: Base) -> Self { ... } +} + +// New custom constructor. +#[godot_api] +impl Monster { + #[func] // Note: the following is incorrect. + fn from_name_hp(name: GString, hitpoints: i32) -> Self { + ... + } +} +``` + +But now, how to fill in the blanks? `Self` requires a base object, how to obtain it? In fact, we cannot return `Self` here. + +```admonish info title="Passing around objects" +When interacting with Godot from Rust, all objects (class instances) need to be transported inside the `Gd` smart pointer -- whether +they appear as parameters or return types. + +The return types of `init` and a few other gdext-provided functions are an exception, because the library requires at this point that you +have a _value_ of the raw object. You never need to return `Self` in your own defined functions (unless it's pure Rust code). + +For details, consult [the chapter about objects][book-objects] or the [`Gd` API docs][api-gd]. +``` + +So we need to return `Gd` instead of `Self`. + +To construct `Gd`, we can use [`Gd::from_init_fn()`][api-gd-from-init-fn], which takes a closure. This closure accepts a `Base` object +and returns an instance of `Self`. In other words, it has the same signature as `init` -- this presents an alternative way of constructing +Godot objects, while allowing to pass in addition context. + +The result of `Gd::from_init_fn()` is a `Gd` object, which can be directly returned by `Monster::from_name_hp()`. + +```rs +#[godot_api] +impl Monster { + #[func] + fn from_name_hp(name: GString, hitpoints: i32) -> Gd { + // Function contains a single statement, the `Gd::from_init_fn()` call. + + Gd::from_init_fn(|base| { + // Accept a base of type Base and directly forward it. + Self { + name: name.into(), // Convert GString -> String. + hitpoints, + base, + } + }) + } +} +``` + +That's it! The just added associated function is now registered in GDScript and effectively works as a constructor: + +```php +var monster = Monster.from_name_hp("Nomster", 100) +``` + + + + + +## Conclusion + +This page gave you an overview of registering functions with Godot: + +- Special methods that hook into the lifecycle of your object. +- User-defined methods that query or modify the state of your instance. +- Associated functions that serve as constructors. + +These are just a few use cases, you are very flexible in how you design your interface between Rust and GDScript. +In the next page, we will look into properties. + +[godot-gdscript-functions]: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#functions +[api-inode3d]: https://godot-rust.github.io/docs/gdext/master/godot/engine/trait.INode3D.html +[api-gd]: https://godot-rust.github.io/docs/gdext/master/godot/obj/struct.Gd.html +[book-objects]: ../intro/objects.md +[api-gd-from-init-fn]: https://godot-rust.github.io/docs/gdext/master/godot/obj/struct.Gd.html#method.from_init_fn diff --git a/src/register/index.md b/src/register/index.md new file mode 100644 index 0000000..b0ec6a2 --- /dev/null +++ b/src/register/index.md @@ -0,0 +1,49 @@ + + +# Registering Rust symbols + +This chapter teaches how you make your own Rust code available to Godot. You do this by _registering_ individual symbols (classes, functions etc.) +in the engine. + +Starting with class registration, the chapter then goes into the details of registering functions, properties, signals and constants. + + + + +## Proc-macro API + +The proc-macro API is currently the only way to register Rust symbols. A variety of procedural macros (derive and attribute macros) are provided +to decorate your Rust items, such as `struct`s or `impl` blocks. Behind the scenes, these macros generate the necessary glue code to register +each item with Godot. + +The library is designed in a way that you can use all your existing knowledge and simply extend it with macro syntax, rather than having to learn +a completely new way of doing things. We try to avoid foreign DSLs (domain-specific languages) and instead build on top of Rust's existing syntax. + +This approach does a respectable job at limiting the amount of boilerplate code you have to write, and thus makes it much easier for you to +focus on the important bits. For example, you will rarely have to repeat yourself more than necessary or register one thing in multiple places +(e.g. declare a method, mention it in another `register` method and then repeat its name yet again as a string literal). + + +## "Exporting" + +The term "exporting" is sometimes erroneously used. Please avoid talking about "exporting classes" or "exporting methods" if you mean +"registering". This can often cause confusion, especially among beginners. + +_Export_ already has two well-defined meanings in the context of Godot: + +1. Exporting a property. This does not _register_ the property with Godot, but renders it visible in the editor. + - GDScript uses the `@export` annotation for this, we use `#[export]`. + - See also [GDScript exported properties][godot-export-properties]. + +2. Exporting projects, meaning bundling them for release. + - The editor provides a UI to build release versions of your game or application, so they can run as a standalone executable. + This process of building the executable is called "exporting". + - See also [Exporting projects][godot-export-projects]. + +[godot-export-properties]: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_exports.html +[godot-export-projects]: https://docs.godotengine.org/en/stable/tutorials/export/index.html diff --git a/src/register/properties.md b/src/register/properties.md new file mode 100644 index 0000000..bc198a4 --- /dev/null +++ b/src/register/properties.md @@ -0,0 +1,140 @@ + + +# Registering properties + +So far, you learned how to register classes and functions. This is already powerful enough to create simple applications with godot-rust, +however you might want to give Godot more direct access to the state of your object. + +This is where properties come into play. In Rust, properties are typically defined as fields of a struct. + +See also [GDScript reference for properties][godot-gdscript-properties]. + + +## Table of contents + + + + +## Registering variables + +Previously, we defined a function `Monster::get_name()`. This works to fetch the name, but requires you to write `obj.get_name()` in GDScript. +Sometimes, you do not need this extra encapsulation and would like to access the field directly. + +The gdext library provides an attribute `#[var]` to annotate fields that should be exposed as variables. This works like the `var` keyword in +GDScript. + +Starting with the earlier struct declaration, we now add the `#[var]` attribute to the `name` field. We also change the type from `String` to +`GString`, since this field is now directly interfacing Godot. + +```rs +#[derive(GodotClass)] +#[class(init, base=Node3D)] +struct Monster { + #[var] + name: GString, + hitpoints: i32, +} +``` + +The effect of this is that `name` is now registered as a _property_ in Godot: + +```php +var monster = Monster.new() + +# Write the property. +monster.name = "Orc" + +# Read the property. +print(monster.name) # prints "Orc" +``` + +In GDScript, properties are syntactic sugar for function calls to getters and setters. You can also do so explicitly: + +```php +var monster = Monster.new() + +# Write the property. +monster.set_name("Orc") + +# Read the property. +print(monster.get_name()) # prints "Orc" +``` + +The `#[var]` attribute also takes parameters to customize whether both getters and setters are provided, and what their names are. You can +also write Rust methods acting as getters and setters, if you have more involved logic. See the [API documentation][api-var-export] for details. + + +```admonish info title="Visibility" +Like `#[func]` functions, `#[var]` fields do not need to be `pub`. This separates visibility towards Godot and towards Rust. + +In practice, you can still access `#[var]` fields from Rust, but via detours (e.g. Godot's reflection APIs). But this is then a deliberate +choice; private fields are primarily preventing _accidental_ mistakes or encapsulation breaches. +``` + + +## Exporting variables + +The `#[var]` attribute exposes a field to GDScript, but does not display it in the Godot editor UI. + +Making a property available to the editor is called _exporting_. Like the GDScript annotation `@export`, gdext provides exports through the +`#[export]` attribute. You might see a pattern with naming here. + +The following code not only makes the `name` field available to GDScript, but it also adds a property UI in the editor. This allows you to +name every `Monster` instance individually, without any code! + +```rs +#[derive(GodotClass)] +#[class(init, base=Node3D)] +struct Monster { + #[export] + name: GString, + hitpoints: i32, +} +``` + +You may have noticed that there is no longer a `#[var]` attribute. This is because `#[export]` always implies `#[var]` -- the name is still +accessible from GDScript like before. + +You can also declare both attributes on the same field. This is in fact necessary as soon as you provide arguments to customize them. + + +## Advanced usage + +Both `#[var]` and `#[export]` attributes accept parameters to further customize how properties are registered in Godot. +Consult the [API documentation][api-var-export] for details. + +```admonish info title="PackedArray mutability" +`Packed*Array` types use copy-on-write semantics, meaning every new instance can be considered an independent copy. When a Rust-side packed +array is registered as a property, GDScript will create a new instance of the array when you mutate it, making changes invisible to Rust code. +There is a [GitHub issue][gh-godot-packedarray] with more details. + +Instead, use `Array` or register designated `#[func]` methods that perform the mutation on Rust side. +``` + + +## Custom types with `#[var]` and `#[export]` + +If you want to register properties of user-defined types, so they become accessible from GDScript code (`#[var]`) or additionally from the +editor (`#[export]`), then you can implement the `Var` and `Export` traits, respectively. + +These traits also come with derive macros, [`#[derive(Var)]`][api-derive-var] and [`#[derive(Export)]`][api-derive-export]. + +```admonish warning title="Performance" +Enabling all sorts of types for `Var` and `Export` seems convenient, but keep in mind that your conversion functions are invoked every time +the engine accesses the property, which may sometimes be behind the scenes. Especially for `#[export]` fields, interactions with the editor UI +or serialization to/from scene files can cause a quite a bit of traffic. + +As a general rule, try to stay close to Godot's own types, e.g. `Array`, `Dictionary` or `Gd`. These are reference-counted or simple pointers. +``` + + +[api-derive-export]: https://godot-rust.github.io/docs/gdext/master/godot/register/derive.Export.html +[api-derive-var]: https://godot-rust.github.io/docs/gdext/master/godot/register/derive.Var.html +[api-var-export]: https://godot-rust.github.io/docs/gdext/master/godot/register/derive.GodotClass.html#properties-and-exports +[godot-gdscript-properties]: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#properties +[gh-godot-packedarray]: https://github.com/godotengine/godot/issues/76150 diff --git a/src/register/signals.md b/src/register/signals.md new file mode 100644 index 0000000..0a9586f --- /dev/null +++ b/src/register/signals.md @@ -0,0 +1,22 @@ + + +# Registering signals + +Signals currently have very limited support in gdext, through the `#[signal]` attribute. Consult its [API documentation][api-signal] for details. + +Signal registration will be completely reworked in the future, with breaking API changes. + +As an alternative, you can use Godot's dynamic API to register signals. The [`Object` class][api-object] has methods `connect()` and +`emit_signal()` that can be used to connect and emit signals, respectively. + +See also [GDScript reference for signals][godot-gdscript-signals]. + + +[api-object]: https://godot-rust.github.io/docs/gdext/master/godot/engine/struct.Object.html +[api-signal]: https://godot-rust.github.io/docs/gdext/master/godot/register/derive.GodotClass.html#signals +[godot-gdscript-signals]: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#signals