Search code examples
rustentity-component-system

Rust - Get component in Vec<Box dyn ComponentTrait>> (ECS)


I'am a junior developer with Rust language. I come from JavaScript and a lot of features and specificities are still unclear to me.

Currently, I'm looking to build my own ECS (entity component system) system in Rust. I'am stay stuck when i want to get an component from an entity.

Actualy i store component in entity with an dyn boxed vector, it's that a good way?

My code:

enum ComponentEnum {
    Position,
    Size
}

trait Component {}

// Position Component
#[derive(PartialEq, PartialOrd, Debug)]
struct Position {
    x: i32,
    y: i32
}

// Size Component
#[derive(PartialEq, PartialOrd, Debug)]
struct Size {
    height: i32,
    width: i32
}

impl Component for Position {}
impl Component for Size {}

struct Entity {
    id: usize,
    components: Vec<Box<dyn Component>>
}

impl Entity {
    fn new(index: usize) -> Self {
        Entity { id: index, components: vec![] }
    }

    // Add a component in Entity
    fn add_component<T: 'static + Component>(&mut self, component: T) {
        self.components.push(Box::new(component));
    }
}

struct EntityStore {
    entities: Vec<Entity>,
    current_index: usize,
}
impl EntityStore {
    fn new() -> EntityStore {
        EntityStore { entities: vec![], current_index: 0 }
    }

    fn generate_index(&self) -> usize {
        unimplemented!();
    }

    // Stop creation system and update EntityStore current_index
    fn end(&mut self) -> &mut Entity {
        let entity = self.entities.get_mut(self.current_index).unwrap();
        self.current_index = self.current_index + 1;
        entity
    }

    fn create_entity(&mut self) -> &mut Self {
        let mut entity = Entity::new(self.current_index);
        self.entities.push(entity);

        self
    }

    // Add component to entity
    fn with_component<T: 'static + Component>(&mut self, component: T) ->  &mut Self {
        let mut entity = self.entities.get_mut(self.current_index).unwrap();
        entity.add_component(component);

        self
    }
}

fn main() {
    let mut es = EntityStore::new();

    // Make entity
    let mut entity1 = es
        .create_entity()
        .with_component(Position { x: 0, y: 0 })
        .with_component(Size { height: 10, width: 10 })
        .end();

    // Get entity position component
    // let component_position_entity1 = entity1.get_component(ComponentEnum::Position);
}

How can I get my Position component back from my entity?

EDIT:

Here, a test function to get a component (in Entity implementation) :

fn get_component(&mut self, component_enum: ComponentEnum) { //want return Position or Size component
        let mut entity_components = &self.components;

        // Search component by Name ?
        // Currently, i try to compare Component trait with Component Enum element...
        let component = entity_components
            .iter_mut()
            .find(|component| component == component_enum)
            .unwrap();

        // Here, the component type is "&mut Box<dyn Component>" but i want type like "&mut Position" or "&mut Size"

        component // Here i need to return a Position or Size struct component, but i have Component Trait so i can't use Position/Size functions
}

Thanks.


Solution

  • I would use enums to differentiate between components types (bear in mind I have very little experience with ECS systems in general). Then you have various ways of getting one type, but I have made a method get_component, that takes a closure to use when finding the right components. You can then pass it a closure that checks for a position component specifically.

    This is my implementation, based on your example:

    
    // Position Component
    #[derive(PartialEq, PartialOrd, Debug)]
    struct Position {
        x: i32,
        y: i32
    }
    
    // Size Component
    #[derive(PartialEq, PartialOrd, Debug)]
    struct Size {
        height: i32,
        width: i32
    }
    
    #[derive(PartialEq, PartialOrd, Debug)]
    enum Component {
        Position(Position),
        Size(Size)
    }
    
    struct Entity {
        id: usize,
        components: Vec<Component>
    }
    
    impl Entity {
        fn new(index: usize) -> Self {
            Entity { id: index, components: vec![] }
        }
    
        // Add a component in Entity
        fn add_component(&mut self, component: Component) {
            self.components.push(component);
        }
        
        fn get_component(&self, pred: impl Fn(&&Component) -> bool) -> Option<&Component>{
            self.components.iter().find(pred)
        }
        
    }
    
    struct EntityStore {
        entities: Vec<Entity>,
        current_index: usize,
    }
    impl EntityStore {
        fn new() -> EntityStore {
            EntityStore { entities: vec![], current_index: 0 }
        }
    
        fn generate_index(&self) -> usize {
            unimplemented!();
        }
    
        // Stop creation system and update EntityStore current_index
        fn end(&mut self) -> &mut Entity {
            let entity = self.entities.get_mut(self.current_index).unwrap();
            self.current_index = self.current_index + 1;
            entity
        }
    
        fn create_entity(&mut self) -> &mut Self {
            let mut entity = Entity::new(self.current_index);
            self.entities.push(entity);
    
            self
        }
    
        // Add component to entity
        fn with_component(&mut self, component: Component) ->  &mut Self {
            let mut entity = self.entities.get_mut(self.current_index).unwrap();
            entity.add_component(component);
    
            self
        }
    }
    
    fn main() {
        let mut es = EntityStore::new();
    
        // Make entity
        let mut entity1 = es
            .create_entity()
            .with_component(Component::Position(Position { x: 0, y: 0 }))
            .with_component(Component::Size(Size { height: 10, width: 10 }))
            .end();
    
        // Get entity position component
        let component_position_entity1 = entity1.get_component(|c| if let Component::Position(_) = c { true} else {false});
        println!("{:?}", component_position_entity1);
    }
    

    Note that there many alternatives to my get_component, but my main point is to use enums to differentiate component types and to not use Box<dyn Component>.