调用函数

一般来说,gdext 库以尽可能符合 Rust 风格的方式映射 Godot 函数。有时,函数签名(signatures)会与 GDScript 略有不同,本文将详细介绍这些差异。

目录

Godot 类

Godot 类位于 godot::classes 模块中。一些常用的类,如 NodeRefCountedNode3D 等,还会在 godot::prelude 中重新导出。

Godot 的大部分功能是通过类内部的函数暴露的。请随时查看 API 文档 以获取更多信息。

Godot 函数

像 Rust 中通常一样,函数分为 方法(带有 &self/mut self接收者)和 关联函数(在 Godot 中称为“静态函数”)。

要访问 Gd<T> 指针上的 Godot API,只需直接在 Gd对象上调用方法即可。这是因为 DerefDerefMut trait,它们通过 Gd 为你提供对象引用。

后续章节 中,我们还将看到如何调用在 Rust 中定义的函数。

#![allow(unused)]
fn main() {
// Call with &self receiver.
let node = Node::new_alloc();
let path = node.get_path();

// Call with &mut self receiver.
let mut node = Node::new_alloc();
let other: Gd<Node> = ...;
node.add_child(other);
}

方法是否需要共享引用(&T)或可变引用(&mut T)取决于该方法在GDExtension API中的声明方式(是否为const)。请注意,这种区分仅为信息性的,不涉及安全性问题,但在实践中,它有助于检测意外的修改。技术上,你总是可以通过Gd::clone()创建另一个指针。

关联函数(在GDScript中称为“静态函数”)是直接在类型本身上调用的。

#![allow(unused)]
fn main() {
Node::print_orphan_nodes();
}

单例(Singletons)

单例类(不要与有时也被称为单例的 自动加载 混淆)提供了一个singleton()函数来访问唯一的实例。方法在该实例上调用:

#![allow(unused)]
fn main() {
let input = Input::singleton();
let jump = input.is_action_pressed("jump");
let mouse_pos = input.get_mouse_position();

// 可变操作需要使用mut:
let mut input = input;
input.set_mouse_mode(MouseMode::CAPTURED);
}

关于是否直接在单例类型上提供方法,而不需要调用singleton(),有讨论。然而,这样做会丢失可变性信息,并且还有一些其他问题。

默认参数

GDScript支持参数的默认值。如果没有传递参数,则使用默认值。作为例子,我们可以使用AcceptDialog.add_button()。GDScript签名如下:

Button add_button(String text, bool right=false, String action="")

因此,你可以在GDScript中以以下方式调用:

var dialog = AcceptDialog.new()
var button0 = dialog.add_button("Yes")
var button1 = dialog.add_button("Yes", true)
var button2 = dialog.add_button("Yes", true, "confirm")

在Rust中,我们仍然有一个基本方法AcceptDialog::add_button(),它没有默认参数。 它可以像平常一样调用:

#![allow(unused)]
fn main() {
let dialog = AcceptDialog::new_alloc();
let button = dialog.add_button("Yes");
}

因为Rust不支持默认参数,我们必须通过不同的方式模拟其他调用。我们决定使用构建者模式。

在gdext中,构建器方法以 _ex后缀 命名。这样的一个方法接收所有必需的参数,就像基本方法一样。它返回一个构建器对象,提供按名称设置可选参数的方法。最终,done()方法结束构建并返回Godot函数调用的结果。

例如,方法AcceptDialog::add_button_ex()。以下这两种调用完全等效:

#![allow(unused)]
fn main() {
let button = dialog.add_button("Yes");
let button = dialog.add_button_ex("Yes").done();
}

你还可以通过构建器对象的方法传递可选参数。只需指定所需的参数。 这里的优点是,你可以使用任意顺序,并且跳过任何参数——不同于GDScript,在GDScript中只能跳过最后的参数。

#![allow(unused)]
fn main() {
// GDScript: dialog.add_button("Yes", true, "")
let button = dialog.add_button_ex("Yes")
    .right(true)
    .done();

// GDScript: dialog.add_button("Yes", false, "confirm")
let button = dialog.add_button_ex("Yes")
    .action("confirm")
    .done();

// GDScript: dialog.add_button("Yes", true, "confirm")
let button = dialog.add_button_ex("Yes")
    .right(true)
    .action("confirm")
    .done();
}

动态调用

有时你可能想调用那些在Rust API中没有暴露的函数。这些可能是你在自定义GDScript代码中编写的函数,或来自其他GDExtensions的方法。

当你没有静态信息时,你可以使用Godot的反射API。Godot提供了Object.call()等方法,在Rust中通过两种方式暴露。

如果你期望调用成功(因为你知道你编写的GDScript代码),可以使用Object::call()。如果调用失败,这个方法会panic并提供详细的消息。

#![allow(unused)]
fn main() {
let node = get_node_as::<Node2D>("path/to/MyScript");

// Declare arguments as a slice of variants.
let args = &["string".to_variant(), 42.to_variant()];

// Call the method dynamically.
let val: Variant = node.call("my_method", args);

// Convert to a known type (may panic; try_to() doensn't).
let vec2 = val.to::<Vector2>();
}

如果你想处理调用失败的情况,可以使用 Object::try_call()。这个方法返回一个 Result ,其中包含结果或CallError错误。

#![allow(unused)]
fn main() {
let result: Result<Variant, CallError> = node.try_call("my_method", args);

match result {
    Ok(val) => {
        let vec2 = val.to::<Vector2>();
        // ...
    }
    Err(err) => {
        godot_print!("Error calling method: {}", err);
    }
}
}