rust 类型的编译时操作

0sgqnhkj  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(78)

我正在从头开始重新设计我的ECS,这次我选择了原型。
原型ECS的核心思想是,我有一个Map,将原型(组件类型的元组)id绑定到包含该原型的所有实体(以及组件数据)的桶。
我想要的是:
我认为我的实体在它们的原型上是通用的,以便在编译时了解每个实体有什么组件,并保证get_component方法的结果。

pub struct Entity<Archetype> {
    archetype_marker: PhantomData<Archetype>,
    index: usize,
}

对于Rust类型,我认为原型应该是直接的类型元组,因为这是最有意义的。例如,(String, usize, u32)是一个原型。id可以用TypeId来计算。
我已经有了一个基本的结构:

struct ComponentTable {
    // for each archetype, store the entities components.
    components: ArchetypeMap,
}

struct ComponentBucket<Archetype> {
    entities: HashMap<usize, Archetype>,
    last_index: usize,
}

ArchetypeMap是一个类似于the AnyMap crate的Map,其中我将ArchetypeMap到ComponentBucket<Archetype>,除了我在mapp中强制实现AbstractBucket trait的值:

pub trait AbstractBucket {
    fn query<Archetype>(&self) -> Option<impl Iterator<Type = Archetype>>;
}

这里的目标是能够覆盖所有的桶进行迭代,即使使用原型Map提供的类型保证。
现在,ECS需要能够创建/删除实体,这对于这个方法来说是微不足道的。

impl ComponentTable {
    pub fn create_entity<Archetype: 'static>(&mut self, components: Archetype) -> Entity<Archetype> {
        match self.components.get_mut::<ComponentBucket<Archetype>>() {
            Some(bucket) => bucket.create_entity(components),
            None => {
                let mut bucket = ComponentBucket::default();
                let entity = bucket.create_entity(components);
                self.components.insert::<ComponentBucket<Archetype>>(bucket);
                entity
            }
        }
    }

    pub fn remove_entity<Archetype: 'static>(&mut self, entity: Entity<Archetype>) -> Option<Archetype> {
        self.components.get_mut::<ComponentBucket<Archetype>>()?.remove_entity(entity)
    }
}

impl<Archetype> ComponentBucket<Archetype> {
    pub fn create_entity(&mut self, components: Archetype) -> Entity<Archetype> {
        let index = self.last_index;
        self.last_index += 1;
        self.entities.insert(index, components);
        Entity::new(index)
    }

    pub fn remove_entity(&mut self, entity: Entity<Archetype>) -> Option<Archetype> {
        self.entities.remove(&entity.index())
    }
}

添加/删除组件可以通过明确完整的原型元组来完成,但我宁愿有一个适用于每个原型的方法,因为这个实现将需要根据实体的原型大小有很多不同的名称:

impl ComponentTable {
    pub fn add_component<T1: 'static, T2: 'static, C: 'static>(&mut self, entity: Entity<(T1, T2)>, component: C) -> Option<Entity<(T1, T2, C)>> {
        let archetype = self.remove_entity(entity)?;
        let new_entity = self.create_entity((archetype.0, archetype.1, component));
        Some(new_entity)
    }

    pub fn remove_component<T1: 'static, T2: 'static, C: 'static>(&mut self, entity: Entity<(T1, T2, C)>) -> Option<Entity<(T1, T2)>> {
        let archetype = self.remove_entity(entity)?;
        let new_entity = self.create_entity((archetype.0, archetype.1));
        Some(new_entity)
    }
}

现在这是我卡住的地方,试图为我的bucket实现AbstractBucket trait:

impl<BucketArchetype> AbstractBucket for ComponentBucket<BucketArchetype> {
    fn query<Archetype>(&self) -> Option<()> {
        // ???? 
    }
}

我怎么能有办法把类型解包成类型的元组呢?这看起来没有意义,因为我的类型不一定是元组,但如果它们不是,我们可以将它们视为一个元素的元组。
这一职能应:如果ArchetypeBucketArchetype的子原型,则返回原型上的迭代器(具有从桶原型到请求原型的Map)。否则,返回None
有了Rust强大的类型系统,我觉得我试图实现的是可行的,但我既没有构建它,也没有找到编译器无法理解的一些例子,证明我的设计无法工作。
我看过现有原型ECS的例子,但它们似乎都在运行时保持原型的价值,而我试图在编译时完成大部分工作。
我还研究了variadic泛型,因为这看起来很适合,但它们在Rust中不可用。
最后,我尝试在编译时对类型执行操作,比如将类型添加到类型元组,并以这种方式构建新类型。这看起来像是在编译时可以执行的操作,但我正在努力解决它。也许Rust不够强大?也许我需要一些更强大的架构为我的原型系统?

x33g5p2x

x33g5p2x1#

如果Archetype是BucketArchetype的子原型,则返回原型上的迭代器(具有从bucket原型到asked原型的Map)。否则,返回None。
这听起来像是你可能想表达一个创建原型的能力的界限:从,也许像:
ecs_contract.rs:

pub struct Entity<Archetype> {
    archetype_marker: PhantomData<Archetype>,
    index: usize,
}

struct ComponentTable {
    // for each archetype, store the entities components.
    components: ArchetypeMap, // you must clarify this type!
}

struct ComponentBucket<Archetype> {
    entities: HashMap<usize, Archetype>,
    last_index: usize,
}

// functionalize the entities to enable more sensible default impl of AbstractBucket
pub trait Entities<A> {
    fn entities(&self) -> HashMap<usize, A>;
}

// note: you probably want to use lifetimes to avoid cloning your underlying bucket data to make this iterator (assuming sub archetypes are subsets of archetypes)
pub trait AbstractBucket<BucketArchetype>: Entities<BucketArchetype> {
    fn query<Archetype>(&self) -> Option<impl Iterator<Type = Archetype>>
    where Archetype: From<BucketArchetype> {
    Some(self.entities().values().map(Archetype::from))
   }
}

ecs_impls.rs:

impl ComponentTable {
    pub fn create_entity<Archetype: 'static>(&mut self, components: Archetype) -> Entity<Archetype> {
        match self.components.get_mut::<ComponentBucket<Archetype>>() {
            Some(bucket) => bucket.create_entity(components),
            None => {
                let mut bucket = ComponentBucket::default();
                let entity = bucket.create_entity(components);
                self.components.insert::<ComponentBucket<Archetype>>(bucket);
                entity
            }
        }
    }

    pub fn remove_entity<Archetype: 'static>(&mut self, entity: Entity<Archetype>) -> Option<Archetype> {
        self.components.get_mut::<ComponentBucket<Archetype>>()?.remove_entity(entity)
    }
}

impl<Archetype> ComponentBucket<Archetype> {
    pub fn create_entity(&mut self, components: Archetype) -> Entity<Archetype> {
        let index = self.last_index;
        self.last_index += 1;
        self.entities.insert(index, components);
        Entity::new(index)
    }

    pub fn remove_entity(&mut self, entity: Entity<Archetype>) -> Option<Archetype> {
        self.entities.remove(&entity.index())
    }
}

// default implementation
impl<BucketArchetype> AbstractBucket for ComponentBucket<BucketArchetype> {}

要保存多种原型,您可能希望创建一个嵌套结构,其中的conceptTable保存一个hashmap的hashmap,每种原型一个hashmap,您可能需要ArchetypeMap行为的更宽松的函数定义。你能提供更多关于你想如何实现多类型Map的细节吗?
一个简单的方法来改善这一切是更多地关注的特点,使特点类似于你想要使用的东西的形式,然后只是使结构,以适应他们之后;然后你可以很容易地在不同的结构上重新实现traits。即原型,实体,组件,这些都是特征,然后TupleArchetype是一个结构,MapArchetype是一个结构,等等......功能抽象接口总是允许多个结构具体实现。
一个想法是使原型Map的函数透镜化成为原型类上的原型方法。然后,不是组件表处理所有类型的原型,原型将从原型树的分支中插入和删除自己。对不起,我试图解决这个问题,但我对Map上的类型感到困惑。希望这能帮上一点忙!

相关问题