using Unity.Burst;
using Unity.Entities;
public struct SystemCounterData : IComponentData
{
public int Value;
}
public partial struct CounterSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.EntityManager.AddComponentData(state.SystemHandle, new SystemCounterData { Value = 0 });
}
// [BurstCompile] 속성은 해당 메서드는 Burst 컴파일러에 의해 최적화 시켜준다.
// 간단한 연산은 안해도 상관없음.
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// 시스템 엔티티(SystemHandle)에서 데이터 가져오기
var counter = SystemAPI.GetComponent<SystemCounterData>(state.SystemHandle);
counter.Value++;
// 값 업데이트
SystemAPI.SetComponent(state.SystemHandle, counter);
}
}

state.SystemHandle
SystemState는 ECS의 ISystem에서 시스템의 상태를 관리하는 구조체 (현재 ISystem)
SystemHandle은 현재 실행 중인 ISystem을 식별하는 핸들
- 시스템에 컴포넌트를 추가하거나 읽을 때 사용
System에 특정한 데이터(컴포넌트)를 저장하고 싶다면state.SystemHandle을 사용하여 추가 가능- 싱글턴 데이터 저장 용도로 활용 가능
- ECS에서 시스템 자체를 엔터티처럼 취급
EntityManager를 이용해 해당 시스템을 관리할 수 있음
SystemState를 통해 현재 시스템이 속한 월드(World)의 EntityManager에 접근
Q. 그렇다면 ISystem은 싱글턴(Singleton)?
ISystem은 일반적인 의미의 싱글턴(Singleton)은 아니다.
하지만 각 ISystem은 ECS 내에서 하나의 시스템 인스턴스로만 존재하기 때문에 “싱글턴과 유사한 개념”으로 볼 수 있다.
ISystem이 일반적인 싱글턴 패턴과 다른 점
| 특성 | 일반적인 싱글턴 패턴 | ECS의 ISystem |
|---|---|---|
| 인스턴스 개수 | 1개 | 1개 |
| 전역 접근 | Instance 프로퍼티를 통해 접근 | World.GetExistingSystem<T>()로 접근 |
| 상태 저장 | 내부 필드로 데이터 저장 | 상태는 SystemState 또는 IComponentData로 관리 |
| 생성 방식 | 수동으로 생성 (new Singleton()) | World에서 자동 생성 |
| 삭제 시점 | 명시적으로 제거 | World이 해제될 때 제거 |
ISystem은 싱글턴처럼 유일한 인스턴스를 가지지만,
자체적으로 상태를 저장하지 않고 SystemState나 ComponentData를 사용하여 데이터를 관리한다는 점에서 차이가 있음.
ISystem 업데이트 순서

기본적으로 Update의 Simulation System Group에서 실행되고 있음.
SimulationSystemGroup에서 가장 먼저 실행되도록 설정
// SimulationSystemGroup에서 가장 먼저 실행되도록 설정 [UpdateInGroup(typeof(SimulationSystemGroup), OrderFirst = true)]


SimulationSystemGroup에서 가장 나중에 실행 되도록 설정
SimulationSystemGroup에서 가장 나중에 실행 되도록 설정 [UpdateInGroup(typeof(SimulationSystemGroup), OrderLast = true)]


Companion Game Object Update Transform System의 업데이트 전에 실행되도록 변경

// 같은 시스템 그룹 내에서만 적용 가능 // Companion Game Object Update Transform System의 업데이트 전에 실행되도록 변경 [UpdateBefore(typeof(CompanionGameObjectUpdateTransformSystem))]


연습
using Unity.Burst;
using Unity.Entities;
public struct SystemCounterData : IComponentData
{
public int Value;
}
[UpdateInGroup(typeof(SimulationSystemGroup))]
public partial struct CounterSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<SystemCounterData>();
state.EntityManager.AddComponentData(state.SystemHandle, new SystemCounterData { Value = 0 });
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// 시스템 엔티티(SystemHandle)에서 데이터 가져오기
var counter = SystemAPI.GetComponent<SystemCounterData>(state.SystemHandle);
counter.Value++;
// 값 업데이트
SystemAPI.SetComponent(state.SystemHandle, counter);
}
}
public struct CounterSystemEntity : IComponentData
{
public Entity SystemEntity;
}
[CreateAfter(typeof(CounterSystem))]
[UpdateAfter(typeof(CounterSystem))]
public partial struct CounterSystemRef : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
if (SystemAPI.TryGetSingletonEntity<SystemCounterData>(out Entity entity))
{
state.EntityManager.AddComponentData(state.SystemHandle, new CounterSystemEntity()
{
SystemEntity = entity
});
}
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// CounterSystemEntity 컴포넌트가 존재하는지 확인
if (!SystemAPI.HasComponent<CounterSystemEntity>(state.SystemHandle))
return;
// SystemCounterData가 있는 엔티티 가져오기
var counterEntity = SystemAPI.GetComponent<CounterSystemEntity>(state.SystemHandle).SystemEntity;
// 해당 엔티티에 SystemCounterData가 있는지 확인
if (!SystemAPI.HasComponent<SystemCounterData>(counterEntity))
return;
// SystemCounterData 가져오기
var counterData = SystemAPI.GetComponent<SystemCounterData>(counterEntity);
}
}


