This guide explains the fundamental concepts of Entity Component Systems (ECS) and how they are implemented in Coyote ECS.
An Entity Component System (ECS) is a software architectural pattern that is commonly used in game development and simulation software. It is based on the principle of composition over inheritance and focuses on data-oriented design.
Entities are the basic units in an ECS. They are essentially just IDs that can have components attached to them. In Coyote ECS, entities are created through the world:
var entity = try world.entities.create();
Components are pure data structures that define the properties and state of entities. They contain no logic or behavior. In Coyote ECS, components are defined as Zig structs:
pub const Components = struct {
pub const Position = struct {
x: f32 = 0,
y: f32 = 0,
};
};
Systems contain the logic and behavior of your application. They operate on entities that have specific components. In Coyote ECS, systems are just functions that take a world as a parameter:
pub fn UpdatePosition(world: *World) void {
var it = world.entities.iteratorFilter(Components.Position);
while(it.next()) |entity| {
// Update position logic here
}
}
The world is the container that manages all entities, components, and systems. It provides the interface for creating and managing the ECS structure:
var world = try World.create();
defer world.deinit();
Components are created through the world’s component manager:
var component = try world.components.create(Components.Position);
Components are attached to entities using the attach
method:
try entity.attach(component, Components.Position{ .x = 0, .y = 0 });
Components can be detached from entities:
entity.detach(component);
When components are no longer needed, they should be destroyed:
component.destroy();
Entities are created through the world:
var entity = try world.entities.create();
Entities can be destroyed when they are no longer needed:
entity.destroy();
Iterate over all components of a specific type:
var it = world.components.iteratorFilter(Components.Position);
while(it.next()) |component| {
// Work with the component
}
Iterate over entities with specific components:
var it = world.entities.iteratorFilter(Components.Position);
while(it.next()) |entity| {
// Work with the entity
}
Access components attached to an entity:
if(entity.getOneComponent(Components.Position)) |position| {
// Work with the position component
}
Systems are executed using the Systems.run
function:
try Systems.run(UpdatePosition, .{world});
Coyote ECS handles memory management automatically, but you should:
defer world.deinit()
after creating a worldworld.components.gc();