Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to get component/components from a particular entity? #283

Open
DrOptix opened this issue Sep 22, 2021 · 2 comments
Open

How to get component/components from a particular entity? #283

DrOptix opened this issue Sep 22, 2021 · 2 comments

Comments

@DrOptix
Copy link

DrOptix commented Sep 22, 2021

I browsed the docs, but I could not figure out for myself.

I know we have queries that will let us go trough all the components of type C from a world, but is there a way to get a component of type C from a particular entity?

Actually I'm interested in getting references &T and &mut T to the component attached to a particular entity.

I tried to do something like:

pub fn with_component<T: Component>(&self) -> Option<&T> {
        if let Ok(entry) = self.world.borrow_mut().entry_ref(self.entity) {
            if let Ok(component) = entry.get_component::<T>() {
                return Some(component);
            }
        }

        None
    }

but then it complained that the reference I return does not live long enough.

I want something like this because I just want &T or &mut T to the component, why do we need to deal with that entry step? :-(

Thank you!

@DrOptix
Copy link
Author

DrOptix commented Sep 22, 2021

More info. My current solution is to pass a callback that gets called with the component reference,

I need all this for a little game engine I work on:
https://github.com/DrOptix/ghost-engine

Relevant files are entity.rs from ghost-engine project and sandbox_layer.rs from ghost-sandbox

pub fn with_component<T: Component, F: FnOnce(&T)>(&self, f: F) {
        if let Ok(entry) = self.world.borrow_mut().entry_ref(self.entity) {
            if let Ok(component) = entry.get_component::<T>() {
                f(component);
            }
        }
    }

    pub fn with_component_mut<T: Component, F: FnOnce(&mut T)>(&mut self, f: F) {
        if let Ok(mut entry) = self.world.borrow_mut().entry_mut(self.entity) {
            if let Ok(component) = entry.get_component_mut::<T>() {
                f(component);
            }
        }
    }

And I use it like this when I want to draw some kind of egui UI for the component:

self.square_entity
                    .with_component_mut::<SpriteRendererComponent, _>(|comp| {
                        ui.horizontal(|ui| {
                            ui.label("Color:");

                            let mut new_color = [
                                (comp.color.x * 255.0) as u8,
                                (comp.color.y * 255.0) as u8,
                                (comp.color.z * 255.0) as u8,
                                (comp.color.w * 255.0) as u8,
                            ];

                            ui.color_edit_button_srgba_unmultiplied(&mut new_color);

                            comp.color = glam::vec4(
                                new_color[0] as f32 / 255.0,
                                new_color[1] as f32 / 255.0,
                                new_color[2] as f32 / 255.0,
                                new_color[3] as f32 / 255.0,
                            );
                        });

                        ui.separator();
                    });

I can live with this, but I would try to avoid the need to design for my self an API where I need to provide callbacks.

@zedrian
Copy link

zedrian commented Sep 23, 2021

Hi @DrOptix,
Unfortunately, I don't see another solution in your case since your Entity is self-contained and stores the world wrapped into RefCell.

However, have you considered the option of making your engine less object-oriented and more ECS-driven, meaning that most of the logic is written in systems?
As an example, I can show you some code I wrote when considering such OOP-to-ECS migration. This system is added to the main application Schedule and contains all the logic needed to create and modify entities in my sandbox (I can't say it's super efficient, but I like how it works):

#[legion::system]
fn manual_creator(#[state] current_editable_entity: &mut Option<Entity>,
                  #[resource] mouse: &Mouse,
                  #[resource] factory: &EntityFactory,
                  #[resource] mouse_button_pressed: &Events<MouseButtonPressed>,
                  #[resource] mouse_button_released: &Events<MouseButtonReleased>,
                  commands: &mut CommandBuffer,
                  world: &mut SubWorld,
                  _dummy: &mut Query<(&Position, &mut Mass)>)
{
    // pressing left mouse button initiates new entity creation
    {
        let should_create_new_entity = mouse_button_pressed
            .iter()
            .find(|MouseButtonPressed { button, .. }| *button == MouseButton::Left)
            .is_some();

        if should_create_new_entity {
            if let Some(CursorState::InsideWindow { x, y }) = mouse.get_cursor_state() {
                *current_editable_entity = Some(factory.create_blank(x, y, commands));
            }
            return;
        }
    }

    // releasing left mouse button finalizes entity creation
    {
        let should_finalize_creation = mouse_button_released
            .iter()
            .find(|MouseButtonReleased { button, .. }| *button == MouseButton::Left)
            .is_some();

        if should_finalize_creation {
            factory.finalize_creation(current_editable_entity.take().unwrap(), commands, world);
            return;
        }
    }

    // moving the mouse cursor updates the mass for the current editable entity (if any)
    if current_editable_entity.is_some() {
        if let Some(CursorState::InsideWindow { x, y}) = mouse.get_cursor_state() {
            let mut entry = world.entry_mut(*current_editable_entity.as_ref().unwrap()).unwrap();
            let position = entry.get_component::<Position>().unwrap().clone();
            let mass = entry.get_component_mut::<Mass>().unwrap();

            let distance_from_center = (position.0 - Vec2::new(x, y)).magnitude();
            *mass = radius_to_mass(distance_from_center);
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants