#ecs #component #bundle #query #gamedev #tiny #i32

trecs

一个小巧且易于使用的ecs库

4个版本

0.1.3 2023年7月31日
0.1.2 2023年6月30日
0.1.1 2023年6月28日
0.1.0 2023年6月27日

#331 in 游戏开发

34 每月下载量

自定义许可证

69KB
1.5K SLoC

一个小巧的实体-组件-系统库

  • 更少的依赖项

  • 快速构建

  • 易于使用

Hello World

// a function-system
fn hello_world() {
    println!("Hello World")
}

fn main() {
    // create a world
    let mut world = tecs::World::new();
    world
    // add "hello world" system into world's start_up_systems
    // startup_systems will only run once
    .add_startup_system(hello_world)
    // make the loop run once only
    .run_until(|| true);
}

将组件添加到世界

您可以使用 spawn 方法将实现了 BundleComponent 特性的任何类型添加到世界中

use tecs::tools::Command;

let mut world = tecs::World::new();
world.spawn(12345);
world.spawn("abcde");

您可以轻松推导 BundleComponent 特性

Component 只是一个标记,它可以用于任何类型

use tecs::bundle::Component;

#[derive(Component)]
struct MyComponent{
    name : String,
}

Bundle 的所有字段都应该实现 Component

use tecs::bundle::Bundle;

#[derive(Bundle)]
struct MyBundle{
    inner : MyComponent,
}

Component 默认以下类型实现

  • usize,u8,u16,u32,u64
  • isize,i8,i16,i32,i64
  • (),bool,&'static str

Bundle 默认为所有只包含实现了 Component 特性的类型的元组实现

例如

  • (usize,&str)

在世界上查询组件

直接使用 Query

use tecs::world::Query;
use tecs::tool::Command;

let mut world = tecs::World::new();
// add two `Bundle` into world     
world.spawn(12345);
world.spawn((54321,"abcde"));

// create a `Query` to get all i32'a refence
let query = Query::<&i32>::new(&mut world);
for item in query {
    println!("{item}")
}
// this code will print: 
// 12345
// 54321

或使用系统

use tecs::world::{Query,Commands};
use tecs::tool::Command;

// use command to do spawn
fn do_spawn(mut commands: Commands){
    commands.spawn(12345);
    commands.spawn((54321,"abcde"));
}

// note: you cant use conflict Query in one system, or the program will panic when you add system into world
// a example of conflict Query: Query<&i32> and Query<&mut i32>
fn print_all_i23(mut query : Query<&i32>){
    for item in query {
        println!("{item}")
    }   
}



world.add_startup_system(do_spawn);
world.add_startup_system(print_all_i23);
world.run_until(||true);

Query 是一个接收两个泛型的类型

pub struct Query<'a, F: WorldFetch, Q: WorldFilter = ()> {...}

WorldFetch 用于在世界上获取组件

它可以是组件的(不可变/可变)引用,或者只包含 WorldFetch 的元组

WorldFilter 用于在世界上获取 Bundle

它可以是

  • All 或 All<(Component1,Component1,...)> 以过滤包含所有组件的 Bundle

  • OneOf 或 OneOf<(Component1,Component1,...)> 以过滤包含至少一个组件的 Bundle

  • Not 或 Not<(Component1,Component1,...)> 以过滤不包含任何组件的 Bundle

示例

  • Query<&i32> 将查询包含 i32 组件的所有组件,并在迭代器中提供 i32 的不可变引用

  • 使用 Query<&mut i32> 使迭代器提供 i32 的可变引用

  • 使用 Query<&i32,All<&str>> 将查询包含 i32&str 的所有组件,并在迭代器中提供 i32 的不可变引用

  • 使用 Query<&i32,AnyOf<(&str,MyComponent)>> 将查询包含 i32&strMyComponent 的所有组件,并在迭代器中提供 i32 的不可变引用

  • 使用 Query<&i32,All<&str>> 将查询包含 i32 且不包含 &str 的所有组件,并在迭代器中提供 i32 的不可变引用

迭代器

可以使用 for 循环获取查询结果,fetch 的类型为 WorldFetch

for fetch in query {}

可以使用 for 循环和 .into_iter() 方法获取查询结果,以及结果的 Entity

for e in query.into_eiter() {}

e 的类型为 EBundle

pub struct EBundle<'a, F: WorldFetch> {...}

可以通过解引用 e 获取查询结果,使用 .entity() 方法获取结果的 Entity

Entity 可用于移除已查询的包

commands.remove(b.entity());

资源

资源按类型存储在世界类型中

use tecs::tools::ResManager;

let mut world = tecs::World::new();
// get resources usize, and init it to 1
world.get_res<usize>().get_or_init(||1);
assert_eq!(*world.get_res<usize>().get().unwrap(),1);

与系统

功能:系统

此功能非常有用

默认启用此功能

此功能允许在世界中运行系统以访问资源和组件

本版本仅支持功能系统

所有实现 SystemParm 特质的类型都可以作为功能系统的参数

以下类型实现了 SystemParm 特质

您可以在 tecs::world 模块中找到它们

类型 用法 注意
Res 在世界中获取类型为 T 的资源 一个系统中不能使用相同的 Res
Resources 在世界中获取任何类型的资源 不能与一个系统中的任何 Res 一起使用
Query 查询世界中的组件 一个系统中不能使用冲突查询,如 Query<&T> 和 Query<&mut T>
命令 向世界中添加和删除包 使用 spawn_many() 方法快速创建具有相同类型的多个包

要运行系统,您需要使用 .add_system() 方法或 .add_startup_system() 方法首先将系统添加到世界中

  • 所有启动系统都只运行一次
  • 系统运行预循环

要在世界中运行系统,您可以使用

  • 使用 .startup() 方法运行所有启动系统

  • 使用 .run_once() 方法一次性运行所有系统(不包括启动系统)

  • 使用 .run() 方法多次运行所有系统,该方法不会返回

  • 使用 .run_until(f) 方法多次运行所有系统,当 f 返回 true 时循环将中断

特性:异步

默认情况下此特性是禁用的

此特性允许你在世界中运行异步函数系统

此特性使得你只能将异步函数系统添加到世界中,原因如下

note: upstream crates may add a new impl of trait `std::future::Future` for type `()` in future versions

‘.startup’、‘.run_once’、‘.run’、‘.run_until’ 方法变为异步函数

依赖关系

约300-740KB
约18K SLoC