Rust-在Vec <Box dyn ComponentTrait >>ECS中获取组件

问题描述

我是使用Rust语言的初级开发人员。 我来自JavaScript,许多功能和特性我仍然不清楚。

当前,我正在寻求在Rust中构建自己的ECS(实体组件系统)系统。 当我想从实体中获取组件时,我会陷入困境。

我确实将组件存储在带有dyn盒装矢量的实体中,这是个好方法吗?

我的代码

enum ComponentEnum {
    Position,Size
}

trait Component {}

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

// Size Component
#[derive(PartialEq,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);
}

如何从我的实体取回“位置”组件?

编辑:

这里是一个用于获取组件的测试函数(在Entity实现中):

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
}

谢谢。

解决方法

我将使用枚举来区分组件类型(请记住,总体而言,我对ECS系统的经验很少)。然后,您可以通过多种方式获得一种类型,但是我已经建立了一种方法get_component,在寻找正确的组件时需要使用闭包。然后,您可以将其传递给闭包,以专门检查位置组件。

这是我的实现,基于您的示例:


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

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

#[derive(PartialEq,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);
}

请注意,get_component可以有很多替代方案,但是我的主要目的是使用枚举来区分组件类型,而不要使用Box<dyn Component>

,

@ user4815162342在评论中发布了此内容,以便直接索引到entity.components

像这样:

    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 v0 = &entity1.components[0];
        let v1 = &entity1.components[1];
        v0.display();
        v1.display();
    }   

但是由于索引取决于添加实体的顺序,所以最好将实体组件存储在哈希图中,或者使用enum标签以使每个组件的内容更清楚是。