godot-rust / gdnative

Rust bindings for Godot 3
https://godot-rust.github.io
MIT License
3.61k stars 210 forks source link

Inheriting non-Godot classes #331

Closed MelvIsntNormal closed 4 years ago

MelvIsntNormal commented 4 years ago

I'm trying to port some code from GDScript to Rust, and I'm looking for a way for a class I've written to inherit another class I've written:

# Hitbox.gd
extends Area2D

class_name Hitbox

export var damage := 1

# SwordHitbox.gd
extends Hitbox

class_name SwordHitbox

export var knockback_vector := Vector2.ZERO

Here's my Rust code for Hitbox:

use gdnative::{Area2D, NativeClass};

#[derive(NativeClass)]
#[inherit(Area2D)]
pub struct Hitbox {
    #[property(path = "/base/damage")]
    damage: u16,
}

#[methods]
impl Hitbox {
    fn _init(_owner: Area2D) -> Self {
        Hitbox { damage: 1 }
    }
}

This is what I've tried for SwordHitbox:

use gdnative::{NativeClass, Vector2};
use crate::hitbox::Hitbox;

#[derive(NativeClass)]
#[inherit(Hitbox)]
pub struct SwordHitbox {
    knockback_vector: Vector2
}

#[methods]
impl SwordHitbox {
    fn _init(_owner: Hitbox) -> Self {
        SwordHitbox { knockback_vector: Vector2::zero() }
    }
}

And I get this error:

error[E0277]: the trait bound `hitbox::Hitbox: gdnative::GodotObject` is not satisfied
 --> src/sword_hitbox.rs:4:10
  |
4 | #[derive(NativeClass)]
  |          ^^^^^^^^^^^ the trait `gdnative::GodotObject` is not implemented for `hitbox::Hitbox`
  |
  = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

I've had a look at GodotObject and it seems like I need to implement a couple of methods that I don't know the details of. Is inheriting Rust structs implemented? Is there any documentation I could read on this topic?

ghost commented 4 years ago

There is no inheritance in Rust, and Rust types are not classes. #[inherit] is just a way to tell our procedural macro to tell Godot that your script can be attached to an Godot object of a certain type. It isn't anything close to the magic that OO languages perform behind the scenes. Unfortunately, there is no straightforward way to translate an OO class hierarchy to idiomatic Rust. You will have to do some work to re-design your architecture.

For more information on how Rust differs from traditional OO languages in its approach, TRPL has an excellent chapter: https://doc.rust-lang.org/book/ch17-00-oop.html

MelvIsntNormal commented 4 years ago

Thanks for the quick reply, though I feel like I shouldn't have been so imprecise in my language now! I am aware of how Rust works and how it doesn't have classes or inheritance; I used the word "class" to refer to something I could attach to a Node (I'm still struggling to word that properly). I guess that's what I get for being lazy :/

I was hoping to avoid changing things too much since I'm trying to translate a tutorial, but it looks like I will have to work around that now. Again, thanks for the help.

ghost commented 4 years ago

something I could attach to a Node

In GDNative terms we usually call that a "script". The base class to script "inheritance" provided by Godot core is only single-level. Inheritance between GDScript classes, for example, depends on language features instead. Since there is no language-level inheritance in Rust, there is no easy inheritance between script types either.

For your specific case, I think it's fine to simply put knockback_vector on Hitbox. The original example is gratuitously "OO" and there is really no need to emulate that in Rust.