19个稳定版本
1.11.1 | 2023年7月11日 |
---|---|
1.10.3 | 2023年2月16日 |
1.7.0-rc2 | 2022年12月25日 |
1.7.0-rc1 | 2022年10月26日 |
0.2.1 |
|
241 在 数据结构 中
每月下载量 105
65KB
1.5K SLoC
entity_data
实体组件数据的容器。
用例
假设你需要存储大量的对象,每个对象可以具有多个不同的字段。但你不想使用Rust的动态调度功能,原因如下:
- 虚拟调度会引入间接引用。
- 你将不得不在堆上为每个对象存储某个位置。这会导致缓存未命中,从而导致对象迭代速度变慢。
面向数据的方法有助于解决这些问题。
架构
一个 实体 是一个对象的标识符。每个实体可以与多个组件(“字段”)相关联。存储本身基于 ECS 技术。
一组独特的组件称为 Archetype
。一个archetype为它的每个组件类型维护一个连续的向量。
示例
简单使用
use entity_data::{EntityStorage, Archetype};
struct Barks {
bark_sound: String,
}
impl Barks {
fn bark(&self) {
println!("{}", self.bark_sound);
}
}
struct Eats {
favorite_food: String,
eaten_food: Vec<String>,
}
impl Eats {
fn eat(&mut self, food: String) {
self.eaten_food.push(food);
}
}
struct Animal {
weight: f32,
habitat: String,
}
#[derive(Archetype)]
struct Dog {
animal: Animal,
barks: Barks,
eats: Eats,
}
#[derive(Archetype)]
struct Bird(Animal, Eats);
fn main() {
let mut storage = EntityStorage::new();
let super_dog = storage.add_entity(Dog {
animal: Animal { weight: 30.0, habitat: "forest".to_string(), },
barks: Barks { bark_sound: "bark.ogg".to_string(), },
eats: Eats { favorite_food: "meat".to_string(), eaten_food: vec![] },
});
let hummingbird = storage.add_entity(Bird(
Animal { weight: 5.0, habitat: "gardens".to_string() },
Eats { favorite_food: "apples".to_string(), eaten_food: vec![] }
));
let super_dog_barks = storage.get::<Barks>(&super_dog).unwrap();
super_dog_barks.bark();
let super_dog_eats = storage.get_mut::<Eats>(&super_dog).unwrap();
super_dog_eats.favorite_food = "beans".to_string();
let hummingbird_eats = storage.get_mut::<Eats>(&hummingbird).unwrap();
hummingbird_eats.eat("seeds".to_string());
}
同时处理多个组件
use entity_data::{EntityId, EntityStorage, System, SystemHandler};
use entity_data::system::SystemAccess;
use macros::Archetype;
#[derive(Default, Debug)]
struct Position {
x: f32,
y: f32,
}
#[derive(Debug)]
struct Name(String);
#[derive(Archetype)]
struct Dog {
pos: Position,
name: Name,
}
struct PositionsPrintSystem {}
#[derive(Default)]
struct ConcatAllNamesSystem {
result: String
}
impl SystemHandler for PositionsPrintSystem {
fn run(&mut self, data: SystemAccess) {
let positions = data.component::<Position>();
let names = data.component::<Name>();
for (pos, name) in positions.iter().zip(names) {
println!("{:?} - {:?}", pos, name);
}
}
}
impl SystemHandler for ConcatAllNamesSystem {
fn run(&mut self, data: SystemAccess) {
let names = data.component::<Name>();
for name in names {
self.result += &name.0;
}
}
}
fn main() {
let mut storage = EntityStorage::new();
let dog0 = storage.add(Dog {
pos: Default::default(),
name: Name("Bobby".to_owned())
});
let dog1 = storage.add(Dog {
pos: Position { x: 3.0, y: 5.0 },
name: Name("Jet".to_owned())
});
let mut positions_print_system = PositionsPrintSystem {};
let mut concat_names_system = ConcatAllNamesSystem::default();
let mut sys0 = System::new(&mut positions_print_system)
.with::<Position>().with::<Name>();
let mut sys1 = System::new(&mut concat_names_system)
.with::<Name>();
// or storage.dispatch_par() to run systems in parallel (requires `rayon` feature to be enabled).
storage.dispatch(&mut [sys0, sys1]);
println!("{}", concat_names_system.result);
}
依赖关系
~1–2MB
~36K SLoC