Sync `.tscn` files with Rust code
- Dominant language
- Rust
- Stars
- 5.2k
- Forks
- 312
- Avg merge
- 11h 10m
- Merged PRs (30d)
- 10
Description
_Edit bromeon: Rust tags for syntax highlighting_
I love Godot (for a variety of reasons), but its reliance on non-typesafe scene files, and (similarly) runtime crashes caused by misconfiguration, is not one of them. I'd love to do away with this, and the easiest way to do this in my mind is to enshrine the `.tscn` files into my nice, typesafe, rust code.
Theoretical example that I have:
# The Goal
I want this node hierarchy, where `MyCustomType` is a rust class, which needs a reference to `Gd` and `Gd`:
```
MyCustomType
|- Camera3D
|- Sprite3D
```
# The Options
### The Bad
I could create a `.tscn` file, and then just load it everywhere (using `OnEditor` in the examples below to nab a reference automatically), but then any change of the root type of my `tscn` now needs a corresponding change in my rust code, otherwise the cast fails at runtime. Less than ideal.
### The Ugly
I could generate the hierarchy manually...
```rs
// my_custom_type.rs
#[derive(GodotClass)]
#[class(base=Node3D)]
pub struct MyCustomType {
camera: OnReady>,
sprite: OnReady>,
base: Base,
}
#[godot_api]
impl INode3D for MyCustomType{
fn init(base: Base) -> Self {
Self {
base,
camera: OnReady::manual(),
sprite: OnReady::manual(),
}
}
fn ready(&mut self) -> () {
let camera = Camera3D::new_alloc();
self.camera.init(camera);
let sprite = Sprite3D::new_alloc();
self.sprite.init(sprite);
let camera = self.camera.clone();
let sprite = self.sprite.clone();
{
let mut base = self.base_mut();
base.add_child(&camera);
base.add_child(&sprite);
}
}
}
```
But this is a bit overly verbose, and if I forget to initialize `sprite` in `ready`? Well we're panicking again.
# The alternative
(disclaimer, I am a rust novice, but I think something like this is theoretically doable?)
```rs
static Scene: Scene = scene!({
Camera3D {
transform: Transform { origin: Vector3 { x: 1, ..default() }, ..default() },
}
Sprite3D {
}
});
#[derive(GodotClass)]
#[class(base=Node3D)]
pub struct MyCustomType {
camera: InScene>,
sprite: InScene>,
base: Base,
}
impl INode3D for MyCustomType {
fn init(base: Base) -> Self {
Self {
base,
camera: Scene.Camera3D,
sprite: Scene.Sprite3D,
}
}
}
```
Notably, behind the scenes (heh), we'd have `godot-rust` run the machinery to add the children to the tree, with the specified overrides (here, a different position). Hell, if it's performant, we could even emit a `tscn` file and load behind the scenes, but I think that gets a bit wonky and is definitely out of scope.
`InScene` would work similarly to `OnReady`, except the initialization in `init` would take in the path in the scene hierarchy, and every further call would be already resolved and realized?
# Closing remarks
This is notably _incredibly_ handwavy, and doesn't take into account a few key problems (scene tree modification, nodes with different names, up casting, base types + specialized scenes), but I figured this was enough of a formed idea rolling around in my head to at least start the discussion here.
Would love to keep spitballing ideas / help with implementation as needed
Contributor guide
Research direction
The issue names no repository files, tests, or concrete entry points. Start by reviewing the proposed scene! and InScene APIs alongside the existing OnReady approach described in the issue; the work is done when the scope, scene-tree behavior, type handling, and implementation criteria are agreed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- godot, rust
- Domain
- game-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100