pub trait System: Send {
// Required methods
fn name(&self) -> &'static str;
fn run(&mut self, world: &mut World);
// Provided methods
fn component_access(&self) -> Access { ... }
fn initialize(&mut self, _world: &mut World) { ... }
fn should_run(&self, _world: &World) -> bool { ... }
fn is_read_only(&self) -> bool { ... }
}Expand description
A trait for types that can be run as systems on a World.
Systems are the behavior layer of the ECS. They operate on entities and components, implementing game logic, physics, rendering, and more.
§Requirements
Systems must be Send to enable future parallel execution. This means
all data accessed by the system must be thread-safe.
§Implementing System
For simple systems, implement this trait directly:
use goud_engine::ecs::{World};
use goud_engine::ecs::system::System;
struct CountEntities {
count: u32,
}
impl System for CountEntities {
fn name(&self) -> &'static str {
"CountEntities"
}
fn run(&mut self, world: &mut World) {
self.count = world.entity_count() as u32;
}
}§Access Tracking
Override component_access() to declare what components your system
reads and writes. This enables the scheduler to detect conflicts and
run non-conflicting systems in parallel.
use goud_engine::ecs::{World, Component, ComponentId};
use goud_engine::ecs::system::System;
use goud_engine::ecs::query::Access;
#[derive(Clone, Copy)]
struct Position { x: f32, y: f32 }
impl Component for Position {}
#[derive(Clone, Copy)]
struct Velocity { x: f32, y: f32 }
impl Component for Velocity {}
struct MovementSystem;
impl System for MovementSystem {
fn name(&self) -> &'static str {
"MovementSystem"
}
fn component_access(&self) -> Access {
let mut access = Access::new();
access.add_write(ComponentId::of::<Position>());
access.add_read(ComponentId::of::<Velocity>());
access
}
fn run(&mut self, world: &mut World) {
// Movement logic here
}
}§Lifecycle
Systems have optional lifecycle hooks:
initialize(): Called once when the system is first addedrun(): Called each time the system executes
§Thread Safety
The Send bound ensures systems can be sent between threads. However,
the scheduler ensures only one system runs on a World at a time (for now).
Required Methods§
Provided Methods§
Sourcefn component_access(&self) -> Access
fn component_access(&self) -> Access
Returns the component access pattern for this system.
Override this to declare which components your system reads and writes. The default implementation returns empty access (no components).
Accurate access information enables:
- Parallel execution of non-conflicting systems
- Compile-time verification of system ordering
- Runtime conflict detection
Sourcefn initialize(&mut self, _world: &mut World)
fn initialize(&mut self, _world: &mut World)
Called once when the system is first added to a scheduler.
Use this for one-time initialization that requires world access. The default implementation does nothing.
§Arguments
world- Mutable reference to the world
Sourcefn should_run(&self, _world: &World) -> bool
fn should_run(&self, _world: &World) -> bool
Returns whether this system should run.
Override this to conditionally skip system execution.
The default implementation always returns true.
§Arguments
world- Reference to the world (for checking conditions)
§Example
use goud_engine::ecs::{World, Component};
use goud_engine::ecs::system::System;
struct GamePaused;
impl Component for GamePaused {}
struct PhysicsSystem;
impl System for PhysicsSystem {
fn name(&self) -> &'static str { "PhysicsSystem" }
fn should_run(&self, world: &World) -> bool {
// Skip physics when game is paused
// (In a real implementation, you'd check a resource)
true
}
fn run(&mut self, world: &mut World) {
// Physics logic
}
}Sourcefn is_read_only(&self) -> bool
fn is_read_only(&self) -> bool
Returns true if this system only reads data.
Read-only systems can potentially run in parallel with other
read-only systems. The default implementation delegates to
component_access().is_read_only().