Files
GhostEngine/doc/docs/systems.md
Misaki d8a7b07624 feat(graphics): improve rendering pipeline and docs
- Refactor D3D12 backend and RenderGraph module
- Update graphics RHI and core rendering components
- Add Random.hlsl shader include
- Regenerate API documentation and update user guides
2026-03-27 22:23:44 +09:00

3.0 KiB

Systems Overview

Systems are where all your ECS logic lives. They interact with data by iterating over entities that match an EntityQuery.

The ISystem Interface

At its core, a system implements ISystem, which provides three lifecycle methods:

public interface ISystem
{
    void Initialize(ref readonly SystemAPI systemAPI);
    void Update(ref readonly SystemAPI systemAPI);
    void Cleanup(ref readonly SystemAPI systemAPI);
}

The SystemAPI struct provides access to the World and TimeData for the current frame.

SystemBase

For most gameplay code, you should inherit from the abstract class SystemBase, which provides convenient wrappers and handles "Should Update" logic automatically.

public class MovementSystem : SystemBase
{
    private Identifier<EntityQuery> _query;

    protected override void OnInitialize(ref readonly SystemAPI systemAPI)
    {
        // Build a query for entities that have both Position and Velocity
        _query = new QueryBuilder()
            .WithAllRW<Position>()
            .WithAll<Velocity>()
            .Build(World);

        // Tell the system base to ONLY run OnUpdate if this query has at least 1 entity
        RequireQueryForUpdate(_query);
    }

    protected override void OnUpdate(ref readonly SystemAPI systemAPI)
    {
        float dt = systemAPI.Time.DeltaTime;
        
        ref var query = ref World.ComponentManager.GetEntityQueryReference(_query);
        
        foreach (var chunk in query.GetChunkIterator())
        {
            var positions = chunk.GetComponentDataRW<Position>();
            var velocities = chunk.GetComponentData<Velocity>();

            for (int i = 0; i < chunk.EntityCount; i++)
            {
                positions[i].Value += velocities[i].Value * dt;
            }
        }
    }
}

Automatic Update Triggers

Notice the RequireQueryForUpdate(_query) call. SystemBase will skip calling OnUpdate if the query results are completely empty, saving processing time. SystemBase also invokes OnStartRunning and OnStopRunning when the system transitions between having matching entities and not.

System Execution Order

By default, systems execute in the order they are added to a SystemGroup. To enforce execution order regardless of initialization sequence, use the [UpdateAfter] and [UpdateBefore] attributes.

[UpdateAfter(typeof(PhysicsSystem))]
[UpdateBefore(typeof(RenderSystem))]
public class MovementSystem : SystemBase
{
    // ...
}

When you call group.SortSystems(), the SystemGroup uses Kahn's algorithm to perform a topological sort and resolves these dependencies. If circular dependencies are detected, the engine will throw an exception.

System Groups

SystemGroups implement ISystem themselves, meaning you can nest groups within groups to build complex hierarchical update loops (e.g. FixedUpdateGroup, PresentationGroup). GhostEngine provides a DefaultSystemGroup that is automatically instantiated by the SystemManager.