Giter VIP home page Giter VIP logo

flax's Introduction

GitHub Workflow Status Crates Docs Codecov Guide

Flax

Flax is a performant and ergonomic Entity Component System.

The world is organized by simple identifiers known as an Entity, which can have any number of components attached to them.

Systems operate on the world's entities and provide the application logic.

Consider reading the User Guide

Features

See a live demo of asteroids using wasm here.

Source

Example Usage

  // Declare static components
  use flax::*;

  component! {
    health: f32,
    regen: f32,
    pos: (f32, f32),
    player: (),
    items: Vec<String>,
  }

  let mut world = World::new();

  // Spawn an entity
  let p = EntityBuilder::new()
      .set(health(), 50.0)
      .tag(player())
      .set(pos(), (0.0, 0.0))
      .set(regen(), 1.0)
      .set_default(items())
      .spawn(&mut world);

  let mut query = Query::new((health().as_mut(), regen()));

  // Apply health regeneration for all matched entites
  for (health, regen) in &mut query.borrow(&world) {
      *health = (*health + regen).min(100.0);
  }

Systems

Queries with logic can be abstracted into a system, and multiple systems can be collected into a schedule.

let regen_system = System::builder()
    .with_query(Query::new((health().as_mut(), regen())))
    .for_each(|(health, regen)| {
        *health = (*health + regen).min(100.0);
    })
    .boxed();

let despawn_system = System::builder()
    .with_query(Query::new(entity_ids()).filter(health().le(0.0)))
    .with_cmd_mut()
    .build(|mut q: QueryBorrow<EntityIds, _>, cmd: &mut CommandBuffer| {
        for id in &mut q {
            cmd.despawn(id);
        }
    })
    .boxed();

let mut schedule = Schedule::from([regen_system, despawn_system]);

schedule.execute_par(&mut world)?;

Relations

Flax provides first class many-many relations between entities, which is useful for tree scene hierarchies, graphs, and physics joints between entities.

Relations can be both state-less or have associated data, like spring or joint strengths.

Relations are cache friendly and querying children of does not require random access. In addition, relations are cleaned up on despawns and are stable during serialization, even if the entity ids migrate due to collisions.

See the guide for more details.

component! {
    child_of(parent): () => [ Debuggable ],
}

let mut world = World::new();

let parent = Entity::builder()
    .set(name(), "Parent".into())
    .spawn(&mut world);

let child1 = Entity::builder()
    .set(name(), "Child1".into())
    .set_default(child_of(parent))
    .spawn(&mut world);

Comparison to other ECS

Compared to other ECS implementations, a component is simply another Entity identifier to which data is attached. This means the same "type" can be added to an entity multiple times.

A limitation of existing implementations such as specs, planck, or hecs is that newtype wrappers need to be created to allow components of the same inner type to coexist.

This leads to having to forward all trait implementations trough e.g derive-more or dereferencing the newtypes during usage.

By making components separate from the type the components can work together without deref or newtype construction.

component! {
    velocity: Vec3,
    position: Vec3,
}

let vel = world.get(entity, velocity())?;
let mut pos = world.get_mut(entity, position())?;
let dt = 0.1;

*pos += *vel * dt;

On a further note, since the components have to be declared beforehand, it limits the amount of types which can be inserted as components. This fixes subtle bugs which come by having the type dictate the component, such as using an Arc<Type> instead of just Type, which leads to subsequent systems not finding the Type on the entity.

Using statically declared components makes the rust type system disallow these cases and catches these bugs earlier.

Motivation

During development of a game in school I used the hecs ECS. It is an awesome library, and the author Ralith has been wonderful in accepting contributions and inquiries.

Despite this, I often made subtle bugs with similar types. The game engine was cluttered with gigantic newtypes for Velocity, Position with many deref coercions in order to coexist.

Unsafe

This library makes use of unsafe for type erasure and the allocation in storage of ComponentBuffers and Archetypes.

License: MIT

flax's People

Contributors

ten3roberts avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.