makspll / bevy_mod_scripting

Bevy Scripting Plugin
Apache License 2.0
411 stars 31 forks source link

Registering a Bundle for Lua #105

Open treytencarey opened 8 months ago

treytencarey commented 8 months ago

I am experimenting with bevy_mod_scripting, sorry for any confusion.

I want to display Text purely in Lua. Here's the code so far

--[[ "convenience to add a component by string type name and return the component handle" ]]
local function add_component(entity, type_name)
    local t = world:get_type_by_name(type_name)
    world:add_default_component(entity, t)
    return world:get_component(entity, t)
end

function once()
    local e = world:spawn()

    local TextSection = world:get_type_by_name("TextSection")
    print(TextSection) -- "ScriptTypeRegistration"

    local TextStyle = world:get_type_by_name("TextStyle")
    print(TextStyle) -- "ScriptTypeRegistration"

    local text = add_component(e, "Text")
    print(text) -- "Text { sections: [], alignment: Left, linebreak_behavior: WordBoundary }"
    -- local text_section = add_component(e, "TextSection") -- error: TextSection is not a component
end

I am having troubles figuring out how to create a TextStyle and TextSection for use in my test.sections. Any ideas?

makspll commented 8 months ago

Hi, the error message is right, the TextSection and TextStyle structs are not components so they cannot be added as component to an entity

treytencarey commented 8 months ago

Yes I was mistaken. I suppose my question was more about spawning entities from within Lua but it doesn't look like there is support for that yet(?) I haven't had much time to look at the api gen code yet, but I plan to soon.

makspll commented 8 months ago

I see, you're on the right track world::spawn works as you expect it to, world does indeed have a spawn method which should give you a new entity:

        methods.add_method("spawn", |_, world, ()| {
            let mut w = world.write();

            Ok(LuaEntity::new(w.spawn(()).id()))
        });
treytencarey commented 8 months ago

After some further investigation, it seems like what I want is to use the world to spawn bundles (w.spawn(ButtonBundle)).

Unfortunately ButtonBundle doesn't have Reflect trait required by the api generator.

I am looking for some guidance on how to proceed, if it would be a good idea to generate wrappers for those without Reflect, e.g.

#[derive(Reflect)]
pub struct ButtonBundleWrapper {
    button_material: Handle<ColorMaterial>,
    style: Style,
    // ... other fields ...
}

impl ButtonBundleWrapper {
    // Create a new wrapper from a ButtonBundle
    pub fn new(bundle: ButtonBundle) -> Self {
        // Extract each component from the bundle, or something like that

        let button_material = bundle.0;
        let style = bundle.1;
        // ... other fields ...

        Self {
            button_material,
            style,
            // ... other fields ...
        }
    }

    // Convert the wrapper back into a ButtonBundle for world spawn
    pub fn into_bundle(self) -> ButtonBundle {
        ButtonBundle(self.button_material, self.style /*, ... other fields ...*/)
    }
}

But there might be a better way I'm missing.

makspll commented 7 months ago

Hi, so bundles are not currently supported since they are neither components or resources (these are the only types we can make references to via ReflectReference), And if the type doesn't support Reflect, we cannot attach a ReflectLuaProxyable type data onto it to transform it into a custom proxy, or even construct a ReflectedValue to refer to it.

Since we do not have runtime type erased access to something like ButtonBundle, the only way to handle this is at compile time, i.e. defining a custom UserData, which knows how to access the world and handle the ButtonBundle