godot-rust / gdnative

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

Can GDNative Rust be simplied down to like this? #155

Closed Raj2032 closed 5 years ago

Raj2032 commented 5 years ago

https://unrealcpp.com/rotating-actor/

Taking a look at Unreal engine C++ code what it is supposed to do is rotate and stuff, and this doesn't look to bad in terms of the boilerplate code. Can it be simpled as much as compared to Unreal Engine C++?

rminderhoud commented 5 years ago

There's literally an example that has a rotation...looks simple enough to me...

    unsafe fn _physics_process(&mut self, mut owner: godot::MeshInstance, delta: f64) {
        use godot::{Color, Vector3, SpatialMaterial};

        self.time += delta as f32;
        owner.rotate_y(self.rotate_speed * delta);

        let offset = Vector3::new(0.0, 1.0, 0.0) * self.time.cos() * 0.5;
        owner.set_translation(self.start + offset);

        if let Some(mat) = owner.get_surface_material(0) {
            let mut mat = mat.cast::<SpatialMaterial>().expect("Incorrect material");
            mat.set_albedo(Color::rgba(self.time.cos().abs(), 0.0, 0.0, 1.0));
        }
}

https://github.com/GodotNativeTools/godot-rust/blob/master/examples/spinning_cube/src/lib.rs

Raj2032 commented 5 years ago

I don't think I was asking about that, I was talking about there is so much biolerplate code just to rotate the cube, take a look at this this is what I got after when I cloned from the github repo

#[macro_use]
extern crate gdnative as godot;

use godot::init::{Property, PropertyHint, PropertyUsage};
use godot::GodotString;

struct RustTest {
    start: godot::Vector3,
    time: f32,
    rotate_speed: f64,
}

impl godot::NativeClass for RustTest {
    type Base = godot::MeshInstance;

    fn class_name() -> &'static str {
        "RustTest"
    }

    fn init(_owner: Self::Base) -> Self {
        Self::_init()
    }

    fn register_properties(builder: &godot::init::ClassBuilder<Self>) {
        builder.add_property(
            Property {
                name: "base/rotate_speed",
                default: 0.05,
                hint: PropertyHint::Range {
                    range: 0.05..1.0,
                    step: 0.01,
                    slider: true
                },
                getter: |this: &mut RustTest| this.rotate_speed,
                setter: |this: &mut RustTest, v| this.rotate_speed = v,
                usage: PropertyUsage::DEFAULT,
            }
        );

        builder.add_property(
            Property {
                name: "test/test_enum",
                default: GodotString::from_str("Hello"),
                hint: PropertyHint::Enum {
                    values: &[
                        "Hello",
                        "World",
                        "Testing",
                    ]
                },
                getter: |_: &mut RustTest| { GodotString::from_str("Hello") },
                setter: (),
                usage: PropertyUsage::DEFAULT,
            }
        );

        builder.add_property(
            Property {
                name: "test/test_flags",
                default: 0,
                hint: PropertyHint::Flags {
                    values: &["A", "B", "C", "D" ],
                },
                getter: |_: &mut RustTest| 0,
                setter: (),
                usage: PropertyUsage::DEFAULT,
            }
        );
    }
}

#[godot::methods]
impl RustTest {

    fn _init() -> Self {
        RustTest {
            start: godot::Vector3::new(0.0, 0.0, 0.0),
            time: 0.0,
            rotate_speed: 0.05,
        }
    }

    #[export]
    unsafe fn _ready(&mut self, mut owner: godot::MeshInstance) {
        owner.set_physics_process(true);
        self.start = owner.get_translation();
        godot_warn!("Start: {:?}", self.start);
        godot_warn!(
            "Parent name: {:?}",
            owner.get_parent().expect("Missing parent").get_name()
        );
    }

    #[export]
    unsafe fn _physics_process(&mut self, mut owner: godot::MeshInstance, delta: f64) {
        use godot::{Color, Vector3, SpatialMaterial};

        self.time += delta as f32;
        owner.rotate_y(self.rotate_speed * delta);

        let offset = Vector3::new(0.0, 1.0, 0.0) * self.time.cos() * 0.5;
        owner.set_translation(self.start + offset);

        if let Some(mat) = owner.get_surface_material(0) {
            let mut mat = mat.cast::<SpatialMaterial>().expect("Incorrect material");
            mat.set_albedo(Color::rgba(self.time.cos().abs(), 0.0, 0.0, 1.0));
        }
    }
}

fn init(handle: godot::init::InitHandle) {
    handle.add_class::<RustTest>();
}

godot_gdnative_init!();
godot_nativescript_init!(init);
godot_gdnative_terminate!();

One of the devs informed me that they are working on a macro that reduces some of these biolerplate code but my question is after when the devs work on the macro, how much of this boilerplate code would be removed? Is it going to be similar to Unreal Engine C++ in terms of simplicity?

karroffel commented 5 years ago

The whole code for exporting properties will be replaced by a procedural macro that automates that.

The rest of the code is pretty much of similar "ease" as the Unreal code.

Raj2032 commented 5 years ago

@karroffel Since I don't understand majority of the code, is this code relevant for exporting?

impl godot::NativeClass for RustTest {
    type Base = godot::MeshInstance;

    fn class_name() -> &'static str {
        "RustTest"
    }

    fn init(_owner: Self::Base) -> Self {
        Self::_init()
    }

    fn register_properties(builder: &godot::init::ClassBuilder<Self>) {
        builder.add_property(
            Property {
                name: "base/rotate_speed",
                default: 0.05,
                hint: PropertyHint::Range {
                    range: 0.05..1.0,
                    step: 0.01,
                    slider: true
                },
                getter: |this: &mut RustTest| this.rotate_speed,
                setter: |this: &mut RustTest, v| this.rotate_speed = v,
                usage: PropertyUsage::DEFAULT,
            }
        );

        builder.add_property(
            Property {
                name: "test/test_enum",
                default: GodotString::from_str("Hello"),
                hint: PropertyHint::Enum {
                    values: &[
                        "Hello",
                        "World",
                        "Testing",
                    ]
                },
                getter: |_: &mut RustTest| { GodotString::from_str("Hello") },
                setter: (),
                usage: PropertyUsage::DEFAULT,
            }
        );

        builder.add_property(
            Property {
                name: "test/test_flags",
                default: 0,
                hint: PropertyHint::Flags {
                    values: &["A", "B", "C", "D" ],
                },
                getter: |_: &mut RustTest| 0,
                setter: (),
                usage: PropertyUsage::DEFAULT,
            }
        );
    }
}

#[godot::methods]
impl RustTest {

    fn _init() -> Self {
        RustTest {
            start: godot::Vector3::new(0.0, 0.0, 0.0),
            time: 0.0,
            rotate_speed: 0.05,
        }
    }
karroffel commented 5 years ago

Yes, the whole part in register_properties is only for exporting properties. The rest of that trait implementation is already generated through a procedural macro (check the other examples).

I plan to add support for attributes so that you can annotate fields and they get exported, but that's quite some more work and I didn't spend any time on that yet.