AgentSkillsCN

ecs-system-lifecycle-manager

创建遵循标准生命周期的ISystem系统(OnCreate、OnUpdate、OnDestroy)、系统分组排序以及查询管理功能。当用户需要创建系统、处理游戏逻辑、安排任务,或管理实体生命周期时,可使用此功能。

SKILL.md
--- frontmatter
name: ecs-system-lifecycle-manager
description: Tạo các System kế thừa ISystem với lifecycle chuẩn (OnCreate, OnUpdate, OnDestroy), system group ordering, và query management. Dùng khi user yêu cầu tạo system, xử lý game logic, schedule job, hoặc quản lý entity lifecycle.

ECS System Lifecycle Manager

Critical Instructions

  • ISystem over SystemBase: Luôn dùng ISystem (unmanaged) thay vì SystemBase (managed). SystemBase chỉ dùng khi bắt buộc (vd: cần managed component).
  • Burst Compile: MỌI ISystem PHẢI có [BurstCompile] trên cả struct lẫn các method.
  • Project Convention: Đặt file vào Assets/Scripts/Core/Systems/ cho core systems, hoặc folder module tương ứng.
  • Dependency Chain: Luôn dùng state.Dependency để chain job handles. KHÔNG gọi .Complete() trừ khi bắt buộc.

Core Principles

  • Single Responsibility: Mỗi System làm đúng 1 việc. Tách logic phức tạp thành nhiều System.
  • Query Caching: Build EntityQuery trong OnCreate, cache vào field, reuse trong OnUpdate.
  • RequireForUpdate: Dùng state.RequireForUpdate<T>() để System chỉ chạy khi có Entity phù hợp.
  • System Group: Đặt System vào đúng group (InitializationSystemGroup, SimulationSystemGroup, PresentationSystemGroup).

System Templates

1. Standard ISystem (Full Lifecycle)

csharp
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;

/// <summary>
/// [Mô tả System xử lý gì, thuộc pipeline nào]
/// </summary>
[BurstCompile]
[UpdateInGroup(typeof(SimulationSystemGroup))]
public partial struct ExampleSystem : ISystem
{
    private EntityQuery _query;

    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        // Chỉ chạy khi có Entity có ExampleComponent
        state.RequireForUpdate<ExampleComponent>();

        // Cache query phức tạp
        _query = SystemAPI.QueryBuilder()
            .WithAll<ExampleComponent>()
            .WithNone<DisabledTag>()
            .Build();
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        float dt = SystemAPI.Time.DeltaTime;

        // Schedule job
        var job = new ExampleJob
        {
            DeltaTime = dt
        };

        state.Dependency = job.ScheduleParallel(state.Dependency);
    }

    [BurstCompile]
    public void OnDestroy(ref SystemState state)
    {
        // Giải phóng NativeContainer nếu có
    }
}

2. System with EntityCommandBuffer (Structural Changes)

csharp
/// <summary>
/// System thực hiện structural changes (add/remove component, create/destroy entity)
/// thông qua EntityCommandBuffer để tránh sync point.
/// </summary>
[BurstCompile]
[UpdateInGroup(typeof(SimulationSystemGroup))]
public partial struct SpawnerSystem : ISystem
{
    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        state.RequireForUpdate<SpawnRequest>();
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
        var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);

        foreach (var (request, entity) in
            SystemAPI.Query<RefRO<SpawnRequest>>().WithEntityAccess())
        {
            var spawned = ecb.Instantiate(request.ValueRO.Prefab);
            ecb.SetComponent(spawned, new LocalTransform
            {
                Position = request.ValueRO.Position,
                Rotation = quaternion.identity,
                Scale = 1f
            });

            // Consume request
            ecb.DestroyEntity(entity);
        }
    }
}

3. System with Singleton Access

csharp
/// <summary>
/// System đọc/ghi singleton data (vd: GlobalVoxelMap, GameConfig).
/// </summary>
[BurstCompile]
[UpdateInGroup(typeof(SimulationSystemGroup))]
public partial struct SingletonReaderSystem : ISystem
{
    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        state.RequireForUpdate<WorldData>();
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        ref var worldData = ref SystemAPI.GetSingletonRW<WorldData>().ValueRW;

        // Đọc/ghi worldData...
    }
}

4. System with Manual Query (IJobChunk)

csharp
/// <summary>
/// System dùng IJobChunk cho logic phức tạp cần truy cập chunk trực tiếp.
/// </summary>
[BurstCompile]
[UpdateInGroup(typeof(SimulationSystemGroup))]
public partial struct AdvancedProcessingSystem : ISystem
{
    private EntityQuery _query;
    private ComponentTypeHandle<ExampleComponent> _exampleHandle;
    private ComponentTypeHandle<LocalTransform> _transformHandle;

    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        _query = SystemAPI.QueryBuilder()
            .WithAllRW<ExampleComponent>()
            .WithAll<LocalTransform>()
            .Build();
        state.RequireForUpdate(_query);
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        _exampleHandle = SystemAPI.GetComponentTypeHandle<ExampleComponent>();
        _transformHandle = SystemAPI.GetComponentTypeHandle<LocalTransform>(true);

        var job = new ChunkProcessJob
        {
            ExampleHandle = _exampleHandle,
            TransformHandle = _transformHandle
        };

        state.Dependency = job.ScheduleParallel(_query, state.Dependency);
    }
}

System Ordering & Groups

Built-in Groups (Execution Order)

code
InitializationSystemGroup     ← Setup, spawn, input reading
  └─ BeginInitializationECBSystem
SimulationSystemGroup         ← Game logic, physics, AI
  ├─ BeginSimulationECBSystem
  ├─ FixedStepSimulationSystemGroup  ← Physics tick
  └─ EndSimulationECBSystem
PresentationSystemGroup       ← Rendering, audio, UI
  └─ BeginPresentationECBSystem

Custom Group

csharp
[UpdateInGroup(typeof(SimulationSystemGroup))]
[UpdateAfter(typeof(PhysicsSystemGroup))]
public partial class VoxelSimulationGroup : ComponentSystemGroup { }

Ordering Attributes

csharp
[UpdateInGroup(typeof(SimulationSystemGroup))]  // Nhóm cha
[UpdateBefore(typeof(RenderingSystem))]          // Chạy trước
[UpdateAfter(typeof(InputSystem))]               // Chạy sau
[CreateBefore(typeof(OtherSystem))]              // Khởi tạo trước

Project-Specific Patterns (VisualWorld)

Suggested System Pipeline

code
SimulationSystemGroup
├── InputProcessingSystem          ← Đọc input, tạo SpawnRequest/ActionRequest
├── VoxelSimulationGroup (custom)
│   ├── ThermodynamicsSystem       ← CA nhiệt, phase change
│   ├── FluidSimulationSystem      ← Dòng chảy chất lỏng
│   └── StructuralIntegritySystem  ← BFS stress, collapse detection
├── PhysicsResponseSystem          ← Tách entity rơi, debris spawn
└── ChunkDirtyFlagSystem           ← Đánh dấu chunk cần rebuild mesh

PresentationSystemGroup
├── MeshRebuildSystem              ← Greedy mesh cho dirty chunks
└── LODSystem                      ← Cập nhật LOD theo camera

WorldData Singleton Pattern

csharp
[BurstCompile]
[UpdateInGroup(typeof(SimulationSystemGroup), OrderFirst = true)]
public partial struct WorldDataInitSystem : ISystem
{
    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        var entity = state.EntityManager.CreateEntity();
        state.EntityManager.AddComponentData(entity, new WorldData
        {
            // Initialize NativeParallelHashMap...
        });
    }
}

Generation Workflow

  1. Xác định trách nhiệm → System làm 1 việc gì?
  2. Chọn System Group → Initialization / Simulation / Presentation
  3. Xác định ordering → UpdateBefore/After nào?
  4. Chọn pattern → Foreach query / IJobEntity / IJobChunk / ECB
  5. Implement OnCreate → RequireForUpdate, cache query
  6. Implement OnUpdate → Schedule job hoặc inline foreach
  7. Implement OnDestroy → Dispose NativeContainer
  8. Đặt fileAssets/Scripts/Core/Systems/{SystemName}System.cs

Validation Checklist

  • [BurstCompile] trên struct VÀ các method
  • [UpdateInGroup] xác định rõ group
  • RequireForUpdate cho mọi dependency
  • Không gọi .Complete() trừ khi đọc kết quả ngay
  • EntityQuery được cache trong OnCreate
  • NativeContainer được Dispose trong OnDestroy
  • ECB sử dụng đúng thời điểm (Begin/End)
  • state.Dependency được assign sau mỗi job schedule
  • Có XML documentation mô tả pipeline position

Anti-Patterns (TRÁNH)

csharp
// ❌ SAI: Dùng SystemBase
public partial class BadSystem : SystemBase { }

// ❌ SAI: Quên BurstCompile
public partial struct BadSystem2 : ISystem { }

// ❌ SAI: Complete() không cần thiết → sync point → giết perf
public void OnUpdate(ref SystemState state)
{
    var job = new MyJob { }.Schedule(state.Dependency);
    job.Complete(); // ← KHÔNG LÀM THẾ NÀY
}

// ❌ SAI: Query mỗi frame thay vì cache
public void OnUpdate(ref SystemState state)
{
    var query = state.GetEntityQuery(typeof(Foo)); // ← Cache trong OnCreate!
}