A performant, zero-dependency ECS library with a nice API written in Rust.
```toml
[dependecies] kiwi-ecs = "1.3" ```
rust
// lib.rs
use kiwi_ecs::*;
To start, create a new World
. This is the starting point of the ecs.
The program can have multiple independent worlds.
rust
pub fn main() {
let mut world = World::new();
}
Components are defined as follows:
```rust
struct Position { x: u32, y: u32 } ```
Unit structs can't be used as Components, this is where you would have to use a flag. Flags are represented as an enum:
```rust
enum Flags { Player, Enemy, Ground, } ```
To spawn a new entity with the given components:
rust
// spawn_entity macro accepts the world as the first parameter, and the
// components to add to the entity as the other parameters
let entity_id = spawn_entity!(world, Position { x: 0, y: 0 });
You can give an entity a flag using the set_flag
method:
rust
world.set_flag(entity_id, Flags::Player);
There are two ways to define systems.
system
macro:```rust // immutable system
fn print_positions(world: &World) { println!("{:?}", pos); }
// mutable system
fn move_entities(world: &mut World) { pos.x += vel.x; pos.y += vel.y }
// query entity ids as well
/// prints all entities ids having the position component fn printentityids(world: &World) { println!("{id}"); }
pub fn main() { let mut world = World::new();
//--snip
// Call the systems printpositions(&world); moveentities(&mut world); printentityids(&world); } ```
To create a mutable system, the function should contain world: &mut World
as its first argument,
for an immutable one, add world: &World
.
The function can contain any number of arguments you can pass to it when calling.
The function can return any type of Result<(), Any>
. If this function has the given result
return type, Ok(())
will be returned at the end of the system.
query
and query_mut
macros:```rust pub fn main() { let mut world = World::new();
//--snip
let queryresult = query!(world, Position); let queryresult = querymut!(world, Position, Velocity); let queryresult = query!(world, EntityId, Position);
// You can now loop over the components queryresult.foreach(|components| { // ... }); } ```
You can further filter queries using flags:
```rust
fn onplayer(world: &World) { if world.hasflag(id, Flags::Player) { // ... } }
let queryresult = query!(world, EntityId, Position) .filter(|(id, _pos)| world.hasflag(*id, Flags::Player)); ```
The try feature of this crate enables returning early from a system.
To enable it:
```toml
[dependencies] kiwi-ecs = { version = "*", features = ["try"] } ```
Mark a system which returns a Result
of ok type ()
with try
:
```rust
fn asystemwithafalliblecondition(world: &World) -> Result<(), String> { let mesh = getmesh(id)?; // fallible function render(mesh, pos)?; } ```
Next to returning an Err
, you can also use
return std::ops::ControlFlow::Continue(())
to skip the current entity
and continue to the next or return std::ops::ControlFlow::Break(())
to
break from the function without an error.
Contributors are always welcome. If you find any bugs, feel free to open an issue. If you feel like it, PRs are also appreciated!
Licensed under the MIT license.