### Basic Entity-Component-System Setup and Processing Source: https://github.com/benmoran56/esper/blob/master/_autodocs/INDEX.md Demonstrates the fundamental setup of an ECS world using Esper, including defining components, creating entities, implementing a processor, and running the game loop. This is useful for getting started with Esper. ```python import esper from dataclasses import dataclass # Define components @dataclass class Position: x: float = 0.0 y: float = 0.0 @dataclass class Velocity: x: float = 0.0 y: float = 0.0 # Create entity with components player = esper.create_entity(Position(10, 20), Velocity(1, 0)) # Define processor class MovementProcessor(esper.Processor): def process(self, dt): for ent, (vel, pos) in esper.get_components(Velocity, Position): pos.x += vel.x * dt pos.y += vel.y * dt # Register and run esper.add_processor(MovementProcessor()) esper.process(dt=0.016) # Query and modify if health := esper.try_component(player, Health): health.hp -= 10 ``` -------------------------------- ### Install Esper from source Source: https://github.com/benmoran56/esper/blob/master/README.md Install Esper directly from the source directory using pip. This is useful for development or when not using a package manager. ```bash pip install . --user ``` -------------------------------- ### Minimal Esper ECS Example Source: https://github.com/benmoran56/esper/blob/master/_autodocs/README.md A basic example demonstrating entity creation, component addition, processor definition, and world processing. This serves as a starting point for understanding Esper's core concepts. ```python import esper from dataclasses import dataclass @dataclass class Position: x: float = 0.0 y: float = 0.0 class MyProcessor(esper.Processor): def process(self): for ent, pos in esper.get_component(Position): print(f"Entity {ent} at {pos.x}, {pos.y}") entity = esper.create_entity(Position(10, 20)) esper.add_processor(MyProcessor()) esper.process() ``` -------------------------------- ### Minimal Esper Example Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md A minimal example demonstrating the core concepts of Esper: defining components, processors, creating entities, and running a basic game loop. ```python import esper from dataclasses import dataclass # 1. Define components (data holders) @dataclass class Position: x: float = 0.0 y: float = 0.0 @dataclass class Velocity: x: float = 0.0 y: float = 0.0 # 2. Define processor (game logic) class MovementProcessor(esper.Processor): def process(self, dt): for entity_id, (vel, pos) in esper.get_components(Velocity, Position): pos.x += vel.x * dt pos.y += vel.y * dt # 3. Create entities player = esper.create_entity(Position(0, 0), Velocity(5, 0)) enemy = esper.create_entity(Position(100, 100), Velocity(-2, 0)) # 4. Register processor and run game loop esper.add_processor(MovementProcessor()) # Game loop for frame in range(10): esper.process(dt=0.016) pos = esper.component_for_entity(player, Position) print(f"Player at ({pos.x:.1f}, {pos.y:.1f})") ``` -------------------------------- ### MovementProcessor Example Implementation Source: https://github.com/benmoran56/esper/blob/master/_autodocs/04-processor-management.md Demonstrates how to implement a custom processor by inheriting from esper.Processor and implementing the process method. This example moves entities based on their Velocity and Position components. ```python import esper from dataclasses import dataclass @dataclass class Position: x: float y: float @dataclass class Velocity: x: float y: float class MovementProcessor(esper.Processor): """Moves entities with Velocity and Position components.""" def process(self, dt): # dt is passed from esper.process(dt) for entity_id, (vel, pos) in esper.get_components(Velocity, Position): pos.x += vel.x * dt pos.y += vel.y * dt # Usage processor = MovementProcessor() esper.add_processor(processor, priority=2) # In main loop esper.process(dt=0.016) # dt passed to all processors ``` -------------------------------- ### Install Esper using pip Source: https://github.com/benmoran56/esper/blob/master/README.md Install the Esper package from PyPi using pip. This command ensures you have the latest version. ```bash pip install --user --upgrade esper ``` -------------------------------- ### Main Game Loop Setup Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md Initializes the Esper world, adds processors, creates an initial player entity, and runs the main game loop, processing delta time and checking for entity existence. ```python import esper import time from processors import MovementProcessor, RenderProcessor def main(): esper.add_processor(MovementProcessor(), priority=2) esper.add_processor(RenderProcessor(), priority=0) player = esper.create_entity(Position(100, 100), Velocity(10, 0)) running = True last_time = time.time() while running: current_time = time.time() dt = current_time - last_time last_time = current_time esper.process(dt) # Check for exit condition if not esper.entity_exists(player): running = False time.sleep(0.001) if __name__ == '__main__': main() ``` -------------------------------- ### Esper Core API Usage Example Source: https://github.com/benmoran56/esper/blob/master/_autodocs/README.md Demonstrates the most common operations in esper, including entity and component management, querying, processors, events, and world switching. This snippet is useful for a quick overview of the framework's capabilities. ```Python import esper from dataclasses import dataclass @dataclass class Position: x: float = 0.0 y: float = 0.0 @dataclass class Velocity: x: float = 0.0 y: float = 0.0 # Entities entity = esper.create_entity(Position(10, 20), Velocity(1, 0)) esper.delete_entity(entity) # Components esper.add_component(entity, Velocity(2, 0)) vel = esper.try_component(entity, Velocity) # Queries for ent, (vel, pos) in esper.get_components(Velocity, Position): pos.x += vel.x * dt # Processors class MyProcessor(esper.Processor): def process(self, dt): pass esper.add_processor(MyProcessor()) esper.process(dt=0.016) # Events esper.set_handler('event', callback) esper.dispatch_event('event', arg1, arg2) # Worlds esper.switch_world('scene_name') esper.list_worlds() ``` -------------------------------- ### Event Pattern Example Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md Illustrates the Event pattern for one-off notifications. This snippet shows dispatching an event from one processor and setting a handler for it in another. ```python # In collision processor esper.dispatch_event('player_damaged', 10) # In health processor esper.set_handler('player_damaged', self.on_player_damaged) def on_player_damaged(self, damage): self.health -= damage ``` -------------------------------- ### Processor Pattern Example Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md An example of the Processor pattern, where game logic is encapsulated within processor classes. This snippet shows how to query entities with multiple components within a processor's process method. ```python class CollisionProcessor(esper.Processor): def process(self): # Get all entities with both components for ent, (body1, body2) in esper.get_components(PhysicsBody, Collider): if self.check_collision(body1, body2): esper.dispatch_event('collision', ent) ``` -------------------------------- ### Processor Logic Example Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md Demonstrates the correct placement of game logic within a processor. This snippet shows a MovementProcessor updating entity positions based on their velocities. ```python class MovementProcessor(esper.Processor): def process(self, dt): for ent, (vel, pos) in esper.get_components(Velocity, Position): pos.x += vel.x * dt # Logic here ``` -------------------------------- ### Get All Components for Entity Source: https://github.com/benmoran56/esper/blob/master/_autodocs/INDEX.md Retrieves all components attached to a specific entity as a tuple. ```python components_for_entity(entity) -> tuple[_C, ...] ``` -------------------------------- ### Component Pattern Example Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md An example of the Component pattern, where components are pure data containers with no associated logic. This snippet defines a dataclass for enemy state. ```python @dataclass class Enemy: """Pure data—no methods.""" ai_state: str = "patrol" patrol_speed: float = 2.0 attack_range: float = 5.0 ``` -------------------------------- ### Try Get Multiple Components for Entity Source: https://github.com/benmoran56/esper/blob/master/_autodocs/INDEX.md Attempts to retrieve specified components for an entity. Returns a tuple of components or None if any are missing. ```python try_components(entity, *component_types) -> tuple[_C, ...] | None ``` -------------------------------- ### Complete Game Loop with Esper Source: https://github.com/benmoran56/esper/blob/master/_autodocs/09-cookbook.md This snippet demonstrates a full game loop using Esper, including component definitions, processor setup, entity creation, and game logic processing. It shows how to manage game state, update entities, and handle basic game events like health loss and entity deletion. ```python import esper import time from dataclasses import dataclass # Define components @dataclass class Position: x: float = 0.0 y: float = 0.0 @dataclass class Velocity: x: float = 0.0 y: float = 0.0 @dataclass class Health: hp: float max_hp: float # Define processors class MovementProcessor(esper.Processor): def process(self, dt): for entity_id, (vel, pos) in esper.get_components(Velocity, Position): pos.x += vel.x * dt pos.y += vel.y * dt class HealthProcessor(esper.Processor): def process(self, dt): for entity_id, (health,) in esper.get_component(Health): if health.hp <= 0: esper.delete_entity(entity_id) class RenderProcessor(esper.Processor): def process(self, dt): for entity_id, (pos,) in esper.get_component(Position): print(f"Entity {entity_id} at ({pos.x:.1f}, {pos.y:.1f})") # Game setup class Game: def __init__(self): esper.add_processor(MovementProcessor(), priority=2) esper.add_processor(HealthProcessor(), priority=1) esper.add_processor(RenderProcessor(), priority=0) self.running = True self.create_entities() def create_entities(self): # Create player self.player = esper.create_entity( Position(x=50, y=50), Velocity(x=0, y=0), Health(hp=100, max_hp=100) ) # Create enemies for i in range(3): esper.create_entity( Position(x=10 + i * 20, y=10), Velocity(x=5, y=0), Health(hp=20, max_hp=20) ) def run(self): last_time = time.time() while self.running: current_time = time.time() dt = min(0.016, current_time - last_time) # Cap at 60 FPS last_time = current_time # Process game logic esper.process(dt) # Simulate input/damage if time.time() % 1 < 0.016: # Every second health = esper.try_component(self.player, Health) if health: health.hp -= 10 # Check game over if not esper.entity_exists(self.player): self.running = False time.sleep(0.001) # Run the game game = Game() game.run() ``` -------------------------------- ### Try Get Component for Entity Source: https://github.com/benmoran56/esper/blob/master/_autodocs/INDEX.md Attempts to retrieve a component of a specified type for a given entity. Returns the component or None if not found. ```python try_component(entity, component_type) -> _C | None ``` -------------------------------- ### Get Esper Library Version Source: https://github.com/benmoran56/esper/blob/master/_autodocs/07-utility-functions.md Retrieves the current version of the esper library as a string. Useful for compatibility checks or logging. ```python import esper print(f"Using esper version {esper.version}") ``` -------------------------------- ### Retrieve Processor Instance in Esper Source: https://github.com/benmoran56/esper/blob/master/_autodocs/04-processor-management.md Shows how to get a registered processor instance using `get_processor`. This is useful for calling methods on one processor from another, like emitting particles from an explosion. ```python import esper from dataclasses import dataclass @dataclass class Particle: lifetime: float velocity_x: float velocity_y: float class ParticleProcessor(esper.Processor): def __init__(self): self.particle_count = 0 def process(self): self.particle_count = 0 for ent, (p,) in esper.get_component(Particle): p.lifetime -= 0.016 if p.lifetime <= 0: esper.delete_entity(ent) else: self.particle_count += 1 def emit_particles(self, x, y, count): for _ in range(count): esper.create_entity(Particle(1.0, 0, -1)) class ExplosionProcessor(esper.Processor): def process(self): # Get access to particle processor to emit particles particle_proc = esper.get_processor(ParticleProcessor) if particle_proc: particle_proc.emit_particles(x=100, y=100, count=50) # Usage esper.add_processor(ParticleProcessor()) esper.add_processor(ExplosionProcessor()) ``` -------------------------------- ### Get All Components for an Entity Source: https://github.com/benmoran56/esper/blob/master/README.md Retrieves all components assigned to a specific entity as a tuple. This is a performance-intensive operation and should not be used frequently, e.g., within `Processor.process`. ```python player_components = esper.components_for_entity(player_entity_id) esper.switch_world('context_name') player_entity_id = esper.create_entity(player_components) ``` -------------------------------- ### Safe Component Removal Pattern Source: https://github.com/benmoran56/esper/blob/master/_autodocs/08-types-and-errors.md Provides an example for safely removing a component from an entity, implying the use of `try_remove_component` or similar checks to avoid errors. ```python import esper entity = esper.create_entity() ``` -------------------------------- ### Try to Get Component Source: https://github.com/benmoran56/esper/blob/master/_autodocs/INDEX.md Attempts to retrieve a component from an entity, returning `None` if the component is not present. This avoids exceptions and is more performant than checking `has_component()` followed by `component_for_entity()`. ```python try_component(entity, ComponentType) ``` -------------------------------- ### Create and Delete Entities Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md Demonstrates how to create a new entity with initial components and how to delete an existing entity. ```python entity = esper.create_entity(Position(10, 20)) esper.delete_entity(entity) exists = esper.entity_exists(entity) ``` -------------------------------- ### Dependency Injection with Esper Processors Source: https://github.com/benmoran56/esper/blob/master/_autodocs/09-cookbook.md Shows how to use dependency injection to provide services like audio managers and loggers to Esper processors. This promotes cleaner code and easier testing. ```python import esper from dataclasses import dataclass class AudioManager: def play_sound(self, sound_name: str): print(f"Playing sound: {sound_name}") class Logger: def log(self, message: str): print(f"[LOG] {message}") class GameProcessor(esper.Processor): def __init__(self, audio: AudioManager, logger: Logger): self.audio = audio self.logger = logger def process(self): self.logger.log("Processing game logic") self.audio.play_sound("ambient.wav") # Setup audio = AudioManager() logger = Logger() processor = GameProcessor(audio, logger) esper.add_processor(processor) ``` -------------------------------- ### Add and Remove Components Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md Shows how to add a component to an entity and how to remove a component. It also demonstrates safely retrieving a component that might not exist. ```python esper.add_component(entity, Health(100)) esper.remove_component(entity, Health) health = esper.try_component(entity, Health) # Returns None if missing ``` -------------------------------- ### Get Esper Version Source: https://github.com/benmoran56/esper/blob/master/_autodocs/INDEX.md Retrieves the current version of the Esper library. This can be useful for compatibility checks or logging. ```python version ``` -------------------------------- ### Registering Processors with Priorities Source: https://github.com/benmoran56/esper/blob/master/_autodocs/04-processor-management.md Shows how to register multiple processor instances with the Esper world using add_processor. The priority parameter determines the execution order, with higher values executing first. ```python import esper from dataclasses import dataclass class InputProcessor(esper.Processor): def process(self, dt): # Handle input first pass class MovementProcessor(esper.Processor): def process(self, dt): # Move entities based on input state pass class RenderProcessor(esper.Processor): def process(self, dt): # Render final positions pass # Register processors with explicit priorities esper.add_processor(RenderProcessor(), priority=0) # Renders last esper.add_processor(MovementProcessor(), priority=2) # Moves second esper.add_processor(InputProcessor(), priority=3) # Handles input first # In game loop esper.process(dt) # Executes in order: Input, Movement, Render ``` -------------------------------- ### Get Specific Processor Instance Source: https://github.com/benmoran56/esper/blob/master/_autodocs/INDEX.md Retrieves a registered processor instance by its type. Returns the processor or None if not found. ```python get_processor(processor_type) -> Processor | None ``` -------------------------------- ### Get Specific Component for Entity Source: https://github.com/benmoran56/esper/blob/master/_autodocs/INDEX.md Retrieves the component of a specified type for a given entity. Raises an error if the component is not found. ```python component_for_entity(entity, component_type) -> _C ``` -------------------------------- ### Add and Process Processors Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md Demonstrates how to define a custom processor and add it to the Esper world. It also shows how to trigger the execution of all registered processors. ```python class MyProcessor(esper.Processor): def process(self, dt): pass esper.add_processor(MyProcessor(), priority=0) esper.process(dt=0.016) # Execute all processors ``` -------------------------------- ### Valid Component Definitions Source: https://github.com/benmoran56/esper/blob/master/_autodocs/08-types-and-errors.md Examples of different Python classes that can be used as components in Esper, including regular classes, dataclasses, and NamedTuples. ```python # Valid component (regular class) class Position: def __init__(self, x=0.0, y=0.0): self.x = x self.y = y ``` ```python # Valid component (dataclass - recommended) from dataclasses import dataclass @dataclass class Velocity: x: float = 0.0 y: float = 0.0 ``` ```python # Valid component (NamedTuple) from typing import NamedTuple class Health(NamedTuple): hp: float max_hp: float ``` ```python # Valid component (any class) class Renderable: def __init__(self, sprite_path): self.sprite_path = sprite_path self.visible = True ``` -------------------------------- ### List All World Contexts in Esper Source: https://github.com/benmoran56/esper/blob/master/_autodocs/05-world-contexts.md Demonstrates how to list all existing world contexts and observe changes after switching and deleting worlds. The 'default' world is always present. ```python import esper print(esper.list_worlds()) # ['default'] esper.switch_world('menu') print(esper.list_worlds()) # ['default', 'menu'] esper.switch_world('gameplay') print(esper.list_worlds()) # ['default', 'menu', 'gameplay'] esper.delete_world('menu') print(esper.list_worlds()) # ['default', 'gameplay'] ``` -------------------------------- ### Execute All Processors with Esper Source: https://github.com/benmoran56/esper/blob/master/_autodocs/04-processor-management.md Illustrates the main game loop entry point using `esper.process()`. This function calls the `process` method on all registered processors, passing arguments like delta time. ```python import esper import time class GameState: def __init__(self): self.running = True self.last_time = time.time() def run_game_loop(self): while self.running: current_time = time.time() dt = current_time - self.last_time self.last_time = current_time # Process all registered processors esper.process(dt) # Cap frame rate at 60 FPS time.sleep(max(0, 0.016 - dt)) ``` -------------------------------- ### Manage World Contexts Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md Demonstrates switching between different isolated ECS environments (worlds), listing available worlds, and deleting a world. ```python esper.switch_world('level_1') # Create if needed esper.list_worlds() # ['default', 'level_1'] esper.delete_world('level_1') ``` -------------------------------- ### Registering Static and Instance Event Handlers Source: https://github.com/benmoran56/esper/blob/master/_autodocs/06-event-system.md Demonstrates how to register static methods and instance methods as event handlers using `set_handler`. This is useful for decoupling components and enabling asynchronous communication within the Esper ECS. ```python import esper class GameEvents: @staticmethod def on_player_damaged(damage): print(f"Player took {damage} damage!") @staticmethod def on_game_over(score): print(f"Game Over! Final score: {score}") class GameProcessor(esper.Processor): def __init__(self): # Register static method handlers esper.set_handler('player_damaged', GameEvents.on_player_damaged) esper.set_handler('game_over', GameEvents.on_game_over) def process(self): # Dispatch events esper.dispatch_event('player_damaged', 10) esper.dispatch_event('game_over', 1000) # Usage processor = GameProcessor() esper.add_processor(processor) esper.process() # Dispatches events ``` ```python import esper class UIManager: def __init__(self): self.damage_count = 0 esper.set_handler('player_damaged', self.on_damage) def on_damage(self, damage): self.damage_count += damage print(f"Total damage this session: {self.damage_count}") class GameProcessor(esper.Processor): def process(self): esper.dispatch_event('player_damaged', 15) esper.dispatch_event('player_damaged', 5) # Usage ui = UIManager() esper.add_processor(GameProcessor()) esper.process() # Calls ui.on_damage twice ``` -------------------------------- ### Define a simple Position Component Source: https://github.com/benmoran56/esper/blob/master/README.md Define a basic Component for storing position data. This example uses standard Python class definition. ```python class Position: def __init__(self, x=0.0, y=0.0): self.x = x self.y = y ``` -------------------------------- ### Switch Between Esper World Contexts Source: https://github.com/benmoran56/esper/blob/master/_autodocs/05-world-contexts.md Shows how to switch between world contexts, creating new ones if they don't exist. It illustrates how operations affect the currently active world and how to verify entity counts in different worlds. ```python import esper from dataclasses import dataclass @dataclass class Position: x: float y: float # Default world is active on import player = esper.create_entity(Position(10, 20)) print(esper.current_world) # 'default' # Switch to a new world (created automatically) esper.switch_world('menu_scene') print(esper.current_world) # 'menu_scene' # Create menu entities in this world menu_button = esper.create_entity(Position(100, 100)) # Switch back to default world esper.switch_world('default') print(f"Default world entities: {len(list(esper.get_entities()))}") # 1 (player) # Switch to menu world esper.switch_world('menu_scene') print(f"Menu world entities: {len(list(esper.get_entities()))}") # 1 (menu_button) ``` -------------------------------- ### Get Specific Component for an Entity Source: https://github.com/benmoran56/esper/blob/master/README.md Retrieves a specific component from an entity if it exists. Raises an error if the component is not found. Useful when combined with `has_component`. ```python if esper.has_component(ent, SFX): sfx = esper.component_for_entity(ent, SFX) sfx.play() ``` -------------------------------- ### Create Entity and Add Components Source: https://github.com/benmoran56/esper/blob/master/README.md Create a new entity and add 'Velocity' and 'Position' components to it using `esper.add_component()`. This is a standard way to initialize entities. ```python player = esper.create_entity() esper.add_component(player, Velocity(x=0.9, y=1.2)) esper.add_component(player, Position(x=5, y=5)) ``` -------------------------------- ### Import Esper Library Source: https://github.com/benmoran56/esper/blob/master/README.md Import the esper library to begin using its functionalities for ECS development. ```python import esper ``` -------------------------------- ### Typed Component Queries in Esper Source: https://github.com/benmoran56/esper/blob/master/_autodocs/08-types-and-errors.md Illustrates how to use Python's type hinting with Esper's component query functions like 'get_component', 'get_components', 'try_component', and 'try_components' for improved code clarity and static analysis. ```python # Component query returns typed tuples from typing import Iterable entities: Iterable[tuple[int, Position]] = esper.get_component(Position) # Multiple components entities: Iterable[tuple[int, tuple[Position, Velocity]]] = esper.get_components( Position, Velocity ) # Component instance pos: Position | None = esper.try_component(entity, Position) # Multiple components components: tuple[Position, Velocity] | None = esper.try_components( entity, Position, Velocity ) # Event handlers def handle_event(arg1: int, arg2: str) -> None: pass esper.set_handler('my_event', handle_event) ``` -------------------------------- ### Get Current World Name Source: https://github.com/benmoran56/esper/blob/master/_autodocs/05-world-contexts.md Access the `current_world` attribute to retrieve the name of the active world context. Use `switch_world()` to change the active world. ```python import esper print(esper.current_world) # 'default' esper.switch_world('gameplay') print(esper.current_world) # 'gameplay' esper.switch_world('default') print(esper.current_world) # 'default' ``` -------------------------------- ### Implement Event-Driven Game System with Esper Source: https://github.com/benmoran56/esper/blob/master/_autodocs/09-cookbook.md Sets up event handlers for entity damage, death, and healing using esper.set_handler and esper.dispatch_event. A CombatProcessor simulates combat by dispatching damage events. ```python import esper from dataclasses import dataclass @dataclass class Health: hp: float max_hp: float class GameEventSystem: def __init__(self): esper.set_handler('entity_damaged', self.on_entity_damaged) esper.set_handler('entity_died', self.on_entity_died) esper.set_handler('entity_healed', self.on_entity_healed) def on_entity_damaged(self, entity_id, damage_amount): health = esper.try_component(entity_id, Health) if health: health.hp -= damage_amount if health.hp <= 0: esper.dispatch_event('entity_died', entity_id) def on_entity_died(self, entity_id): print(f"Entity {entity_id} died!") esper.delete_entity(entity_id) def on_entity_healed(self, entity_id, heal_amount): health = esper.try_component(entity_id, Health) if health: health.hp = min(health.hp + heal_amount, health.max_hp) class CombatProcessor(esper.Processor): def process(self): # Simulate combat for entity_id in esper.get_entities(): if esper.has_component(entity_id, Health): # Random damage if __import__('random').random() < 0.1: esper.dispatch_event('entity_damaged', entity_id, 5) # Usage events = GameEventSystem() esper.add_processor(CombatProcessor()) ``` -------------------------------- ### Get Multiple Components Efficiently Source: https://github.com/benmoran56/esper/blob/master/_autodocs/INDEX.md Retrieves all components of specified types for an entity. This is more efficient than calling `get_component()` multiple times, especially when querying for many component types. ```python get_components(entity, ComponentType1, ComponentType2, ...) ``` -------------------------------- ### Set and Dispatch Events Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md Shows how to define a handler for a specific event name and how to dispatch an event with associated arguments. ```python def on_collision(e1, e2): print(f"Collision: {e1} and {e2}") esper.set_handler('collision', on_collision) esper.dispatch_event('collision', 1, 2) ``` -------------------------------- ### Get All Entities Source: https://github.com/benmoran56/esper/blob/master/_autodocs/01-entity-management.md Returns a generator that yields all entity IDs currently in the world (excluding dead entities marked for deletion). This is useful for iterating over all entities when component types are not relevant. ```Python import esper # Create some entities for i in range(5): esper.create_entity() ``` -------------------------------- ### Scene Management System with Esper Source: https://github.com/benmoran56/esper/blob/master/_autodocs/09-cookbook.md This snippet shows how to implement a scene management system using Esper's world switching capabilities. It defines different scene types and provides methods to switch between them, clearing the database and setting up new processors and entities for each scene. ```python import esper from enum import Enum class SceneType(Enum): MENU = 'menu' GAMEPLAY = 'gameplay' PAUSE = 'pause' LEVEL_COMPLETE = 'level_complete' class SceneManager: def __init__(self): self.current_scene = SceneType.MENU self.scenes = {} self._setup_scenes() def _setup_scenes(self): for scene_type in SceneType: esper.switch_world(scene_type.value) self.scenes[scene_type] = scene_type.value def switch_to(self, scene_type: SceneType): # Clean up current scene esper.clear_database() # Switch to new scene esper.switch_world(scene_type.value) self.current_scene = scene_type # Setup scene if scene_type == SceneType.MENU: self._setup_menu() elif scene_type == SceneType.GAMEPLAY: self._setup_gameplay() elif scene_type == SceneType.PAUSE: self._setup_pause() def _setup_menu(self): esper.add_processor(MenuInputProcessor(), priority=10) esper.add_processor(MenuRenderProcessor(), priority=0) # Create menu entities def _setup_gameplay(self): esper.add_processor(GameInputProcessor(), priority=10) esper.add_processor(MovementProcessor(), priority=5) esper.add_processor(CollisionProcessor(), priority=4) esper.add_processor(GameRenderProcessor(), priority=0) # Load level def _setup_pause(self): esper.add_processor(PauseMenuProcessor(), priority=10) # Draw pause UI class MenuInputProcessor(esper.Processor): def process(self): # Handle menu input pass class GameInputProcessor(esper.Processor): def process(self): # Handle gameplay input pass class MenuRenderProcessor(esper.Processor): def process(self): # Render menu pass class GameRenderProcessor(esper.Processor): def process(self): # Render gameplay pass class PauseMenuProcessor(esper.Processor): def process(self): # Render pause menu pass class MovementProcessor(esper.Processor): def process(self): pass class CollisionProcessor(esper.Processor): def process(self): pass ``` -------------------------------- ### Clear Deferredly Deleted Entities Source: https://github.com/benmoran56/esper/blob/master/_autodocs/07-utility-functions.md Finalizes the deletion of entities marked for deferred deletion. This is called automatically at the start of `process()`, but manual calls are needed if processors are executed manually. ```python import esper # Create and delete entities entity1 = esper.create_entity() entity2 = esper.create_entity() # Mark for deferred deletion esper.delete_entity(entity1) esper.delete_entity(entity2) # Entities are still "alive" until cleared print(esper.entity_exists(entity1)) # False (marked dead) # Manual cleanup (automatic in process()) esper.clear_dead_entities() # Now truly deleted from the database print(len(list(esper.get_entities()))) # 0 ``` -------------------------------- ### process Source: https://github.com/benmoran56/esper/blob/master/_autodocs/04-processor-management.md The main game loop entry point. Calls the `process()` method on all registered processors in priority order (highest first). ```APIDOC ## process ### Description The main game loop entry point. Calls the `process()` method on all registered processors in priority order (highest first). First clears dead entities (those marked for deletion), then executes each processor. All arguments and keyword arguments are passed through to each processor's `process()` method. Typical usage passes delta time: `esper.process(dt)`. ### Method ```python def process(*args: Any, **kwargs: Any) -> None ``` ### Parameters #### Path Parameters - **args** (Any) - Optional - Positional arguments to pass to all processor `process()` methods - **kwargs** (Any) - Optional - Keyword arguments to pass to all processor `process()` methods ### Response #### Success Response (None) This function returns nothing. ### Raises Any exception raised by a processor will propagate. ### Example ```python import esper import time def run_game_loop(): while True: current_time = time.time() dt = current_time - self.last_time self.last_time = current_time # Process all registered processors esper.process(dt) time.sleep(max(0, 0.016 - dt)) ``` ``` -------------------------------- ### Testing Esper Entities and Components Source: https://github.com/benmoran56/esper/blob/master/_autodocs/09-cookbook.md Provides unit tests for core Esper functionalities, including entity creation, component querying, and component modification. Ensures the integrity of your ECS setup. ```python import esper from dataclasses import dataclass @dataclass class TestComponent: value: int def test_entity_creation(): esper.clear_database() entity = esper.create_entity(TestComponent(42)) assert esper.entity_exists(entity) component = esper.try_component(entity, TestComponent) assert component.value == 42 def test_component_queries(): esper.clear_database() @dataclass class A: value: int @dataclass class B: value: int e1 = esper.create_entity(A(1), B(2)) e2 = esper.create_entity(A(3)) e3 = esper.create_entity(B(4)) # Query both components results = esper.get_components(A, B) assert len(results) == 1 assert results[0][0] == e1 def test_component_modification(): esper.clear_database() entity = esper.create_entity(TestComponent(0)) comp = esper.component_for_entity(entity, TestComponent) comp.value = 100 comp = esper.component_for_entity(entity, TestComponent) assert comp.value == 100 # Run tests test_entity_creation() test_component_queries() test_component_modification() print("All tests passed!") ``` -------------------------------- ### Project File Organization Source: https://github.com/benmoran56/esper/blob/master/_autodocs/QUICKSTART.md Illustrates a typical file structure for an Esper-based game project, separating components, processors, entities, and main game logic. ```tree my_game/ ├── components.py # All component definitions ├── processors.py # All processor implementations ├── events.py # Event handler setup ├── entities.py # Entity factory functions ├── main.py # Main game loop └── scenes/ ├── menu.py # Menu scene setup ├── gameplay.py # Gameplay scene setup └── level_complete.py # Level complete scene setup ``` -------------------------------- ### clear_dead_entities Source: https://github.com/benmoran56/esper/blob/master/_autodocs/07-utility-functions.md Finalizes the deletion of all entities marked as dead. This function is called automatically at the start of `process()`, but can be called manually if processors are executed without `process()`. It also clears component query caches. ```APIDOC ## clear_dead_entities ### Description Finalizes the deletion of all entities marked as dead (deleted via `delete_entity(entity, immediate=False)`). This function is called automatically at the start of `process()`, so manual calls are only necessary if you're executing processors manually without using `process()`. Clears component query caches after finalizing deletions. ### Method Python Function ### Parameters None ### Returns - `None` - This function returns nothing ### Example ```python import esper # Create and delete entities entity1 = esper.create_entity() entity2 = esper.create_entity() # Mark for deferred deletion esper.delete_entity(entity1) esper.delete_entity(entity2) # Entities are still "alive" until cleared print(esper.entity_exists(entity1)) # False (marked dead) # Manual cleanup (automatic in process()) esper.clear_dead_entities() # Now truly deleted from the database print(len(list(esper.get_entities()))) ``` ### Advanced Example: Manual Processor Execution ```python import esper class GameLoop: def __init__(self): self.processors = [InputProcessor(), MovementProcessor()] def update(self, dt): # Execute processors manually without using esper.process() for processor in self.processors: processor.process(dt) # Must manually finalize entity deletion esper.clear_dead_entities() # Usage loop = GameLoop() while running: loop.update(0.016) ``` ``` -------------------------------- ### Manual Processor Execution with Entity Cleanup Source: https://github.com/benmoran56/esper/blob/master/_autodocs/07-utility-functions.md Shows how to manually execute processors and then manually finalize entity deletions using `clear_dead_entities()`. ```python import esper class GameLoop: def __init__(self): self.processors = [InputProcessor(), MovementProcessor()] def update(self, dt): # Execute processors manually without using esper.process() for processor in self.processors: processor.process(dt) # Must manually finalize entity deletion esper.clear_dead_entities() # Usage loop = GameLoop() while running: loop.update(0.016) ``` -------------------------------- ### Timed Processor Execution for Profiling Source: https://github.com/benmoran56/esper/blob/master/_autodocs/04-processor-management.md Shows how to use `timed_process` to execute all registered processors and record their execution times. This is useful for performance profiling. ```python import esper from esper.tests.processors import ( MovementProcessor, CollisionProcessor, RenderProcessor, ) class ProfiledGame: def __init__(self): self.frame_count = 0 def run(self): for frame in range(300): esper.timed_process(dt=0.016) self.frame_count += 1 if frame % 60 == 0: # Print timing information every second self.print_timings() def print_timings(self): print(f"\n--- Frame {self.frame_count} Timings ---") for processor_name, elapsed_ms in esper.process_times.items(): print(f"{processor_name}: {elapsed_ms}ms") # Usage game = ProfiledGame() esper.add_processor(MovementProcessor()) esper.add_processor(CollisionProcessor()) esper.add_processor(RenderProcessor()) game.run() ``` -------------------------------- ### Safe Component Access Patterns Source: https://github.com/benmoran56/esper/blob/master/_autodocs/08-types-and-errors.md Demonstrates three patterns for safely accessing components: checking with `has_component` first, using `try_component`, and utilizing the walrus operator with `try_component` (Python 3.8+). ```python import esper entity = esper.create_entity() # Pattern 1: Check before access if esper.has_component(entity, Position): pos = esper.component_for_entity(entity, Position) print(f"Position: {pos.x}, {pos.y}") # Pattern 2: Use try_component (preferred) pos = esper.try_component(entity, Position) if pos: print(f"Position: {pos.x}, {pos.y}") # Pattern 3: Use walrus operator (Python 3.8+) if pos := esper.try_component(entity, Position): print(f"Position: {pos.x}, {pos.y}") ``` -------------------------------- ### Retrieve Component Instance from Entity Source: https://github.com/benmoran56/esper/blob/master/_autodocs/02-component-management.md Use `component_for_entity` to get a specific component instance from an entity. This is a direct lookup and will raise a `KeyError` if the component or entity does not exist. It's often used after checking existence with `has_component`. ```Python import esper from dataclasses import dataclass @dataclass class Health: hp: float max_hp: float entity = esper.create_entity(Health(hp=50, max_hp=100)) # Unsafe: KeyError if component doesn't exist health = esper.component_for_entity(entity, Health) health.hp -= 10 # Safe: Check first if esper.has_component(entity, Health): health = esper.component_for_entity(entity, Health) health.hp -= 10 ``` -------------------------------- ### Implement Query Caching with Esper Source: https://github.com/benmoran56/esper/blob/master/_autodocs/09-cookbook.md Demonstrates how to cache query results for components to optimize performance. The cache is automatically invalidated when components change, but manual invalidation is also supported. ```python import esper from dataclasses import dataclass @dataclass class Position: x: float y: float @dataclass class Velocity: x: float y: float class CachedMovementProcessor(esper.Processor): def __init__(self): self.cached_entities = None self.cache_valid = False def process(self, dt): # Use cache if valid, otherwise query if not self.cache_valid: self.cached_entities = esper.get_components(Velocity, Position) self.cache_valid = True for entity_id, (vel, pos) in self.cached_entities: pos.x += vel.x * dt pos.y += vel.y * dt def invalidate_cache(self): self.cache_valid = False # Usage: Cache automatically invalidated by esper when components change # Manual invalidation if needed processor = CachedMovementProcessor() ``` -------------------------------- ### Get Components with Four Specific Types Source: https://github.com/benmoran56/esper/blob/master/_autodocs/03-component-queries.md Queries all entities that possess exactly four specified component types. Returns entity IDs and a tuple of the components in the order they were requested. This is a type-safe overload for common use cases. ```python import esper from dataclasses import dataclass @dataclass class Position: x: float y: float @dataclass class Velocity: x: float y: float @dataclass class Acceleration: x: float y: float @dataclass class Mass: value: float class PhysicsProcessor(esper.Processor): def process(self, dt): for ent, (pos, vel, acc, mass) in esper.get_components( Position, Velocity, Acceleration, Mass ): # Advanced physics calculations for entities with all components vel.x += (acc.x / mass.value) * dt vel.y += (acc.y / mass.value) * dt pos.x += vel.x * dt pos.y += vel.y * dt ``` -------------------------------- ### Custom Game Loop with Manual Processor Management Source: https://github.com/benmoran56/esper/blob/master/_autodocs/07-utility-functions.md Implement a custom game loop to manage processor execution order and manually clear dead entities and cache. ```python import esper class CustomGameLoop: """Game loop with custom processor execution.""" def __init__(self): self.running = True self.processors = [] def add_processor(self, processor_instance, priority=0): processor_instance.priority = priority self.processors.append(processor_instance) self.processors.sort(key=lambda p: p.priority, reverse=True) def run(self): while self.running: self.update(0.016) def update(self, dt): # Execute processors in priority order for processor in self.processors: processor.process(dt) # Manually handle deferred deletion esper.clear_dead_entities() # Clear cache if needed esper.clear_cache() ``` -------------------------------- ### Handling KeyError for Missing Components Source: https://github.com/benmoran56/esper/blob/master/_autodocs/08-types-and-errors.md Demonstrates how to handle KeyError when accessing a non-existent entity or component using try-except blocks. It also shows the safer approach using `try_component`. ```python import esper entity = esper.create_entity() # This raises KeyError: entity doesn't have Position component try: pos = esper.component_for_entity(entity, Position) except KeyError as e: print(f"Component not found: {e}") # Safer approach using try_component pos = esper.try_component(entity, Position) if pos is None: print("No position component") ``` -------------------------------- ### Dispatching Events with Arguments and No Arguments Source: https://github.com/benmoran56/esper/blob/master/_autodocs/06-event-system.md Demonstrates how to register handlers for events and dispatch them with or without arguments. Use `set_handler` to register a function for an event name and `dispatch_event` to trigger it. ```python import esper def handle_collision(entity_a, entity_b): print(f"Collision between {entity_a} and {entity_b}") esper.set_handler('collision', handle_collision) # Dispatch with arguments esper.dispatch_event('collision', 5, 10) # Calls handle_collision(5, 10) # Dispatch with no arguments def handle_ready(): print("System ready!") esper.set_handler('ready', handle_ready) esper.dispatch_event('ready') # Calls handle_ready() ``` -------------------------------- ### Add and Remove Processors in Esper Source: https://github.com/benmoran56/esper/blob/master/_autodocs/04-processor-management.md Demonstrates adding and removing processor instances using `add_processor` and `remove_processor`. Processors are added with optional priorities, and removed by their class type. ```python import esper class PauseMenuProcessor(esper.Processor): def process(self): # Handle pause menu pass class GameplayProcessor(esper.Processor): def process(self, dt): # Handle game logic pass # Active in game esper.add_processor(GameplayProcessor()) # User pauses game esper.add_processor(PauseMenuProcessor(), priority=10) # High priority esper.remove_processor(GameplayProcessor) # Stop gameplay logic # User unpauses esper.remove_processor(PauseMenuProcessor) esper.add_processor(GameplayProcessor()) ``` -------------------------------- ### Scene Management with World Contexts Source: https://github.com/benmoran56/esper/blob/master/_autodocs/05-world-contexts.md Manage different game scenes (e.g., menu, gameplay) by switching world contexts and adding/removing processors accordingly. This pattern helps isolate scene-specific logic and entities. ```python import esper class Game: def __init__(self): self.current_scene = 'menu' self.setup_menu_scene() def setup_menu_scene(self): esper.switch_world('menu') esper.add_processor(MenuInputProcessor(), priority=10) esper.add_processor(MenuRenderProcessor()) # Create menu entities def setup_gameplay_scene(self): esper.switch_world('gameplay') esper.add_processor(GameInputProcessor(), priority=10) esper.add_processor(MovementProcessor(), priority=5) esper.add_processor(CollisionProcessor(), priority=4) esper.add_processor(GameRenderProcessor()) # Create game entities def load_menu(self): # Save current scene state if needed current_processors = esper.list_worlds() # Switch to menu self.setup_menu_scene() self.current_scene = 'menu' def load_gameplay(self): self.setup_gameplay_scene() self.current_scene = 'gameplay' def run(self): while True: if self.current_scene == 'menu': self.process_menu() else: self.process_gameplay() def process_menu(self): esper.process() # Processes menu # Check for start game event def process_gameplay(self): esper.process(dt=0.016) # Processes gameplay # Check for pause/quit events ``` -------------------------------- ### Inspect a Specific Esper World Source: https://github.com/benmoran56/esper/blob/master/_autodocs/07-utility-functions.md Switch to a specified world and print summary information, including the total number of entities and the components of the first few entities. ```python import esper class DebugTools: @staticmethod def inspect_world(world_name): esper.switch_world(world_name) entities = list(esper.get_entities()) print(f"World '{world_name}':") print(f" Entities: {len(entities)}") for entity_id in entities[:5]: # Show first 5 components = esper.components_for_entity(entity_id) component_names = [type(c).__name__ for c in components] print(f" Entity {entity_id}: {', '.join(component_names)}") if len(entities) > 5: print(f" ... and {len(entities) - 5} more entities") ```