ECS – Component concepts (3)

4. 여러 종류의 Component

(6) Chunk components

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-chunk.html

Chunk Component는 개별 엔터티(Entity)가 아닌 Chunk 단위로 데이터를 저장하는 컴포넌트

즉, Chunk 전체에 대한 공통 데이터를 저장하는 데 사용됨

1) Chunk Component의 주요 특징

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-chunk-introducing.html

  • Chunk에 데이터를 저장 → 개별 Entity가 아닌 Chunk 자체에 속하는 데이터
  • 빠른 처리 가능 → Chunk 단위로 한 번만 검사하여 성능 최적화
  • 구조적 변경(Structural Change)이 발생하지 않음
  • Shared Component와 비슷하지만, 중복 제거(Deduplication) 없음
  • Unmanaged 타입만 사용 가능

2) Shared Component와 차이점

특징Shared ComponentChunk Component
저장 위치Chunk 내의 모든 Entity가 같은 값을 공유Chunk 자체에 값이 저장됨
값 변경 시새로운 Chunk 로 이동 필요 (구조적 변경 발생)Chunk 내부에서 값 변경 가능 (구조적 변경 없음)
값 공유 방식같은 값이 있는 Chunk는 중복 제거각 Chunk 가 자신만의 값을 가짐
지원 타입Managed & Unmanaged 가능Unmanaged만 가능

Chunk Component는 구조적 변경 없이 특정 Chunk의 데이터를 조작할 때 유용

3) Chunk Component의 사용 예시

Chunk 단위로 바운딩 박스 저장

  • 각 Chunk의 AABB(바운딩 박스)를 저장하여 카메라 시야 안에 있는 Chunk만 처리 가능
  • 최적화 가능:각 Entity를 검사하는 대신 Chunk 단위로 필터링 가능
using Unity.Entities;
using Unity.Mathematics;

// Chunk 단위로 AABB (Axis-Aligned Bounding Box) 저장
public struct ChunkBounds : IComponentData
{
    public float3 Min;
    public float3 Max;
}

Chunk Component를 추가하는 시스템

  • Chunk 내의 모든 엔터티가 공유하는 바운딩 박스 업데이트 가능
  • Chunk가 카메라 안에 있는 경우만 Entity를 처리하도록 최적화 가능
using Unity.Entities;
using Unity.Mathematics;

public partial struct ChunkBoundsSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var chunk in SystemAPI.QueryBuilder().WithAll<ChunkBounds>().Build().GetChunks(ref state))
        {
            // 예제: 바운딩 박스를 업데이트하는 로직
            chunk.SetChunkComponentData(new ChunkBounds { Min = new float3(-1, -1, -1), Max = new float3(1, 1, 1) });
        }
    }
}

특정 Chunk만 처리하는 시스템

  • Chunk 단위로 화면에 보이는지 확인 후, 보이는 Chunk 만 처리
  • Chunk 내부의 모든 Entity를 개별 검사하지 않고 빠르게 필터링 가능
using Unity.Entities;
using UnityEngine;

public partial struct ChunkCullingSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        foreach (var (chunkBounds, chunk) in SystemAPI.Query<ChunkBounds>().WithChunkAccess())
        {
            if (IsChunkVisible(chunkBounds))
            {
                Debug.Log("Processing entities in visible chunk");
                // Chunk 안의 Entity 처리 로직
            }
        }
    }

    private bool IsChunkVisible(ChunkBounds bounds)
    {
        // 간단한 AABB 검사 로직 (예제)
        return bounds.Min.x < 10 && bounds.Max.x > -10;
    }
}

4) Chunk Component의 장점과 단점

장점

  • Chunk 단위로 데이터 저장 → 빠른 필터링 가능
  • 구조적 변경이 발생하지 않음 → 성능 최적화
  • 엔터티 개별 검사 없이 Chunk 레벨에서 빠른 판단 가능

단점

  • Chunk마다 별도로 값을 저장하므로 메모리 사용 증가 가능
  • 같은 값이라도 공유되지 않음 (Shared Component처럼 Deduplication 없음)
  • Managed 타입 사용 불가 (Unmanaged 타입만 가능)

5) Chunk Component 활용법

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-type.html

Chunk Component는 일반적인 IComponentData와는 다른 방식의 API를 사용하여 추가, 제거, 가져오기 및 설정을 수행해야함

특히 EntityManager의 API를 사용할 때 주의

1> Chunk Component 추가 방법

특정 Entity의 Chunk에 Chunk Component 추가

EntityManager.AddChunkComponentData<ExampleChunkComp>(entity);
  • 특정 Entity가 속한 Chunk에 Chunk Component 추가
  • 이때, Chunk Component는 해당 Chunk의 모든 Entity가 공유하게 됨
2> Chunk Component 조회(Query) 방법

Chunk Component를 포함하는 Chunk를 찾을 때 ComponentType.ChunkComponent<T> 사용

EntityQuery query = GetEntityQuery(
    typeof(ExampleComponent), 
    ComponentType.ChunkComponent<ExampleChunkComp>() // Chunk Component 쿼리
);
NativeArray<ArchetypeChunk> chunks = query.ToArchetypeChunkArray(Allocator.Temp);
  • ExampleComponent가 포함된 Chunk 중에서 ExampleChunkComp도 있는 Chunk를 찾음
3> Chunk Component 데이터 가져오기 & 설정하기

특정 Chunk의 Chunk Component 값 설정

EntityManager.SetChunkComponentData<ExampleChunkComp>(
    chunks[0], 
    new ExampleChunkComp { Value = 6 }
);
  • 첫 번째 Chunk에 대해 ExampleChunkComp 값을 6으로 설정

특정 Chunk의 Chunk Component 값 가져오기

ExampleChunkComp exampleChunkComp = EntityManager.GetChunkComponentData<ExampleChunkComp>(chunks[0]);
Debug.Log(exampleChunkComp.Value); // 6 출력
  • 첫 번째 Chunk의 ExampleChunkComp 값을 가져옴
4> Entity를 통해 Chunk Component 값 가져오기 & 설정하기

특정 Entity가 속한Chunk의 Chunk Component 값을 변경

EntityManager.SetChunkComponentData<MyChunkComp>(
    entity, 
    new MyChunkComp { Value = 6 }
);
  • 특정 Entity가 속한 Chunk의 MyChunkComp 값을 6으로 설정

특정 Entity가 속한 Chunk의 Chunk Component 값을 가져오기

MyChunkComp myChunkComp = EntityManager.GetChunkComponentData<MyChunkComp>(entity);
Debug.Log(myChunkComp.Value); // 6 출력
  • 해당 Entity가 속한 Chunk의 MyChunkComp 값을 가져옴
5> Job에서 Chunk Component 사용하기

EntityManager는 Job 내부에서 사용할 수 없으므로 ComponentTypeHandle<T>을 활용해야 한다!

IJobChunk에서 Chunk Component 접근하기

struct MyJob : IJobChunk
{
    public ComponentTypeHandle<ExampleChunkComponent> ExampleChunkCompHandle;

    public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    {
        // 청크의 Chunk Component 데이터 가져오기
        ExampleChunkComponent myChunkComp = chunk.GetChunkComponentData(ExampleChunkCompHandle);

        // 청크의 Chunk Component 데이터 설정
        chunk.SetChunkComponentData(ExampleChunkCompHandle, new ExampleChunkComponent { Value = 7 });
    }
}
  • IJobChunk 내부에서는 chunk.GetChunkComponentData()chunk.SetChunkComponentData()를 사용
  • EntityManager 없이 직접 Chunk 데이터를 조작 가능
6> Chunk Component 관련 주의사항
  • Chunk Component는 Chunk에 속하지만, 추가/제거 시 Entity의 아키타입이 변경되어 구조적 변경(Structural Change)이 발생
  • 새로 생성된 Chunk Component는 기본값(Default)으로 초기화됨
  • 읽기 전용으로 사용할 경우 ComponentType.ChunkComponentReadOnly<T>를 사용하면 Job 스케줄링 최적화 가능
정리
  • Chunk Component는 특정 Chunk 자체의 상태를 저장하는데 유용
  • EntityManager의 AddChunkComponentData(), SetChunkComponentData(), GetChunkComponentData()를 사용하여 접근
  • Job에서는 ComponentTypeHandle<T>을 사용하여 접근해야 함
  • 구조적 변경이 발생하지만, 개별 Entity가 아닌 Chunk 단위의 데이터를 관리하는 데 최적

Chunk 단위 데이터 최적화 및 효율적인 필터링에 적합

(7) Enableable Component

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-enableable-use.html

Enableable Component은 Entity의 특정 컴포넌트를 활성화/비활성화할 수 있도록 하는 기능

이는 구조적 변경(Structural Change) 없이 컴포넌트의 상태를 빠르게 변경할 수 있어, 자주 변하는 상태(State)를 다룰 때 유용

1) Enableable Component의 특징

  • IEnableableComponent을 구현한 IComponentData 또는 IBufferElementData만 사용 가능
  • 활성화/비활성화해도 아키타입(Archetype)이 변경되지 않음 → 구조적 변경 없음
  • 값을 그대로 유지한 채 비활성화 가능
  • 비활성화된 컴포넌트는 EntityQuery에서 존재하지 않는 것처럼 동작
  • 멀티스레드(Job System)에서 안전하게 활성화/비활성화 가능
  • 기본적으로 모든 Enableable Component는 활성화된 상태로 생성됨

2) Enableable Component 생성 방법

Enableable Component를 만들려면 IEnableableComponent 인터페이스를 구현해야 한다.

using Unity.Entities;

public struct Health : IComponentData, IEnableableComponent
{
    public int Value;
}
  • IEnableableComponent을 추가하면 해당 컴포넌트는 활성화/비활성화 가능
  • 일반적인 IComponentData와 동일하게 사용 가능

3) Enableable Component 활성화/비활성화 방법

특정 Entity의 Enableable Component 상태 확인

bool isEnabled = EntityManager.IsComponentEnabled<Health>(entity);
Debug.Log(isEnabled);  // true (기본적으로 활성화됨)
  • IsComponentEnabled<T>(Entity)을 사용하여 현재 컴포넌트 상태 확인 가능

특정 Entity의 Enableable Component 비활성화

EntityManager.SetComponentEnabled<Health>(entity, false);
  • SetComponentEnabled<T>(Entity, bool)을 사용하여 컴포넌트 활성화/비활성화
  • 컴포넌트를 비활성화해도 값은 그대로 유지됨

비활성화된 컴포넌트의 값 가져오기

Health h = EntityManager.GetComponentData<Health>(entity);
Debug.Log(h.Value);  // 비활성화 상태여도 값은 유지됨
  • 비활성화 상태에서도 값은 유지됨
  • 단, EntityQuery에서는 존재하지 않는 것처럼 동작

4) Enableable Component와 EntityQuery

Enableable Component가 비활성화된 경우, 기본적으로 Query에서 제외된다.

EntityQuery query = GetEntityQuery(typeof(Health), typeof(Translation));
  • Health가 비활성화된 Entity는 이 쿼리에 포함되지 않음
  • Health가 활성화된 Entity만 결과에 포함됨

Enableable Component가 비활성화된 상태에서도 포함시키는 방법

EntityQuery queryIgnoreEnableable = GetEntityQuery(new EntityQueryDesc
{
    All = new ComponentType[] { typeof(Health), typeof(Translation) },
    Options = EntityQueryOptions.IgnoreComponentEnabledState
});
  • EntityQueryOptions.IgnoreComponentEnabledState를 사용하면 비활성화된 컴포넌트도 포함
  • 구조적 변경이 발생한 경우에만 동작이 변경되므로 더 효율적

5) Job System에서 Enableable Component 사용하기

Enableable Component는 멀티스레드(Job System)에서 안전하게 활성화/비활성화 가능하다.

Job 내부에서 Enableable Component 사용하기

struct EnableableJob : IJobEntity
{
    public ComponentLookup<Health> HealthLookup;

    public void Execute(Entity entity)
    {
        if (HealthLookup.IsComponentEnabled(entity))
        {
            // Health가 활성화된 경우
            Health h = HealthLookup[entity];
            h.Value += 10;
            HealthLookup[entity] = h;
        }
    }
}
  • ComponentLookup<T>를 사용하여 Job 내부에서 안전하게 활성화/비활성화 확인 가능
  • IsComponentEnabled<T>(Entity)를 활용하여 조건부 실행 가능

6) Enableable Component 관련 주의사항

  • 비활성화된 컴포넌트는 EntityQuery에서 기본적으로 존재하지 않는 것으로 간주됨
  • 멀티스레드 환경에서 다른 Job이 같은 컴포넌트를 변경할 경우, Race Condition 발생 가능 (경쟁)
  • EntityQueryOptions.IgnoreComponentEnabledState를 사용하면 비활성화 상태도 포함 가능
  • 구조적 변경을 피하기 위해 자주 변경되는 상태(State)는 Enableable Component를 활용하는 것이 유리

정리

  • Enableable Component는 IEnableableComponent을 구현한 IComponentData 또는 IBufferElementData만 사용 가능
  • 비활성화해도 값은 유지되지만, 기본적으로 EntityQuery에서 제외됨
  • IsComponentEnabled<T>(Entity)SetComponentEnabled<T>(Entity, bool)을 사용하여 활성화/비활성화 가능
  • 구조적 변경 없이 상태(State)를 관리할 수 있어 성능 최적화에 유리
  • Job System에서도 ComponentLookup<T>를 사용하여 안전하게 활용 가능

상태 변화가 자주 발생하는 경우 Enableable Component를 사용하면 최적화에 도움

(8) Singleton Component

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/components-singleton.html

Singleton Component특정 월드(World)에서 오직 하나의 인스턴스만 존재하는 컴포넌트를 의미한다.

즉, 한 개의 Entity만 특정 컴포넌트를 가지고 있으면 Singleton Component로 간주된다.

1) Singleton Component의 특징

  • 특정 월드에서 단 하나의 인스턴스만 존재
  • 다른 Entity에 같은 컴포넌트를 추가하면 더 이상 Singleton이 아님
  • 다른 월드에 존재하는 동일한 컴포넌트는 별도로 관리됨 (다른 월드의 Singleton 상태에 영향 없음)
  • System에서 쉽게 접근할 수 있도록 Singleton 전용 API 제공
  • 서버 기반 아키텍처에서 특정 클라이언트 데이터를 관리할 때 유용

2) Singleton Component 생성 방법

Singleton Component는 일반적인 IComponentData와 동일하게 생성하지만, 오직 하나의 Entity만 해당 컴포넌트를 가지도록 관리해야 한다.

using Unity.Entities;

public struct GameSettings : IComponentData
{
    public float Gravity;
}
  • 일반적인 IComponentData처럼 정의 가능

Singleton Component 추가

EntityManager.CreateSingleton<GameSettings>();
  • CreateSingleton<T>()을 사용하면 자동으로 단 하나의 Entity에만 추가

3) Singleton Component 접근 방법

Singleton API를 사용하면 특정 Entity를 직접 찾지 않아도 단일 인스턴스를 가져올 수 있음.

Singleton Entity 가져오기

Entity singletonEntity = EntityManager.GetSingletonEntity<GameSettings>();
  • GetSingletonEntity<T>()을 사용하면 Singleton을 가진 엔터티 직접 가져오기 가능

Singleton 값 가져오기

GameSettings settings = EntityManager.GetSingleton<GameSettings>();
Debug.Log(settings.Gravity);
  • GetSingleton<T>()을 사용하면 컴포넌트 데이터 직접 가져오기 가능

Singleton 값 변경하기

GameSettings settings = EntityManager.GetSingleton<GameSettings>();
settings.Gravity = 9.8f;
EntityManager.SetSingleton(settings);
  • SetSingleton<T>()을 사용하여 값 수정 가능

Singleton 여부 확인

bool exists = EntityManager.HasSingleton<GameSettings>();
Debug.Log(exists);  // true or false
  • HasSingleton<T>()을 사용하여 Singleton이 존재하는지 확인 가능

4) Singleton API 비교

API설명
CreateSingleton<T>()Singleton Component 생성
GetSingletonEntity<T>()Singleton을 포함한 엔터티 가져오기
GetSingleton<T>()Singleton 데이터 가져오기
SetSingleton<T>()Singleton 데이터 설정하기
HasSingleton<T>()Singleton이 존재하는지 확인
TryGetSingleton<T>(out T value)Singleton을 가져오되, 없을 경우 안전하게 처리
GetSingletonRW<T>()Read/Write 가능한 참조 반환 (주의 필요)

5) Singleton Component와 SystemAPI

Singleton Component는 SystemAPI에서도 편리하게 접근 가능

SystemAPI를 이용한 Singleton 접근

public partial struct GameSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        GameSettings settings = SystemAPI.GetSingleton<GameSettings>();
        Debug.Log(settings.Gravity);
    }
}
  • SystemAPI.GetSingleton<T>()을 사용하면 더 간결한 코드 가능

6) Singleton 사용 시 주의점

Singleton API는 자동으로 Job Dependency를 해결하지 않음

  • EntityManager.GetComponentData<T>()는 자동으로 Job을 완료하지만, EntityManager.GetSingleton<T>()자동으로 Job을 완료하지 않음
  • Job이 실행 중일 때 Singleton에 접근하면 오류 발생 가능
  • 해결 방법: EntityManager.CompleteDependencyBeforeRO() 또는 CompleteDependencyBeforeRW() 사용
EntityManager.CompleteDependencyBeforeRO<GameSettings>();  
GameSettings settings = EntityManager.GetSingleton<GameSettings>();

GetSingletonRW<T>() 사용 시 주의

  • GetSingletonRW<T>()참조(Reference)를 반환하므로,
    Job과 동시에 접근하면 Race Condition 발생 가능
  • 안전한 사용 방법
    • 네이티브 컨테이너(NativeContainer) 내부 데이터만 수정
    • Jobs Debugger에서 오류가 없는지 반드시 확인

정리

  • Singleton Component는 특정 월드에서 단 하나만 존재하는 컴포넌트
  • SystemAPI 및 EntityManager를 통해 쉽게 접근 가능
  • 구조적 변경 없이 특정 데이터를 쉽게 관리할 수 있음
  • Job System과 함께 사용할 때는 Dependency 관리 필요
  • 서버 기반 시스템이나 특정 설정 값을 관리할 때 매우 유용

GameSettings, PlayerController, ServerTimestamp 등의 전역 설정을 관리할 때 Singleton을 사용하면 편리

NamespaceMethod
EntityManagerCreateSingleton
EntityQueryGetSingletonEntity
GetSingleton
GetSingletonRW
TryGetSingleton
HasSingleton
TryGetSingletonBuffer
TryGetSingletonEntity
GetSingletonBuffer
SetSingleton
SystemAPIGetSingletonEntity
GetSingleton
GetSingletonRW
TryGetSingleton
HasSingleton
TryGetSingletonBuffer
TryGetSingletonEntity
GetSingletonBuffer
SetSingleton

댓글 달기

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

위로 스크롤