1 unstable release
0.1.2 | Jan 18, 2024 |
---|
#7 in #gd-native
Used in 2 crates
23KB
265 lines
gdrust_export_node_path_macro
Reduces boilerplate code when acquiring references through NodePath
.
Usage consists of (full example on a few paragraphs bellow):
- Replacing
#[derive(NativeClass, Default)] #[inherit(Node)]
with#[extends(Node)]
- Removing the
NodePath
fields marked with#[property]
- Placing
#[export_path]
behind the fields you wish to acquire throughNodePath
- Removing your implementation of
fn new(&mut self, _owner: &Node)
- Manually call
self.grab_nodes_by_path(_owner);
on your_ready()
declaration
The #[extends]
attribute macro:
- Replaces itself with
#[derive(NativeClass)] #[inherit(Node)]
- Then, for each field marked with
#[export_path]
:- Declares another field of similar name but with a
path_
prefix andNodePath
as type, as well as a regular#[property]
attribute. (Vec<Ref<Node>>
uses aVec<NodePath>
type instead) - Example input/output:
#[export_path] node: Option<Ref<Node>>
/#[property] path_node: NodePath, node: Option<Ref<Node>>,
- Declares another field of similar name but with a
- Declares a impl block with two functions:
fn grab_nodes_by_path(&mut self, _owner: &Node) { // searches for all the nodes/instances from the NodePath fields generated, assigning each to their original field in self }
fn new(_owner: &Node) -> Self { Self::default() }
Supports exporting:
Option<Ref<T>>
where T is a Godot's built-in type that inheritsNode
(such as:Node2D
,Control
,ProgressBar
,KinematicBody
, ...)Vec<Ref<T>>
where T is a Godot's built-in type that inheritsNode
Option<Instance<T>>
where T is a custom native script defined by you, as long as the script inherits a Godot's built-in type that inheritsNode
Vec<Instance<T>>
where T is a custom native script defined by you, as long as the script inherits a Godot's built-in type that inheritsNode
PS 1: Note that Vec<Ref<T>>
/Vec<Instance<T>>
uses Ref<T>
/Instance<T>
directly instead of Option<Ref<T>>
/Option<Instance<T>>
PS 2: Not related to this crate, but to gdnative-rust
itself: In the editor, Vec<NodePath>
will be shown as an array of Variant
, you have to click on each element of the array, chose NodePath
, then you'll be able to drag nodes in.
The base of the implementation was taken from gdrust.
Usage
Replace
#[derive(NativeClass)]
#[inherit(Node)]
struct MyGodotScript {
#[property] path_exported_node: NodePath,
exported_node: Option<Ref<Node>>,
#[property] path_exported_instance: NodePath,
exported_instance: Option<Instance<NativeScriptTest>>,
#[property] paths_exported_nodes: Vec<NodePath>,
vec_nodes: Vec<Ref<Node>>,
#[property] paths_exported_instances: Vec<NodePath>,
vec_instances: Vec<Instance<NativeScriptTest>>,
}
impl MyGodotScript {
fn new(_owner: &Node) -> Self {
return Self {
path_exported_node: NodePath::default(),
exported_node: None,
path_exported_instance: NodePath::default(),
exported_instance: None,
paths_exported_nodes: Vec::new(),
vec_nodes: Vec::new(),
paths_exported_instances: Vec::new(),
vec_instances: Vec::new(),
};
}
}
#[methods]
impl MyGodotScript {
#[method]
fn _ready(&mut self, #[base] _owner: &Node) {
self.exported_node = Some(unsafe {_owner.get_node_as::<Node>(self.path_exported_node.new_ref()).unwrap().assume_shared()});
self.exported_instance = Some(unsafe {_owner.get_node_as_instance::<NativeScriptTest>(self.path_exported_instance.new_ref()).unwrap().claim()});
for path in self.paths_exported_nodes.iter() {
self.vec_nodes.push(unsafe { _owner.get_node_as::<Node>(path.new_ref()).unwrap().assume_shared()});
}
for path in self.paths_exported_instances.iter() {
self.vec_instances.push(unsafe { _owner.get_node_as_instance::<NativeScriptTest>(path.new_ref()).unwrap().claim()});
}
}
}
#[derive(NativeClass)]
#[inherit(Node)]
pub struct NativeScriptTest { }
impl NativeScriptTest {
fn new(_owner: &Node) -> Self {
return Self {};
}
}
With
#[extends(Node)] // you can replace Node with any other Godot built-in node type
struct MyGodotScript {
#[export_path] exported_node: Option<Ref<Node>>, // you can replace Node with any other Godot built-in node type
#[export_path] exported_instance: Option<Instance<NativeScriptTest>>, // replace NativeScriptTest with your own type
#[export_path] vec_nodes: Vec<Ref<Node>>, // you can replace Node with any other Godot built-in node type
#[export_path] vec_instances: Vec<Instance<NativeScriptTest>>, // replace NativeScriptTest with your own type
}
#[methods]
impl MyGodotScript {
#[method]
fn _ready(&mut self, #[base] _owner: &Node) { // replace Node with the extended type
self.grab_nodes_by_path(_owner); // you must call this manually, it replaces your old _ready() call
}
}
#[extends(Node)]
pub struct NativeScriptTest { }
Which expands to (manually formatted for readability)
#[derive(gdnative::prelude::NativeClass, Default)]
#[inherit(Node)]
struct MyGodotScript {
#[property] path_vec_instances: Vec<gdnative::prelude::NodePath>,
#[property] path_vec_nodes : Vec<gdnative::prelude::NodePath>,
#[property] path_exported_instance: gdnative::prelude::NodePath,
#[property] path_exported_node : gdnative::prelude::NodePath,
exported_node: Option<Ref<Node>>,
exported_instance: Option<Instance<NativeScriptTest>>,
vec_nodes: Vec<Ref<Node>>,
vec_instances: Vec<Instance<NativeScriptTest>>,
}
impl MyGodotScript {
fn new(_owner: &Node) -> Self { Self::default() }
fn grab_nodes_by_path(&mut self, owner: &Node) {
self.exported_node = Some(unsafe { owner.get_node_as::<Node>(self.path_exported_node.new_ref()).unwrap().assume_shared() });
self.exported_instance = Some(unsafe { owner.get_node_as_instance::<NativeScriptTest>(self.path_exported_instance.new_ref()).unwrap().claim() });
for path in self.path_vec_nodes.iter() {
self.vec_nodes.push(unsafe { owner.get_node_as::<Node>(path.new_ref()).unwrap().assume_shared() });
}
for path in self.path_vec_instances.iter() {
self.vec_instances.push(unsafe { owner.get_node_as_instance::<NativeScriptTest>(path.new_ref()).unwrap().claim() });
}
}
}
Misc / Limitations
- You may still freely use the other
gdnative
attributes like#[register_with]
#[no_constructor]
#[user_data]
#[property]
. Just make sure to always place them bellow#[extends]
, except#[property]
which still goes behind fields. grab_nodes_by_path(&mut self, owner: &Node)
will panic if it fails to validate any of the exported paths.- You cannot define your own
Self::new()
. _owner
ingrab_nodes_by_path(&mut self, owner: &Node)
uses hardcoded&
, so you cannot declareowner
in_ready(&mut self, #[base] _owner: &Node)
asowner: TRef<Node>
.
Dependencies
~3.5–4.5MB
~91K SLoC