Metadata-Version: 2.4
Name: pl-ecs
Version: 0.1.0
Summary: Peach's Lightweight Entity Component System
Author-email: Peach <james@christhall.us>
License: MIT
Keywords: ecs,entity-component-system
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: black>=23.0; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Dynamic: license-file

# PL-ECS
Peach's Lightweight Entity-Component-System

Low code, low dependency, simple implementation of ECS.

# Quick Start

```bash
pip install pl-ecs
```

## Entity
A unique identifier used to hold components together.

Entities are created and deleted via the World that they exist.

```python
ent = world.create_entity()
world.delete_entity(ent)
```

## Component
Pure data aggregated to an entity

Components included in a `components/` package at the root of the project are automatically imported to PL-ECS at start up. It is not a requirement that all Component definitions exist in one package but it is recommended as it makes the code cleaner and easier to manage. It is slighly more efficient as well since the penalty of dynamic class creation and registration are paid at initialization and not during run time mid-frame.

Components are created using the `@component_dataclass` decorator. The class declaration should be familiar as it is identical to python's dataclasses.

Example:
```python
from ecs import component_dataclass

@component_dataclass
class MyComponent:
    name: str = "me"
```

By creating a Component it becomes available to all Systems in the World without requiring a dependency on the module that created the class.

To use a Component in a system, just get it or create it.
```python
from ecs import Component

# Get the class
MyComponent = Component.get("MyComponent")
my_component = MyComponent(name="mine")

# Create the object
my_component = Component.create("MyComponent", name="mine")
```

## System
Logic that acts on components, creates new ones, modifies entities

Systems included in a `systems/` package at the root of the project are automatically imported to PL-ECS at start up. It is not a requirement that all Systems exist in one package but it is recommended as it makes the code cleaner and easier to manage. It also helps reduce the dependencies when the World is initialized and allows everything to be run/managed via strings in config files without requiring code changes.

It is best to keep Systems simple, do one thing and do it well.

To create a System create a subclass and provide an `update(self, dt, world)` method. If a custom `__init__()` is needed, it must have two keyword arguments, one for `name: str` another for `config: dict`.

```python
from dataclasses import dataclass
from ecs import World, System

@dataclass
class Config:
    my_data: str

class MySystem(System):

    def __init__(self, name: str, config: dict)
        """ Optional overide """

        # Validate config
        try:
            config = Config(**config)
        except Exception as e:
            raise ValueError(f"Invalid config")

        self._data = config.my_data

    def update(self, dt: float, world: World):
        for ent, comp in world.entities_with("MyComponent"):
            do_something(comp)
```

Once a System is created it can be instantiated by name without requiring dependencies outside of PL-ECS
```python
from ecs import System

my_config = {}
my_system = System(name="MySystem", config=my_config)
```

## World
Manager of the registered World. Executes each System every frame, in the order that they're registered, and maintains all Entities and their Components.

A `Frame` is a single pass through all added Systems. Systems are run in the order that they are added.

### Entity Management
Create an entity:
```python
entity = world.create_entity()
```

Delete an entity:
```python
world.delete_entity(ent)
```

### Component Management
Add Component to an entity:
```python
world.add_component(entity, component)
```

Get a Component for an entity:
```python
component = world.get_component(ent, "MyComponent")

# Returns None if component does not exist for entity
if component is None:
    do_nothing()
else:
    do_something(component)
```

Remove a Component from an entity:
```python
world.remove_component(ent, "MyComponent")
```

Query entities that match Component signatures:
```python
# Query returns a tuple of all entities and the components in the request
for ent, my_comp in world.entities_with("MyComponent"):
    do_something(my_comp)
```

### System Management

Add a system to the world:
```python
world.add_system(System(name="MySystem"))
```

Remove a system from the world:
```python
world.remove_system("MySystem")
```

Run a frame:
```python
world.update(dt=1.0)
```
