ECS System Lifecycle Manager
Critical Instructions
- •ISystem over SystemBase: Luôn dùng
ISystem(unmanaged) thay vìSystemBase(managed).SystemBasechỉ dùng khi bắt buộc (vd: cần managed component). - •Burst Compile: MỌI
ISystemPHẢ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
EntityQuerytrongOnCreate, cache vào field, reuse trongOnUpdate. - •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
- •Xác định trách nhiệm → System làm 1 việc gì?
- •Chọn System Group → Initialization / Simulation / Presentation
- •Xác định ordering → UpdateBefore/After nào?
- •Chọn pattern → Foreach query / IJobEntity / IJobChunk / ECB
- •Implement OnCreate → RequireForUpdate, cache query
- •Implement OnUpdate → Schedule job hoặc inline foreach
- •Implement OnDestroy → Dispose NativeContainer
- •Đặt file →
Assets/Scripts/Core/Systems/{SystemName}System.cs
Validation Checklist
- •
[BurstCompile]trên struct VÀ các method - •
[UpdateInGroup]xác định rõ group - •
RequireForUpdatecho 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!
}