Rust — Получить компонент в Vec> (ECS)

#rust #entity-component-system

#Ржавчина #сущность-компонент-система

Вопрос:

Я младший разработчик с языком Rust. Я пришел из JavaScript, и многие функции и особенности для меня все еще неясны.

В настоящее время я собираюсь создать свою собственную систему ECS (entity component system) в Rust. Я застрял, когда хочу получить компонент от объекта.

На самом деле я сохраняю компонент в сущности с помощью dyn-векторной упаковки, это хороший способ?

Мой код:

 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>(amp;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(amp;self) -> usize {
        unimplemented!();
    }

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

    fn create_entity(amp;mut self) -> amp;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>(amp;mut self, component: T) ->  amp;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);
}
  

Как я могу вернуть свой компонент Position из моей сущности?

Редактировать:

Здесь тестовая функция для получения компонента (в реализации объекта) :

 fn get_component(amp;mut self, component_enum: ComponentEnum) { //want return Position or Size component
        let mut entity_components = amp;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 "amp;mut Box<dyn Component>" but i want type like "amp;mut Position" or "amp;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
}
  

Спасибо.

Комментарии:

1. В чем здесь собственно вопрос? Код не компилируется, или он работает не так, как ожидалось, или вы просто ищете code review? Было бы полезно, если бы вы описали реальную проблему, которую хотите решить, а не полностью абстрактный шаблон типа «система компонентов объекта», который может плохо отображаться на Rust и может даже не понадобиться для того, над чем вы работаете.

2. Привет! Извините за это, мой вопрос таков: с моим текущим кодом, как я могу получить компонент из объекта?

3. Глядя на ваш код, похоже, что amp;entity[index] это должно сработать. Пожалуйста, покажите нам, что вы пробовали и где вы застряли — например, вы написали get_component() функцию, но она не смогла скомпилироваться? Также, пожалуйста, имейте в виду, что не все знакомы с конкретным шаблоном, который вы пытаетесь реализовать.

4. Я обновил свой пост с помощью тестовой функции для получения компонента ^^

Ответ №1:

Я бы использовал перечисления для различения типов компонентов (имейте в виду, что у меня очень мало опыта работы с системами ECS в целом). Тогда у вас есть различные способы получения одного типа, но я создал метод get_component , который требует замыкания для использования при поиске нужных компонентов. Затем вы можете передать ему замыкание, которое проверяет наличие компонента position конкретно.

Это моя реализация, основанная на вашем примере:

 
// 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(amp;mut self, component: Component) {
        self.components.push(component);
    }
    
    fn get_component(amp;self, pred: impl Fn(amp;amp;Component) -> bool) -> Option<amp;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(amp;self) -> usize {
        unimplemented!();
    }

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

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

        self
    }

    // Add component to entity
    fn with_component(amp;mut self, component: Component) ->  amp;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> .

Ответ №2:

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

Но поскольку индекс зависит от порядка добавления объектов, то вам было бы намного лучше хранить компоненты объектов в хэш-карте или с enum тегом, чтобы было понятнее, что представляет собой каждый компонент.