3 분 소요

Mass Entity에 대하여

Unreal의 Mass AI 시스템은 ‘매~우’ 많은 양의 Npc 등을 구현할 때 사용한다

  • 좀비 같은 적을 100마리 정도만 사용한다면,
    Actor로도 충분할 수 있음
    • 그러나 이 이상 넘어가면 점점 처리하기 힘들어짐

그렇기에 사용하는 것이 Mass Entity!

  • Mass Entity는 이러한 계산을 위하여
    데이터 지향 계산 방식을 채택한
    일종의 프레임워크
    (DOD, Data-Oriented Design : 데이터 지향 설계)

  • Entity는 단순히 ‘데이터’만 갖는 일종의 요소
    • 각각의 ‘구성’을 통해 속성을 부여(name,helath 등)
      (Fragment!)
    • 그리고 ‘시스템’을 통해 로직을 처리 (이동, 체력 등)
      (Processor!)
      • Entity는 단순히 ID이지만 여기에 데이터를 레고처럼 조립하는 방식
        -> ECS(Entity Component System)!
  • 언리얼 5.6 기준 공식 문서

Entity, Archetype, Fragment

Image

  • Fragment
    주요 데이터 구조
    (Processor의 계산에 사용되는 최소 단위의 데이터 ‘조각’)
    • Transform, Velocity, LOD 등등이 존재
    • 별도의 ‘로직’을 포함하지 않음!
    • 태그?
      데이터가 없는 ‘사소’한 Fragment
      (존재 여부 자체를 데이터로 활용하는 용도)
    • Chunk Fragment?
      Entity 대신, ‘청크’와 연결된 Fragment
      (LOD 계산 등, 관리 프로세스에 사용되는 청크 단위의 데이터를 저장)
  • Archetype(아키타입)
    Fragment의 ‘목록’이 같은 엔티티의 모음!
    • 내부의 엔티티는 ‘메모리 청크(Chunk)’로 조직
      (이들은 ‘연속적’으로 메모리에 배치됨)
      • 동일 아키타입의 엔티티와 관련된 Fragment를 참조할 때 훌륭한 퍼포먼스를 발휘
        (캐시 적중률 상승! + SIMD 처리 가능)
  • 이렇기에 Entity 자체는 매우 가볍고
    아키타입에서 보관된 자신의 정보를 쉽게 찾을 수 있음

예시)


Entity A : {TransformFragment} , {VelocityFragment}
Entity B : {TransformFragment} , {VelocityFragment}
Entity C : {TransformFragment} , {VelocityFragment} , {HealthFragment}

-> C 별개의 아키타입으로 분류!

+ 혹시 Processor 등으로 Fragment 제거/추가  경우, 아키타입이 바뀌게 
  -> 이건 비싼 작업이므로,  프레임(Tick) 실행되는 것은 피할 !

Processor

Image

  • Processor(프로세서)
    프로세싱 로직을 공급하는 ‘상태 없는 클래스’
    (프로세싱 작업하는 클래스가 프로세서…)
    • 프로세서는 데이터(Fragment) 값을 갱신!
    • Fragment를 추가 or 제거(‘구조적 변경’)
      • 다만 이 작업은 아키타입 자체가 이동해야 하기에
        특정 상황에서만 호출을 권장함
        (ex : 사망, 무기 획득 등)
  • Entity Query
    프로세서가 사용하며, ‘작업 수행’에 필요한 Fragment 타입을 지정함
    (엔티티의 식별자에 관계 없이, 프로세서에 Fragment 배치를 제공)
  • MassCommandBuffer
    위에서 말한 ‘구조적 변경’을 ‘안전’하게 수행
    • 아키타입을 옮기는 것을 ‘바로’ 수행하면 다른 프로세서에서 크래시가 발생 가능!
      그렇기에 커맨드 버퍼에게 ‘예약’하고, 일반적인 데이터 프로세싱의 진행 이후, 구조 변경 진행
  • EntityView
    현재 프로세스가 아닌 다른 ‘엔티티’에 엑세스 해야 할때 사용
    • 엔티티의 데이터(Fragment)를 직접 확인하기 위해 사용
    • 일반적으로 프로세서가 쿼리를 통해 받는 데이터는 ‘나 자신’(Current Entity)
      그러나 ‘다른 엔티티’의 상태를 확인해야 한다면
      그 엔티티의 ID를 통해 EntityView를 생성하고 데이터를 체크해야 함

Entity Manager (FMassEntityManager)

  • UMassEntitySubsystem
    • 역할 : 엔진에서 UWorld 내부에 Mass AI의 자리를 마련해주는 컨테이너 이자 관리자
    • FMassEntityManager의 인스턴스를 하나 들고 있고
      다른 시스템들이 이에 접근할 수 있도록 함
  • FMassEntityManager
    • 실질적인 매니저 로직이 들어간 구조체
      • 아키타입 관리, Fragment 조작, 메모리 관리 등

// 월드 서브시스템을 통해 매니저를 가져옴
UWorld* World = ...;
UMassEntitySubsystem* EntitySubsystem = World->GetSubsystem<UMassEntitySubsystem>();
FMassEntityManager& EntityManager = EntitySubsystem->GetMutableEntityManager();

Trait

Image

  • Trait
    Entity 생성의 블루프린트 같은 역할을 함
    • Mass 시스템이 실제 엔티티를 생성(Spawn)할 때,
      Trait을 보고 ‘정의’된 Fragment를
      Entity에 넣어 생성함
  • MassEntityConfig
    이러한 Trait를 가지는 ‘데이터 에셋’
    • 정확히는 Spawner가 이것을 기반으로 엔티티를 생성함
    • 내부에 여러 Trait이 존재
  • Entity Template
    위의 MassEntityConfig로부터 생성
    • 시스템이 MassEntityConfig를 읽은 후,
      Trait들을 해석하여 Fragment가 필요한지 체크
      이후, 목록을 확정하고, FMassEntityTemplate로 데이터를 만들어놓음
      -> 이후 DataAsset이 아니라, 미리 만들어둔 데이터에서 바로 복사해 사용

Trait들을 통해
‘아키타입’을 만드는 것!

흐름 정리

Image

  • Trait에 Fragment의 정의를 넣어둠

  • MassEntityConfig 라는 데이터 에셋을 통해
    사용할 Trait을 정의함

  • 게임 시작 시, EntityTemplate 를 빌드함
    (차후 사용할 녀석들)

  • 이후 매니저가 아키타입을 준비
    • 다만 LazyLoading 방식이기에
      Spawner가 실제로 엔티티 생성을 요청하고
      그러한 아키타입이 없다면 새로이 아키타입을 생성하는 방식
  • 참고로 Processor는 이들과 연결되지 않음
    Query를 이용하여 매칭됨
    (MassEntityManager!)

  • 매니저의 중앙집권체제

그 외에도 여러 서브 시스템 존재

  • 관련한 월드 서브시스템들이 존재
    • 그렇기에 World의 수명에 귀속
  • 이건 플러그인과 모듈로 분리되어 있음!
    (MassGameplay 등)

  • Mass Simulation Subsystem (UMassSimulationSubsystem)
    • 역할 : Mass AI의 심장
    • 기능 : MassProcessor들의 실행 순서와 단계를 관리
      프로세서들이 멀티스레드에서 안전하게 돌아가도록 스케줄링
      게임 루프(Tick)에 맞춰 시뮬레이션을 동작
  • Mass Spawner Subsystem (UMassSpawnerSubsystem)
    • 역할: 엔티티의 생성을 담당
    • 기능 :레벨에 배치된 MassSpawner 액터들과 통신
      MassEntityConfig에 정의된 대로 엔티티를 실제로 찍어내는(Spawn) 역할
  • Mass Representation Subsystem (UMassRepresentationSubsystem)
    • 역할: 엔티티의 시각화(렌더링)를 담당
    • 기능: 수만 개의 엔티티를 모두 액터로 그리면 느려지므로,
      이를 ISM(Instanced Static Mesh)으로 그려줄지,
      가까이 오면 액터로 바꿔줄지(LOD 처리) 등을 결정하고 관리
  • Mass Actor Subsystem (UMassActorSubsystem)
    • 역할: 기존 액터(Actor)와 Mass 엔티티 간의 연결을 담당
    • 기능: Mass AI를 쓴다고 해서 모든 것을 ECS로만 할 수는 없음
      기존의 AActor와 데이터를 주고받거나 동기화해야 할 때의 중간 다리 역할
  • Mass Signal Subsystem (UMassSignalSubsystem)
    • 역할: 엔티티 간의 이벤트/신호 전달을 담당
    • 기능: 특정 엔티티에게 “너 데미지 입었어” 같은 신호를 보낼 때 사용
      직접 함수를 호출하는 대신 신호를 큐에 넣고 처리하는 방식(비동기적)을 지원

댓글남기기