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); } }