Exported methods

In order to receive data from Godot, you can export methods. With the #[method] attribute, godot-rust takes care of method registration and serialization. Note that the constructor is not annotated with #[method].

Recent API changes

#[export] has been renamed to #[method] and is now deprecated. It keeps working for the time being though (i.e. gdnative 0.11).

For more information, see gdnative::derive::NativeClass.

The exported method's first parameter is always &self or &mut self (operating on the Rust object), and the second parameter is &T or TRef<T> (operating on the Godot base object, with T being the inherited type).

#![allow(unused)]
fn main() {
#[derive(NativeClass)]
#[inherit(Node)]
pub struct GodotApi {
    enemy_count: i32,
}

#[methods]
impl GodotApi {
    fn new(_base: &Node) -> Self {
        // Print to both shell and Godot editor console
        godot_print!("_init()");
        Self { enemy_count: 0 }
    }
    
    #[method]
    fn create_enemy(
        &mut self,
        typ: String,
        pos: Vector2
    ) {
        godot_print!("create_enemy(): type '{}' at position {:?}", typ, pos);
        self.enemy_count += 1;
    }

    #[method]
    fn create_enemy2(
        &mut self,
        typ: GodotString,
        pos: Variant
    ) {
        godot_print!("create_enemy2(): type '{}' at position {:?}", typ, pos);
        self.enemy_count += 1;
    }

    #[method]
    fn count_enemies(&self) -> i32 {
        self.enemy_count
    }  
}
}

The two creation methods are semantically equivalent, yet they demonstrate how godot-rust implicitly converts the values to the parameter types (unmarshalling). You could use Variant everywhere, however it is more type-safe and expressive to use specific types. The same applies to return types, you could use Variant instead of i32. GodotString is the Godot engine string type, but it can be converted to standard String. To choose between the two, consult the docs.

In GDScript, you can then write this code:

var api = GodotApi.new()

api.create_enemy("Orc", Vector2(10, 20));
api.create_enemy2("Elf", Vector2(50, 70));

print("enemies: ", api.count_enemies())

# don't forget to add it to the scene tree, otherwise memory must be managed manually 
self.add_child(api)

The output is:

_init()
create_enemy(): type 'Orc' at position (10.0, 20.0)
create_enemy2(): type 'Elf' at position Vector2((50, 70))
enemies: 2

Passing classes

The above examples have dealt with simple types such as strings and integers. What if we want to pass entire classes to Rust?

Let's say we want to pass in an enemy from GDScript, instead of creating one locally. It could be represented by the Node2D class and directly configured in the Godot editor. What you then would do is use the Ref wrapper:

#![allow(unused)]
fn main() {
#[derive(NativeClass)]
#[inherit(Node)]
pub struct GodotApi {
    // Store references to all enemy nodes
    enemies: Vec<Ref<Node2D>>,
}

#[methods]
impl GodotApi {
    // new() etc...

    #[method]
    fn add_enemy(
        &mut self,
        enemy: Ref<Node2D> // pass in enemy
    ) {
        self.enemies.push(enemy);
    }
  
    // You can even return the enemies directly with Vec.
    // In GDScript, you will get an array of nodes.
    // An alternative would be VariantArray, able to hold different types.
    #[method]
    fn get_enemies(
        &self,
    ) ->  Vec<Ref<Node2D>> {
        self.enemies.clone()
    }
}
}

Special methods

Godot offers some special methods. Most of them implement notifications, i.e. callbacks from the engine to notify the class about a change.

If you need to override a Godot special method, just declare it as a normal exported method, with the same name and signature as in GDScript. You can also omit the base parameter if you don't need it.

#![allow(unused)]
fn main() {
#[method]
fn _ready(&mut self, #[base] base: &Node) {...}

#[method]
fn _process(&mut self, #[base] base: &Node, delta: f32) {...}

#[method]
fn _physics_process(&mut self, #[base] base: &Node, delta: f32) {...}
}

If you want to change how GDScript's default formatter in functions like str() or print() works, you can overload the to_string GDScript method, which corresponds to the following Rust method:

#![allow(unused)]
fn main() {
#[method]
fn _to_string(&self, #[base] base: &Node) -> String {...}
}

Errors

If you pass arguments from GDScript that are incompatible with the Rust method's signature, the method invocation will fail. In this case, the code inside the method is not executed. An error message is printed on the Godot console, and the value null is returned for the GDScript function call.

If code inside your method panics (e.g. by calling unwrap() on an empty option/result), the same happens: error message and return value null.