necs 0.2.0

An ECS implementation, primarily serving as a learning project
Documentation
# necs

![crates.io badge](https://img.shields.io/crates/v/necs)

`necs` is a simple Entity Component System (ECS) written in Rust. It is designed primarily for learning purposes to understand how ECS architectures work under the hood.

It provides two distinct storage implementations:
1. **Default**: A simple Struct of Arrays (SoA) storage where components are stored in sparse vectors indexed by Entity ID.
2. **Archetypes**: An optional implementation where entities with the same set of components are grouped together in memory for better data locality.

## Features

*   **Generational Entities**: Entity IDs are recycled with a generation counter
*   **Type-Erased Storage**: Components can be any `'static` type
*   **Flexible Querying**: Query all components of a specific type mutably or immutably
*   **Archetype-Based Storage (Optional)**: Group entities by component composition for cache efficiency
*   **Bundle API**: Create entities with multiple components at once or add/remove sets of components atomically
*   **No Systems**: There are no built-in "Systems" or schedulers. You simply query the `World` and iterate over data to implement your logic

## Installation

Add `necs` to your `Cargo.toml`.

```toml
[dependencies]
necs = "0.2.0" # Adjust path or git url as necessary
```

To enable the archetype-based storage:

```toml
[dependencies]
necs = { version = "0.2.0", features = ["archetypes"] }
```

## Usage

### Basic World

The default `World` allows you to spawn entities and attach components freely.

```rust
use necs::{World, Component};

// 1. Define Components
#[derive(Debug, Clone, Copy, PartialEq)]
struct Position { x: f64, y: f64 }

#[derive(Debug, Clone, Copy, PartialEq)]
struct Velocity { x: f64, y: f64 }

fn main() {
    // 2. Initialize World
    let mut world = World::new();

    // 3. Spawn Entities
    let entity = world.spawn();

    // 4. Add Components
    world.add_component(&entity, Position { x: 0.0, y: 0.0 });
    world.add_component(&entity, Velocity { x: 1.0, y: 0.5 });

    // 5. Systems (Query and Iterate)
    // In necs, a "system" is just a loop over data.
    let mut velocities = world.query_component_mut::<Velocity>();
    for vel in velocities {
        vel.x += 0.1;
    }
    
    // Check results
    let vel = world.get_component::<Velocity>(&entity).unwrap();
    println!("New Velocity: {:?}", vel);
}
```

### Archetype World

If you enable the `archetypes` feature, you can use `ArchetypedWorld`. This moves entities into specific "Archetypes" (tables) based on their component signature. This makes adding/removing components slightly slower (due to moving memory), but iteration is cache-friendly.

```rust
use necs::{ArchetypedWorld, Component};

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

    let entity = world.spawn();
    
    // When you add components, the entity migrates to a new archetype automatically.
    world.add_component(&entity, 100u32); // Health
    world.add_component(&entity, "Player"); // Name
    
    // Queries work the same way
    let names = world.query_component::<&str>();
    assert_eq!(names[0], &"Player");
}
```

### Using Bundles

The `archetypes` feature also enables the `Bundle` API, allowing you to spawn entities with a predefined set of components or add multiple components in a single call. This is more efficient as it reduces the number of archetype migrations.

```rust
use necs::{ArchetypedWorld, Component};

#[derive(Debug, Clone, Copy, PartialEq)]
struct Health(f32);
#[derive(Debug, Clone, Copy, PartialEq)]
struct Position { x: f32, y: f32 }

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

    // Spawn an entity with multiple components at once
    let entity = world.spawn_with((
        Health(100.0),
        Position { x: 10.0, y: 20.0 }
    ));
    
    // Add multiple components to an existing entity
    // This performs a single migration
    world.add_components(&entity, (
        "Hero",
        true // IsAlive
    ));
    
    // Remove multiple components at once
    world.remove_components::<(Health, Position)>(&entity);
}
```

### Advanced Querying

The `archetypes` feature enables tuple-based queries, allowing you to iterate over entities that have *all* specific components.

```rust
use necs::{ArchetypedWorld, Component};

fn main() {
    let mut world = ArchetypedWorld::new();
    // ... spawn entities with Position and Velocity ...

    // Iterate over all entities with both Position and Velocity
    // This is highly optimized as it iterates over contiguous memory blocks
    world.query::<(Position, Velocity)>().for_each(|(pos, vel)| {
        println!("Entity at {:?} moving with {:?}", pos, vel);
    });
    
    // Mutable queries
    world.query_mut::<(Position, Velocity)>().for_each(|(pos, vel)| {
        pos.x += vel.x;
        pos.y += vel.y;
    });
}
```

## Disclaimer

`necs` is not intended to be a replacement for production-ready ECS frameworks like `bevy_ecs`, `specs`, or `hecs`. It lacks advanced features like:
*   Parallel system execution.
*   Complex query filters (e.g., `With`, `Without`, Joined queries).
*   Change detection.

It serves as a reference implementation for educational exploration.