Unity ECS & GameObject 통합
Unity ECS (Entity Component System) - GameObjects와의 통합 및 마이그레이션
섹션 제목: “Unity ECS (Entity Component System) - GameObjects와의 통합 및 마이그레이션”Unity 6.4부터 Entity Component System(ECS)가 코어 엔진 패키지로 통합되면서, 기존의 GameObject 기반 프로젝트와 ECS를 함께 사용할 수 있게 되었습니다. 이 문서는 ECS의 핵심 개념, GameObject와의 통합 방법, 그리고 실무 적용 전략을 다룹니다.
1. ECS 기본 개념
섹션 제목: “1. ECS 기본 개념”1.1 Entity-Component-System 아키텍처
섹션 제목: “1.1 Entity-Component-System 아키텍처”ECS는 데이터 지향적 설계(Data-Oriented Design) 패턴으로, 세 가지 핵심 개념으로 구성됩니다:
- Entity (엔티티): 게임 객체의 정체성을 나타내는 고유 ID. 데이터 컨테이너 역할을 하며, 자체적으로 동작을 가지지 않습니다.
- Component (컴포넌트): 엔티티에 붙어있는 데이터 구조체. ECS 컴포넌트는 객체지향의 MonoBehaviour와 달리, 순수한 데이터만 저장합니다.
- System (시스템): 컴포넌트의 데이터를 읽고 처리하는 로직. 특정 컴포넌트 조합을 가진 엔티티들을 쿼리하여 배치 처리합니다.
1.2 GameObject와의 차이점
섹션 제목: “1.2 GameObject와의 차이점”| 항목 | GameObject | ECS Entity |
|---|---|---|
| 구조 | 객체지향 (OOP) | 데이터 지향 (Data-Oriented) |
| MonoBehaviour | 데이터 + 동작 혼재 | 데이터만 보유 |
| 처리 방식 | 개별 Update() 호출 | 배치 처리 (시스템) |
| 메모리 배치 | 분산 | 선형 (Cache-friendly) |
| 성능 | 중소 규모 | 대규모 처리에 최적 |
1.3 ECS의 핵심 장점
섹션 제목: “1.3 ECS의 핵심 장점”- 성능 향상: 선형 메모리 배치로 CPU 캐시 효율성 증대
- 확장성: 수천 개의 엔티티도 효율적으로 처리
- 결정론적 실행: 시스템 실행 순서가 명확하여 디버깅이 용이
- 병렬 처리: C# Job System과 Burst Compiler를 활용한 고성능 멀티스레딩
- 메모리 효율: 불필요한 메모리 오버헤드 제거
2. GameObject와 ECS의 통합 전략
섹션 제목: “2. GameObject와 ECS의 통합 전략”2.1 Conversion 시스템
섹션 제목: “2.1 Conversion 시스템”기존의 GameObject 기반 프로젝트를 ECS로 마이그레이션하는 가장 실용적인 방식은 Conversion 시스템입니다:
// GameObject를 Editor에서 설계한 후, 런타임에 ECS 엔티티로 변환// 이 과정은 GameObjectConversionSystem이 자동으로 처리Conversion 과정:
- Scene에 GameObject를 배치
- 특정 Component를 GameObject에 추가 (예:
IConvertGameObjectToEntity) - 게임 실행 시 ConversionSystem이 자동으로 GameObject를 ECS 엔티티로 변환
- 변환된 엔티티는 ECS 시스템에 의해 관리됨
2.2 Hybrid ECS (GameObject + Entity 혼용)
섹션 제목: “2.2 Hybrid ECS (GameObject + Entity 혼용)”Unity 6.4부터는 하이브리드 모드에서 GameObject와 ECS 엔티티를 동시에 사용할 수 있습니다:
using Unity.Entities;using UnityEngine;
// GameObject는 그대로 사용public class PlayerController : MonoBehaviour{ void Update() { // 기존 GameObject 로직 }}
// 동시에 ECS 엔티티도 사용 가능public partial struct PlayerMovementSystem : ISystem{ [BurstCompile] public void OnUpdate(ref SystemState state) { // ECS 기반 대량 데이터 처리 }}이점:
- 기존 코드 유지 가능
- 성능이 필요한 부분부터 ECS로 마이그레이션
- 점진적 전환 가능
2.3 Unified Transforms (통합 트랜스폼)
섹션 제목: “2.3 Unified Transforms (통합 트랜스폼)”Unity 6.4의 큰 변화는 ECS와 GameObject의 트랜스폼을 통합하는 것입니다:
- ECS 엔티티의
LocalTransform컴포넌트와 GameObject의Transform컴포넌트가 동기화 - GameObject 계층 구조를 ECS 엔티티 계층으로 직접 매핑 가능
Parent컴포넌트를 통한 엔티티 간 부모-자식 관계 구현
3. ECS 핵심 API 및 구현
섹션 제목: “3. ECS 핵심 API 및 구현”3.1 LocalTransform 컴포넌트
섹션 제목: “3.1 LocalTransform 컴포넌트”LocalTransform은 엔티티의 상대적 위치, 회전, 스케일을 정의합니다:
using Unity.Transforms;using Unity.Mathematics;
// LocalTransform 생성LocalTransform transform = LocalTransform.FromPosition(1, 2, 3);
// 속성에 직접 접근float3 position = transform.Position;quaternion rotation = transform.Rotation;float scale = transform.Scale;
// 변환 적용 (함수형 - 새로운 값 반환)transform = transform.RotateZ(math.radians(45f));transform.Position += math.up();중요: ECS 트랜스폼은 값 타입이므로, 변경 후 반드시 할당해야 합니다.
3.2 Parent 컴포넌트와 계층 구조
섹션 제목: “3.2 Parent 컴포넌트와 계층 구조”엔티티 계층을 구성하려면:
using Unity.Entities;using Unity.Transforms;
// 자식 엔티티에 Parent 컴포넌트 추가ecb.AddComponent(childEntity, new Parent { Value = parentEntity });
// ParentSystem이 자동으로 실행되어 계층 관계 유지// 주의: 계층이 크면 성능이 저하될 수 있음성능 최적화 팁:
- 너무 깊은 계층 피하기
- 정적 엔티티는 플래그로 표시하여 캐시 효율성 증대
- 여러 개의 루트 레벨 트랜스폼으로 분산
3.3 JobEntity를 사용한 대량 처리
섹션 제목: “3.3 JobEntity를 사용한 대량 처리”using Unity.Entities;using Unity.Transforms;using Unity.Burst;
[BurstCompile]public partial struct MoveSystem : ISystem{ [BurstCompile] public void OnUpdate(ref SystemState state) { new MoveJob().ScheduleParallel(); }
[BurstCompile] private partial struct MoveJob : IJobEntity { public void Execute(ref LocalTransform transform) { // 모든 LocalTransform 컴포넌트를 병렬로 처리 transform.Position.y += 1.0f; } }}4. GameObject에서 ECS로의 마이그레이션 가이드
섹션 제목: “4. GameObject에서 ECS로의 마이그레이션 가이드”4.1 단계별 마이그레이션 전략
섹션 제목: “4.1 단계별 마이그레이션 전략”Phase 1: 분석 및 계획
- 기존 MonoBehaviour의 역할 분석
- ECS 컴포넌트 구조 설계
- 성능 이득이 큰 부분 우선 선정
Phase 2: 하이브리드 모드 도입
- ECS 패키지 설치
- 핵심 로직부터 ECS 시스템으로 재작성
- GameObject와 ECS 엔티티 간 상호작용 구현
Phase 3: 점진적 전환
- 한 번에 하나의 기능 단위로 전환
- 각 단계마다 테스트
- 성능 프로파일링으로 이득 확인
Phase 4: 완전 ECS 전환
- 모든 핵심 로직을 ECS 기반으로 구현
- GameObject 의존성 제거
- 성능 최적화
4.2 일반적인 마이그레이션 패턴
섹션 제목: “4.2 일반적인 마이그레이션 패턴”MonoBehaviour 로직 → ECS
// Before: MonoBehaviour 기반public class EnemyController : MonoBehaviour{ private float speed = 5f;
void Update() { transform.position += Vector3.forward * speed * Time.deltaTime; }}
// After: ECS 기반public struct EnemySpeed : IComponentData{ public float Value;}
[BurstCompile]public partial struct EnemyMovementSystem : ISystem{ [BurstCompile] public void OnUpdate(ref SystemState state) { float deltaTime = SystemAPI.Time.DeltaTime;
foreach (var (transform, speed) in SystemAPI.Query<RefRW<LocalTransform>, RefRO<EnemySpeed>>()) { transform.ValueRW.Position += new float3(0, 0, 1) * speed.ValueRO.Value * deltaTime; } }}5. 실전 예제: 플레이어와 적 시스템
섹션 제목: “5. 실전 예제: 플레이어와 적 시스템”5.1 컴포넌트 정의
섹션 제목: “5.1 컴포넌트 정의”using Unity.Entities;using Unity.Mathematics;
// 플레이어 태그public struct Player : IComponentData {}
// 적 태그public struct Enemy : IComponentData {}
// 속도 데이터public struct Velocity : IComponentData{ public float3 Value;}
// 체력 데이터public struct Health : IComponentData{ public int CurrentHP; public int MaxHP;}
// 공격력 데이터public struct Damage : IComponentData{ public float Value;}5.2 움직임 시스템
섹션 제목: “5.2 움직임 시스템”using Unity.Burst;using Unity.Entities;using Unity.Transforms;
[BurstCompile][UpdateInGroup(typeof(SimulationSystemGroup))]public partial struct MovementSystem : ISystem{ [BurstCompile] public void OnUpdate(ref SystemState state) { float deltaTime = SystemAPI.Time.DeltaTime;
new MovementJob { DeltaTime = deltaTime } .ScheduleParallel(); }
[BurstCompile] private partial struct MovementJob : IJobEntity { public float DeltaTime;
private void Execute(ref LocalTransform transform, in Velocity velocity) { transform.Position += velocity.Value * DeltaTime; } }}5.3 체력 시스템
섹션 제목: “5.3 체력 시스템”using Unity.Burst;using Unity.Entities;
[BurstCompile]public partial struct HealthSystem : ISystem{ private EntityQuery destroyQuery;
[BurstCompile] public void OnUpdate(ref SystemState state) { var ecb = new EntityCommandBuffer(Allocator.Temp);
foreach (var (health, entity) in SystemAPI.Query<RefRW<Health>>().WithEntityAccess()) { if (health.ValueRO.CurrentHP <= 0) { ecb.DestroyEntity(entity); } }
ecb.Playback(state.EntityManager); }}6. 성능 최적화 팁
섹션 제목: “6. 성능 최적화 팁”6.1 메모리 레이아웃 최적화
섹션 제목: “6.1 메모리 레이아웃 최적화”- 컴포넌트 크기 최소화: 필요한 데이터만 저장
- 캐시 효율성: 자주 함께 접근하는 컴포넌트를 가까이 배치
- 정적 컴포넌트 표시:
IEnableableComponent로 활성/비활성 관리
6.2 시스템 순서 최적화
섹션 제목: “6.2 시스템 순서 최적화”// 시스템 그룹을 활용한 실행 순서 제어[UpdateInGroup(typeof(SimulationSystemGroup))]public partial struct MySystem : ISystem { }6.3 Burst Compilation 활용
섹션 제목: “6.3 Burst Compilation 활용”// [BurstCompile] 특성으로 성능 극대화[BurstCompile]public partial struct OptimizedSystem : ISystem{ // Burst로 컴파일되는 C# 코드 // C# Job System 사용 가능}7. 주의사항 및 일반적인 함정
섹션 제목: “7. 주의사항 및 일반적인 함정”7.1 일반적인 실수
섹션 제목: “7.1 일반적인 실수”- 변환 후 할당 누락:
transform.Position변경 후 할당 필수 - 부모 컴포넌트 누락: 계층 구조가 필요하면 반드시
Parent추가 - 시스템 실행 순서 문제: 의존성이 있는 시스템들은 올바른 순서로 실행되어야 함
- 메모리 누수: EntityCommandBuffer를 제때 해제하지 않으면 누수 발생
7.2 디버깅 팁
섹션 제목: “7.2 디버깅 팁”// Entity 디버깅 출력Debug.Log($"Entity: {entity}");
// 컴포넌트 확인if (SystemAPI.HasComponent<Health>(entity)){ var health = SystemAPI.GetComponent<Health>(entity); Debug.Log($"Health: {health.CurrentHP}/{health.MaxHP}");}8. 공식 자료 및 커뮤니티
섹션 제목: “8. 공식 자료 및 커뮤니티”8.1 공식 문서
섹션 제목: “8.1 공식 문서”8.2 학습 자료
섹션 제목: “8.2 학습 자료”- Unity Learn: “Basics of DOTS: Jobs and Entities”
- Entity Component System Samples (GitHub)
- HelloCube 튜토리얼
8.3 커뮤니티 지원
섹션 제목: “8.3 커뮤니티 지원”- Unity Discussions - Entities 카테고리
- Official Unity Discord - DOTS 채널
Unity 6.4의 ECS 코어 패키지 통합으로 GameObject와 ECS를 함께 사용할 수 있게 되었습니다. 기존의 객체지향 게임 개발 방식에서 데이터 지향적 설계로 점진적으로 전환할 수 있으며, 이를 통해 성능, 확장성, 그리고 유지보수성을 크게 향상시킬 수 있습니다.
성능이 중요한 프로젝트라면 ECS 도입을 강력히 권장하며, 단계적 마이그레이션을 통해 리스크를 최소화할 수 있습니다.
9. ComponentLookup — 병렬 Job에서 다른 엔티티 읽기
섹션 제목: “9. ComponentLookup — 병렬 Job에서 다른 엔티티 읽기”SystemAPI.Query는 현재 엔티티 집합만 처리하지만, ComponentLookup<T>를 사용하면 Job 내부에서 임의 엔티티의 컴포넌트를 참조할 수 있습니다.
[BurstCompile]public partial struct DamageSystem : ISystem{ // 읽기 전용 룩업 — 병렬 실행 허용 [ReadOnly] ComponentLookup<Health> _healthLookup;
public void OnCreate(ref SystemState state) { _healthLookup = state.GetComponentLookup<Health>(isReadOnly: true); }
[BurstCompile] public void OnUpdate(ref SystemState state) { _healthLookup.Update(ref state);
new ApplyDamageJob { HealthLookup = _healthLookup }.ScheduleParallel(); }
[BurstCompile] private partial struct ApplyDamageJob : IJobEntity { [ReadOnly] public ComponentLookup<Health> HealthLookup;
void Execute(ref Damage dmg, in Target target) { // 다른 엔티티(target.Entity)의 체력 읽기 if (HealthLookup.TryGetComponent(target.Entity, out var hp)) dmg.Pending = math.min(dmg.Value, hp.CurrentHP); } }}10. IEnableableComponent — 활성/비활성 토글
섹션 제목: “10. IEnableableComponent — 활성/비활성 토글”컴포넌트를 추가/제거하지 않고 비용 없이 켜고 끌 수 있습니다.
// 활성화 가능한 컴포넌트public struct Stunned : IComponentData, IEnableableComponent { }
// 시스템에서 토글public partial struct StunSystem : ISystem{ public void OnUpdate(ref SystemState state) { var ecb = new EntityCommandBuffer(Allocator.Temp);
foreach (var (stun, entity) in SystemAPI.Query<RefRO<StunTimer>>().WithEntityAccess()) { bool isStunned = stun.ValueRO.TimeRemaining > 0f; // 쿼리 필터에 반영 — 컴포넌트 추가/제거 비용 없음 state.EntityManager.SetComponentEnabled<Stunned>(entity, isStunned); }
ecb.Playback(state.EntityManager); }}
// 스턴 상태인 엔티티만 처리하는 시스템[UpdateAfter(typeof(StunSystem))]public partial struct FrozenMovementSystem : ISystem{ public void OnUpdate(ref SystemState state) { // WithAll<Stunned>는 Enabled 상태인 것만 쿼리 foreach (var transform in SystemAPI.Query<RefRW<LocalTransform>>().WithAll<Stunned>()) { // 이동 처리 스킵 } }}참고 자료
섹션 제목: “참고 자료”- Unity Entity Component System: https://unity.com/ecs
- Entity Component System Documentation: https://docs.unity3d.com/Packages/com.unity.entities@latest
- ECS Concepts: https://docs.unity3d.com/Packages/[email protected]/manual/ecs_core.html
- Using Transforms in Entities: https://docs.unity3d.com/Packages/[email protected]/manual/transforms-using.html
- Kodeco ECS Tutorial: https://www.kodeco.com/7630142-entity-component-system-for-unity-getting-started
- DEV Community ECS Guide: https://dev.to/rk042/a-step-by-step-guide-to-creating-and-adding-components-in-unity-ecs-3f70