ECS Guide
Learn how to use the Entity Component System in R-Type.
What is ECS?
The Entity Component System (ECS) is an architectural pattern that separates data from behavior:
- Entities are unique identifiers
- Components are data containers
- Systems implement behavior
Creating Entities
#include "ecs/src/ECS.hpp"
using namespace ECS;
Registry registry;
// Create a new entity
Entity player = registry.spawnEntity();
Adding Components
Components are plain data structures that can be attached to entities:
// Define a component
struct PositionComponent {
float x;
float y;
};
// Add component to entity (components are auto-registered on first use)
registry.emplaceComponent<PositionComponent>(player, 100.0f, 200.0f);
Creating Systems
Systems operate on entities that have specific components:
class MovementSystem {
public:
void update(Registry& registry, float deltaTime) {
// Get all entities with Position and Velocity
auto view = registry.view<PositionComponent, VelocityComponent>();
for (auto entity : view) {
auto& pos = registry.getComponent<PositionComponent>(entity);
auto& vel = registry.getComponent<VelocityComponent>(entity);
// Update position based on velocity
pos.x += vel.x * deltaTime;
pos.y += vel.y * deltaTime;
}
}
};
Querying Components
// Check if entity has a component
if (registry.hasComponent<PositionComponent>(entity)) {
auto& pos = registry.getComponent<PositionComponent>(entity);
// Use position...
}
// Remove a component
registry.removeComponent<PositionComponent>(entity);
// Kill an entity
registry.killEntity(entity);
Example: Creating a Spaceship
// Create entity
Entity spaceship = registry.spawnEntity();
// Add components
registry.emplaceComponent<PositionComponent>(spaceship, 400.0f, 300.0f);
registry.emplaceComponent<VelocityComponent>(spaceship, 0.0f, 0.0f);
registry.emplaceComponent<SpriteComponent>(spaceship, "spaceship.png");
registry.emplaceComponent<HealthComponent>(spaceship, 100);
registry.emplaceComponent<PlayerControlledComponent>(spaceship);
Best Practices
Keep Components Simple
- Components should be plain data structures
- No logic in components
- Prefer composition over inheritance
System Independence
- Systems should not depend on each other
- Use the Registry as the communication layer
- Systems can run in any order (with exceptions for dependencies)
Component Granularity
- Small, focused components are better than large ones
- Easier to reuse and combine
- Better cache performance
Common Patterns
Tagging Components
Empty components used as flags:
struct EnemyTag {};
struct PlayerTag {};
registry.emplace_component<EnemyTag>(enemy);
Optional Components
Check for component existence to enable optional behavior:
if (registry.has_component<AIComponent>(entity)) {
// This entity has AI
}
Component Events
Use callbacks to react to component changes:
registry.on_component_added<HealthComponent>([](Entity entity, HealthComponent& health) {
// Initialize health bar UI
});
Performance Tips
- Batch Operations: Process all entities of the same type together
- Cache Components: Store references when accessing multiple times
- Minimize Component Size: Smaller components = better cache utilization
- Use Sparse Sets: The Registry uses sparse sets for efficient storage
API Reference
For detailed API documentation, see the API Reference.
Next Steps
- Read about Network Synchronization
- Learn how to create Custom Systems
- Explore Component Examples