我正在尝试拥有一组实现特定特征的对象。
如果我使用一个返回值的特征,这是可行的
use std::collections::BTreeMap;
struct World {
entities: Vec<usize>,
database: BTreeMap<usize, Box<ReadValue>>,
//database : BTreeMap<usize,Box<ReadEcs>>, // Doesn't work
}
struct SourceInputGateway {
entity_id: usize,
}
trait ReadValue {
fn read(&self) -> f32;
}
impl ReadValue for SourceInputGateway {
fn read(&self) -> f32 {
0.0
}
}
但如果我想将 Self
作为值返回,那么这将不起作用,无论是作为方法模板参数还是关联类型
trait ReadEcs {
type T;
fn read(&self) -> &Self::T;
}
impl ReadEcs for SourceInputGateway {
type T = SourceInputGateway;
fn read(&self) -> &Self::T {
self
}
}
我想做的是有一个实现 ReadEcs
的类型映射,具体类型并不重要。
进一步澄清编辑
如果我通过添加来扩展示例
// Different sized type
struct ComputeCalculator {
entity_id : usize,
name : String,
}
impl ReadValue for ComputeCalculator {
fn read(&self) -> f32 {
1230.0
}
}
那我就可以了
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_write() {
let mut world = World::new();
world.database.insert(0,Box::new(SourceInputGateway{ entity_id : 1}));
world.database.insert(2,Box::new(ComputeCalculator{ entity_id : 2 , name : "foo".into() }));
for (k,ref v) in world.database {
let item : &Box<ReadValue> = v;
item.read();
}
}
}
但是如果我更改或添加一个返回 Self 的特征方法,我就不能这样做了。 我想了解一种无需不安全指针即可绕过它的方法。
最佳答案
我仔细考虑了这个问题,我认为可以在保留类型安全和连续存储的所有优点的同时解决这个问题。
定义带有存储指针的实体管理器
struct Entities {
entities: Vec<usize>,
containers: Vec<Box<Storage>>,
}
存储行为相关数据的组件本身
struct Position {
entity_id: usize,
position: f32,
}
struct Velocity {
entity_id: usize,
velocity: f32,
}
我们会有很多组件实例,所以我们需要一个连续的内存存储,通过索引访问。
struct PositionStore {
storage: Vec<Position>,
}
struct VelocityStore {
storage: Vec<Velocity>,
}
trait Storage {
// Create and delete instances of the component
fn allocate(&mut self, entity_id: usize) -> usize;
// Interface methods that would correspond to a base class in C++
fn do_this(&self);
// fn do_that(&self);
}
store 的 trait 实现了一个 arena
风格的存储以及它将传递给组件的方法。这可能位于 ECS 的“系统”部分,但留作稍后练习。
我想获得有关如何将构造自定义对象的 Fn() 传递给 allocate()
方法的提示。我还没弄明白。
impl Storage for PositionStore {
fn allocate(&mut self, entity_id: usize) -> usize {
self.storage.push(Position {
entity_id,
position: 0.0,
});
self.storage.len() - 1
}
fn run(&self) {
self.storage.iter().for_each(|item| { println!("{}",item.position); });
}
}
impl Storage for VelocityStore {
fn allocate(&mut self, entity_id: usize) -> usize {
self.storage.push(Velocity {
entity_id,
velocity: 0.0,
});
self.storage.len() - 1
}
fn do_this(&self) {
self.storage.iter().for_each(|item| { println!("{}",item.velocity); });
}
}
一些样板。
impl Default for PositionStore {
fn default() -> PositionStore {
PositionStore {
storage: Vec::new(),
}
}
}
impl Default for VelocityStore {
fn default() -> VelocityStore {
VelocityStore {
storage: Vec::new(),
}
}
}
我觉得这个可以再考虑一下。它存储了组件的存储和它们的位置之间的关系
代替 T::default()
,您可能希望传递一个 lambda 函数,该函数对每个组件都有特定的初始化
impl Entities {
fn register<T>(&mut self) -> usize
where
T: Storage + Default + 'static,
{
self.containers.push(Box::new(T::default()));
self.containers.len() - 1
}
fn create<T>(&mut self, entity_id: usize, id: usize) -> usize {
self.containers[id].allocate(entity_id)
}
fn run_loop(&self) {
self.containers.iter().for_each(|x| x.do_this());
}
}
一个测试用例,看看这是否有效
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn arbitary() {
let mut entities = Entities {
entities: Vec::new(),
containers: Vec::new(),
};
let velocity_store_id = entities.register::<VelocityStore>();
let position_store_id = entities.register::<PositionStore>();
let _ = entities.create::<Velocity>(123, velocity_store_id);
let _ = entities.create::<Velocity>(234, velocity_store_id);
let _ = entities.create::<Position>(234, position_store_id);
let _ = entities.create::<Position>(567, position_store_id);
entities.run_loop();
}
}
关于rust - 具有返回 self 的特征的集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55578125/