Closed beatgammit closed 5 years ago
The error that you are seeing is related to an unloading error inside of Godot, this should be fixed on Godot's master
branch (maybe even 3.1.1
, I don't know if it was cherry-picked or not).
Oh, awesome! I'll have to play with the new Godot then. I'll post back here if I can confirm that it works on the latest Godot, and I'll probably add a PR for an example loading resources.
Tried this on 3.1.1, and it stills crashes on loader.load. Unable to handle return Option with Option.expect() too.
I believe I have fixed this in my PR https://github.com/GodotNativeTools/godot-rust/pull/168 as I ran into exactly the same issue as you.
Here is my working code, you'll see it is very rough.
Note the script expects to be in a Spatial node and the scene to have a Spatial (or subclass) root node. It depends on some minor changes I made to specify a transform nicely. https://github.com/tom-leys/godot-rust/commit/30923cb42b6967ac35d9b33526b03fe1218cff72 .
I am no means an expert, I only just got this working myself. I hope it helps you.
You'll see in my picture the 3 "spaceships" (yes, they look like conveyors)
#[derive(gdnative::NativeClass)]
#[inherit(gdnative::Spatial)]
pub struct StarWorldLink {
}
#[gdnative::methods]
impl StarWorldLink {
fn _init(_owner: gdnative::Spatial) -> Self {
StarWorldLink {
}
}
#[export]
fn _ready(&self, mut owner: gdnative::Spatial) {
godot_print!("hello, StarWorldLink.");
let ship_scene = ResourceLoader::godot_singleton().load(
GodotString::from_str("res://ship_root.tscn"),
GodotString::from_str("PackedScene"), false);
godot_print!("StarWorldLink Have scene");
if let Some(ship_scene) = ship_scene.and_then(|s| s.cast::<PackedScene>()) {
for x in 0..3 {
let mut instance = ship_scene.instance(0); // 0 - GEN_EDIT_STATE_DISABLED
if let Some(mut instance) = instance.and_then(|inst| unsafe {
inst.cast::<Spatial>()
}) {
godot_print!("StarWorldLink About to make instances");
let transform = Transform::translate(Vector3::new(5.0 + 5.0 * x as f32, 0.0, 0.0));
unsafe {
instance.set_global_transform(transform);
owner.add_child(Some(instance.to_object()), false);
}
godot_print!("StarWorldLink Instance {} Made", x);
}
else {
godot_print!("Should be able to instance scene");
}
}
}
else {
godot_print!("StarWorldLink could not load ship_link scene");
}
}
}
Thx man!!! Just tested it and it worked. Finally being able to load scenes through ResourceLoader.
Can you explain why the need to cast from resource -> PackedScene -> Spatial instead of add_child() directly (this crashed the program for me)?
I'm really glad that this helped you getting it working too, that's awesome!
The process as I understand it is
Check out https://docs.godotengine.org/en/3.1/getting_started/step_by_step/resources.html to help you understand step 1.
Here is version 2 which creates a new ship every 2 seconds until there are 10.
#[derive(gdnative::NativeClass)]
#[inherit(gdnative::Spatial)]
pub struct StarWorldLink {
world_id : u32,
ship_scene : Option<PackedScene>,
num_ships : u32,
time_passed : f64,
}
#[gdnative::methods]
impl StarWorldLink {
fn _init(_owner: gdnative::Spatial) -> Self {
StarWorldLink {
world_id: 0,
ship_scene: None,
num_ships: 0,
time_passed: 0.0,
}
}
#[export]
fn _ready(&mut self, mut owner: gdnative::Spatial) {
godot_print!("hello, StarWorldLink.");
let ship_scene = ResourceLoader::godot_singleton().load(
GodotString::from_str("res://ship_root.tscn"),
GodotString::from_str("PackedScene"), false);
if let Some(ship_scene) = ship_scene.and_then(|s| s.cast::<PackedScene>()) {
godot_print!("StarWorldLink Have scene");
self.ship_scene = Some(ship_scene);
}
else {
godot_print!("StarWorldLink could not load ship_link scene");
}
}
#[export]
fn _process(&mut self, mut owner: gdnative::Spatial, delta: f64) {
self.time_passed += delta;
if self.num_ships < 10 && self.time_passed > (self.num_ships as f64 * 2.0) {
if let Some(ship_scene) = &self.ship_scene {
self.num_ships += 1;
let mut instance = ship_scene.instance(0); // 0 - GEN_EDIT_STATE_DISABLED
if let Some(mut instance) = instance.and_then(|inst| unsafe {
inst.cast::<Spatial>()
}) {
let transform = Transform::translate(Vector3::new(10.0 + 6.0 * self.num_ships as f32, 0.0, 0.0));
unsafe {
instance.set_global_transform(transform);
owner.add_child(Some(instance.to_object()), false);
}
godot_print!("StarWorldLink Instance {} Made", self.num_ships);
} else {
godot_print!("Should be able to instance scene");
}
}
}
}
}
Glad that it works! :) PR #168 is merged, does this resolve this issue so it can be closed or do you think it should stay open?
We should probably create a simple example as originally requested, I'm sure it would be highly useful for the next new person.
Yes sounds good, I will create a tracker issue for this.
Scene Creation example here - https://github.com/GodotNativeTools/godot-rust/pull/170
I would expect that #170 makes it well covered but lets see if OP has something to say :D
Yeah, that looks sufficient. If I need anything else, I'll fiddle with it and perhaps make a new example.
I'm working on a terrain generator, and that requires loading and instantiating scenes, and then adding them as children to the current object. I have an example in GDScript that does that, but I can't figure out how to port it to Rust.
I'm essentially trying to replicate this GDScript:
And here's my attempt in Rust:
The first line seems to work since I can print out
loader
withgodot_print!("loader: {:?}")
, and it gives me something like this:However, the next line always crashes the game, with the following being printed out periodically:
From what I can tell, the last two arguments are optional, so I gave them what appear to be default values.
I'd be happy to create an example that loads resources once I can figure it out. Here's my terrain generation code in GDScript that I'm trying to port to Rust:
EDIT:
I found another project that has bindings that seem to work with resource loading, so I think this is a problem with these bindings, but I'm not 100% sure:
https://github.com/Thinkofname/rust-godot-example/blob/master/rust/src/lib.rs