hyperion_ec_no_s 1.0.2

A dense ECS without the S
Documentation
# Hyperion


## A dense ECS without the S.


\
**Named after:**
> The eighth-largest moon of Saturn.
> It is distinguished by its highly *irregular* shape, *chaotic* rotation,
> low density (lol), and its unusual sponge-like appearance.
> -- Wikipedia

## Quick things:


- No systems
- No queries
- No archetypes
- Simply component storage managers and a world that handles it with generational ID handles
- Use the command buffer to queue up changes you set up in your loops so they don't crash and burn.
- A resource registry to store things like textures, sounds etc. in the world for easy access via handles.
- Unique resources in the resource registry accessible just via their types.

## Why:


I wanted a way to easily move around data for rust game dev and didn't want to manually create vectors for new structs
everytime.
This allows me to have a world in my gamestate that manages it for me and gives me access to the data everywhere.
\
I did not want a full ECS, go use hecs or something else if you need systems/queries and speed for accessing smaller
lego block components.

## Test Speed:


Done using the test: 'large_scale_component_iteration_speed_test_separate_timings'
\
Ran using: 'cargo test --release large_scale_component_iteration_speed_test_separate_timings -- --nocapture'
\
1.5mill entities tested - Laptop with an AMD Ryzen AI 7 350.

    Loop 1: get_all_components_mut time = 13.9909ms, get_component_mut per entity time = 2.07895s
    Loop 2: get_all_components_mut time = 13.8826ms, get_component_mut per entity time = 1.9981674s
    Loop 3: get_all_components_mut time = 15.2029ms, get_component_mut per entity time = 2.0337959s
    Loop 4: get_all_components_mut time = 14.5411ms, get_component_mut per entity time = 1.7639305s
    Loop 5: get_all_components_mut time = 14.0716ms, get_component_mut per entity time = 1.762936s
    Average time (get_all_components_mut): 14.33782ms
    Average time (get_component_mut per entity): 1.92755596s

On the same laptop in release mode version of the bunny mark linked below I get around 240,000 bunnies at 60fps with
Y sorting and 270_000ish without.
\
Please note that the laptop gpu is very bad and just commenting out the texture draw call (not the loop)
lets it hit over 2.5 million bunnies still at 60fps.

## Docs


https://docs.rs/hyperion_ec_no_s/latest/hyperion/

## Examples:


Tetra bunny mark: https://gitlab.com/thatloganguy/hyperion_bunnymark

### Basic usage:


```rust
use hyperion::prelude::*;

// Component can be derived or impl
#[derive(Component)]

struct Position(u32, u32);

#[derive(Component)]

struct Health(i32);

fn main() {
    let mut world = World::new();

    // Create an entity and give it some components
    let e = world.create_entity();
    world.add_component(e, Position(1, 2));
    world.add_component(e, Health(100));

    // Read mut
    if let Some(pos) = world.get_component_mut::<Position>(e) {
        pos.0 += 10; // mutate in place
    }
    // Non mut
    if let Some(hp) = world.get_component::<Health>(e) {
        println!("entity health = {}", hp.0);
    }

    // Get the owning Entity for a Position component reference
    // I know this example is dumb but it's just show how it works
    if let Some(pos_ref) = world.get_component::<Position>(e) {
        if let Some(owner) = world.get_entity_for::<Position>(pos_ref) {
            println!("owner of pos is {:?}", owner);
        }
    }

    // Iterate over all Position components
    for p in world.get_all_components_mut::<Position>() {
        p.0 += 1;
        p.1 += 1;
    }

    // Get all entities that have a Position component
    let entities_with_position: Vec<_> = world.get_entities_for::<Position>();

    for entity in entities_with_position {
        if let Some(pos) = world.get_component::<Position>(entity) {
            println!("entity {:?} has Position({}, {})", entity, pos.0, pos.1);
        }
    }

    // Note: This is not a query/system. This is just a quicker way to get entities with multiple components.
    // It is not trying to be like other ECS and only exists for qol. There are no archetypes etc.
    // Get all entities that have both Position and Health,
    let entities_with_pos_and_health = world.get_entities_and_components_with_2::<Position, Health>();
    for (entity, pos, hp) in entities_with_pos_and_health {
        println!(
            "entity {:?}: Position({}, {}), Health({})",
            entity, pos.0, pos.1, hp.0
        );
    }

    // Despawn the entity (removes its components too)
    world.destroy_entity(e);
    // Reset the world.
    world.purge();
}
```

### Command Buffer:


```rust
use hyperion::prelude::*;

#[derive(Component)]

struct Position(u32, u32);

fn main() {
    let mut world = World::new();
    let mut commands = CommandBuffer::default();

    // Queue: create 100 entities, each with a Position component
    // You can also just queue raw entities if you want for some reason with create_entity
    for _ in 0..100 {
        commands.create_and_add_component(Position(0, 0));
    }

    // Apply queued creates; get back the Entity IDs that were created - this includes ones without components
    let entities = commands.execute(&mut world);

    // Queue: destroy all of them
    for e in entities {
        commands.destroy_entity(e);
    }

    // Apply queued destroys
    commands.execute(&mut world);
}
```

### Resource Registry:


```rust
use hyperion::prelude::*;

#[derive(Debug)]

struct Texture {
    id: u32,
}

#[derive(Debug)]

struct Config {
    max_textures: usize,
    app_name: String,
}

fn main() {
    let mut world = World::new();

    // Make some resources and store the handles (generational IDs like entities)
    let mut handles = Vec::new();
    for i in 0..100 {
        let texture = Texture { id: i };
        let handle = world.register_resource::<Texture>(texture);
        handles.push(handle);
    }

    // Easy to get
    for handle in &handles[0..5] {
        if let Some(tex) = world.get_resource::<Texture>(*handle) {
            println!("Got texture with id: {}", tex.id);
        }
    }

    // Easy to change
    if let Some(tex) = world.get_resource_mut::<Texture>(handles[0]) {
        tex.id = 999;
    }

    // Simple to remove, wow.
    for handle in handles {
        let removed = world.remove_resource::<Texture>(handle);
        if let Some(tex) = removed {
            println!("Removed texture with id: {}", tex.id);
        }
    }

    // Register a unique Config resource - only one of a type can be registered.
    // Reregistering will replace it. This actually is the same for components on entities.
    world.register_unique(Config {
        max_textures: 100,
        app_name: "HyperionApp".to_string(),
    });

    // Wow so easy to grab.
    if let Some(config) = world.get_unique::<Config>() {
        println!("Unique Config: max_textures = {}, app_name = {}", config.max_textures, config.app_name);
    }

    // Don't forget mut
    if let Some(config) = world.get_unique_mut::<Config>() {
        config.max_textures = 200;
        config.app_name = "UpdatedHyperionApp".to_string();
    }

    // It's changed crazy.
    if let Some(config) = world.get_unique::<Config>() {
        println!("Updated Unique Config: max_textures = {}, app_name = {}", config.max_textures, config.app_name);
    }

    // Kill it. Gets it back just in case you need it.
    if let Some(removed_config) = world.remove_unique::<Config>() {
        println!("Removed unique Config: {:?}", removed_config);
    }

    // Yeah it's gone.
    assert!(world.get_unique::<Config>().is_none());
    // Purge it all cause why not.
    world.purge();
}
```

### Final notes


AI was used for most of the comments and generating tests because normally I don't write them. So if things aren't quite
right please blame the future overlords.