10个不稳定版本 (3个破坏性更改)

0.4.0 2021年2月25日
0.3.1 2020年9月28日
0.2.4 2020年6月19日
0.2.1 2019年12月5日
0.1.1 2019年3月9日

#203 in 游戏开发

Download history 681/week @ 2024-03-13 861/week @ 2024-03-20 749/week @ 2024-03-27 922/week @ 2024-04-03 818/week @ 2024-04-10 792/week @ 2024-04-17 779/week @ 2024-04-24 700/week @ 2024-05-01 792/week @ 2024-05-08 804/week @ 2024-05-15 883/week @ 2024-05-22 842/week @ 2024-05-29 696/week @ 2024-06-05 603/week @ 2024-06-12 678/week @ 2024-06-19 622/week @ 2024-06-26

每月下载量2,722
25 个包 中使用 (16 个直接使用)

MIT 许可证

1MB
11K SLoC

Legion ECS

Build Status Crates.io Docs.rs

Legion旨在为Rust游戏项目提供一个功能丰富的高性能实体组件系统(ECS)库,具有最少的样板代码。

入门指南

世界

世界 是一组 实体 的集合,其中每个实体可以附加任意数量的 组件

use legion::*;
let world = World::default();

实体可以通过 push(用于单个实体)或 extend(用于具有相同组件类型的实体集合)来插入。世界将为每个插入的实体创建一个唯一的ID,以便您可以稍后引用该实体。

// a component is any type that is 'static, sized, send and sync
#[derive(Clone, Copy, Debug, PartialEq)]
struct Position {
    x: f32,
    y: f32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
struct Velocity {
    dx: f32,
    dy: f32,
}

// push a component tuple into the world to create an entity
let entity: Entity = world.push((Position { x: 0.0, y: 0.0 }, Velocity { dx: 0.0, dy: 0.0 }));

// or extend via an IntoIterator of tuples to add many at once (this is faster)
let entities: &[Entity] = world.extend(vec![
    (Position { x: 0.0, y: 0.0 }, Velocity { dx: 0.0, dy: 0.0 }),
    (Position { x: 1.0, y: 1.0 }, Velocity { dx: 0.0, dy: 0.0 }),
    (Position { x: 2.0, y: 2.0 }, Velocity { dx: 0.0, dy: 0.0 }),
]);

您可以通过 entries 访问实体。条目允许您查询实体附加了哪些类型的组件,获取组件引用,或添加和删除组件。

// entries return `None` if the entity does not exist
if let Some(mut entry) = world.entry(entity) {
    // access information about the entity's archetype
    println!("{:?} has {:?}", entity, entry.archetype().layout().component_types());

    // add an extra component
    entry.add_component(12f32);

    // access the entity's components, returns `None` if the entity does not have the component
    assert_eq!(entry.get_component::<f32>().unwrap(), &12f32);
}

查询

条目不是搜索或批量访问世界的最方便或最高效的方式。 查询 允许以高性能和表达性强的方式遍历世界中的实体。

// you define a query be declaring what components you want to find, and how you will access them
let mut query = <&Position>::query();

// you can then iterate through the components found in the world
for position in query.iter(&world) {
    println!("{:?}", position);
}

您可以根据一组组件搜索实体。

// construct a query from a "view tuple"
let mut query = <(&Velocity, &mut Position)>::query();

// this time we have &Velocity and &mut Position
for (velocity, position) in query.iter_mut(&mut world) {
    position.x += velocity.x;
    position.y += velocity.y;
}

您可以在基本查询上添加额外的过滤器。例如,您可以选择排除包含特定组件的实体,或者只包括在查询运行以来某个组件已更改的实体(此过滤是保守的,粒度较粗)。

// you can use boolean expressions when adding filters
let mut query = <(&Velocity, &mut Position)>::query()
    .filter(!component::<Ignore>() & maybe_changed::<Position>());

for (velocity, position) in query.iter_mut(&mut world) {
    position.x += velocity.dx;
    position.y += velocity.dy;
}

查询还有更多功能。有关更多信息,请参阅模块文档

系统

你可能已经注意到,当我们想要写入一个组件时,我们需要使用 iter_mut 来遍历我们的查询。但是,也许你的应用程序希望能够在不同的实体上处理不同的组件,甚至可能是同时并行处理?虽然手动操作是可能的(参见 World::split),但是当应用程序的不同部分不知道彼此需要什么组件,或者可能有或可能没有冲突的访问需求时,这样做是非常困难的。

系统调度器 自动化了这个过程,甚至可以在比手动操作更细粒度的层面上进行调度。系统是工作单元。每个系统都被定义为一个函数,它提供了访问查询和共享 资源 的权限。然后,可以将这些系统添加到调度器中,调度器是系统的线性序列,按照观察副作用(如写入组件)的时间顺序排列。调度器将自动并行执行所有系统,同时保持每个系统从其自身视角看来的执行顺序。

// a system fn which loops through Position and Velocity components, and reads the Time shared resource
// the #[system] macro generates a fn called update_positions_system() which will construct our system
#[system(for_each)]
fn update_positions(pos: &mut Position, vel: &Velocity, #[resource] time: &Time) {
    pos.x += vel.dx * time.elapsed_seconds;
    pos.y += vel.dy * time.elapsed_seconds;
}

// construct a schedule (you should do this on init)
let mut schedule = Schedule::builder()
    .add_system(update_positions_system())
    .build();

// run our schedule (you should do this each update)
schedule.execute(&mut world, &mut resources);

有关更多信息,请参阅 系统模块系统过程宏

特性标志

Legion 提供了一些特性标志

  • parallel - 通过 rayon 库启用并行迭代器和并行调度执行。默认启用。
  • extended-tuple-impls - 将视图和组件元组的最大大小从 8 扩展到 24,但会增加编译时间。默认关闭。
  • serialize - 启用 serde 序列化模块和相关功能。默认启用。
  • crossbeam-events - 实现了 crossbeam Sender 通道的 EventSender 特性,允许它们用于事件订阅。默认启用。

WASM

Legion 默认启用并行性,但目前 Web Assembly 不支持并行,因为它以单线程运行。因此,为了为 WASM 编译,请确保在 Cargo.toml 中设置 default-features = false。此外,如果你想要使用 serialize 特性,你必须启用 stdwebwasm-bindgen 特性,这些特性将通过 uuid 包进行代理。有关更多信息,请参阅 uuid 包

legion = { version = "*", default-features = false, features = ["wasm-bindgen"] }

依赖项

~1.6–3MB
~59K SLoC