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 Component | Chunk 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을 사용하면 편리