ECS – ISystem Study (1)

참고
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

SystemStateECS의 ISystem에서 시스템의 상태를 관리하는 구조체 (현재 ISystem)

SystemHandle은 현재 실행 중인 ISystem을 식별하는 핸들

  1. 시스템에 컴포넌트를 추가하거나 읽을 때 사용
    • System에 특정한 데이터(컴포넌트)를 저장하고 싶다면 state.SystemHandle을 사용하여 추가 가능
    • 싱글턴 데이터 저장 용도로 활용 가능
  2. 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싱글턴처럼 유일한 인스턴스를 가지지만,

자체적으로 상태를 저장하지 않고 SystemStateComponentData를 사용하여 데이터를 관리한다는 점에서 차이가 있음.

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

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤