ISM과 Actor 전환
Instanced Static Mesh와 Actor 간의 전환 메커니즘과 풀링 시스템을 파악합니다
Actor 풀링 시스템
Actor 스폰/디스폰 비용을 줄이는 풀링 메커니즘
Mass Entity는 카메라에 가까운 엔티티만 Actor로 표현합니다. Actor의 생성/파괴는 비용이 크므로, 풀링(Pooling) 시스템으로 미리 생성해둔 Actor를 재활용합니다.
// Actor Pool 동작
//
// 초기화: Pool Size만큼 Actor 사전 생성
// ┌──────────────────────────────────────┐
// │ Actor Pool [50개] │
// │ [Inactive][Inactive]...[Inactive] │
// └──────────────────────────────────────┘
//
// 엔티티가 가까워질 때 (ISM → Actor):
// 1. 풀에서 비활성 Actor 획득
// 2. Entity의 Fragment 데이터를 Actor에 동기화
// 3. Actor 활성화 (SetActorHiddenInGame(false))
// 4. ISM 인스턴스 제거
//
// 엔티티가 멀어질 때 (Actor → ISM):
// 1. Actor의 현재 상태를 Entity Fragment에 동기화
// 2. Actor 비활성화 (SetActorHiddenInGame(true))
// 3. Actor를 풀에 반환
// 4. ISM 인스턴스 생성
풀 크기가 너무 작으면 가까이 다가가도 Actor가 스폰되지 않습니다. 너무 크면 초기 메모리가 낭비됩니다. 일반적으로 동시에 화면에 보이는 가까운 엔티티 수를 기준으로 설정하세요. City Sample은 보행자 약 50~100개의 풀을 사용합니다.
데이터 동기화
Entity Fragment와 Actor 간의 데이터 양방향 동기화
ISM에서 Actor로 전환될 때, Entity Fragment의 데이터를 Actor에 반영해야 합니다. 반대로 Actor에서 ISM으로 전환될 때는 Actor의 상태를 Fragment에 저장합니다.
// EMassTranslationDirection으로 방향을 구분
//
// MassToActor (ISM → Actor 전환 시):
// Fragment 데이터 → Actor 프로퍼티
// - Transform Fragment → Actor Location/Rotation
// - Velocity Fragment → Movement Component
// - Health Fragment → Health Component
//
// ActorToMass (Actor → ISM 전환 시):
// Actor 프로퍼티 → Fragment 데이터
// - Actor Location → Transform Fragment
// - Movement Speed → Velocity Fragment
//
// 동기화는 Trait의 BuildTemplate에서 등록한
// 초기화 콜백에서 처리됨
void UHealthTrait::BuildTemplate(
FMassEntityTemplateBuildContext& BuildContext,
const UWorld& World) const
{
BuildContext.AddFragment<FHealthFragment>();
// 양방향 동기화 콜백 등록
BuildContext.GetMutableObjectFragmentInitializers()
.Add([=](UObject& Owner,
FMassEntityView& EntityView,
const EMassTranslationDirection Direction)
{
FHealthFragment& Health =
EntityView.GetFragmentData<FHealthFragment>();
if (AActor* Actor = Cast<AActor>(&Owner))
{
if (Direction ==
EMassTranslationDirection::MassToActor)
{
// Fragment → Actor
if (auto* HC = Actor->FindComponentByClass
<UHealthComponent>())
{
HC->SetHealth(Health.Current);
HC->SetMaxHealth(Health.Max);
}
}
else // ActorToMass
{
// Actor → Fragment
if (auto* HC = Actor->FindComponentByClass
<UHealthComponent>())
{
Health.Current = HC->GetHealth();
Health.Max = HC->GetMaxHealth();
}
}
}
});
}
전환 전략
부드러운 전환을 위한 실전 기법
히스테리시스(Hysteresis)
ISM→Actor 전환 거리와 Actor→ISM 전환 거리를 다르게 설정합니다. 경계에서 반복 전환(Ping-Pong)을 방지합니다. 예: Actor 활성화 50m, ISM 전환 70m.
프레임 분산
한 프레임에 모든 전환을 처리하지 않고, 틱당 전환 수를 제한합니다. 갑자기 카메라가 이동해도 프레임 드롭을 방지합니다.
우선순위 큐
카메라에 가장 가까운 엔티티부터 Actor 전환 우선순위를 부여합니다. 풀 크기가 부족할 때 먼 엔티티는 ISM을 유지합니다.
페이드 전환
ISM과 Actor를 짧은 시간 동시에 표시하며 투명도로 전환합니다. 시각적으로 팝인(Pop-in)이 줄어듭니다.
Representation 디버깅
표현 시스템의 상태를 시각적으로 확인하는 방법
// 콘솔 명령으로 Representation 상태 확인
// 모든 엔티티의 표현 타입을 색상으로 표시
mass.debug.Representation
// LOD 레벨 시각화
mass.debug.LOD
// Actor 풀 상태 확인
mass.debug.ActorPool
// ISM 인스턴스 수 표시
mass.debug.ISMCount
// 특정 엔티티의 표현 정보
mass.debug.DebugEntity [EntityIndex]
stat Mass 명령으로 Mass Entity 관련 통계를 확인할 수 있습니다. 특히 Representation 전환 횟수와 Actor 풀 사용률을 모니터링하여 풀 크기와 LOD 거리를 조정하세요.
핵심 요약
- Actor 풀링으로 사전 생성된 Actor를 재활용하여 스폰/디스폰 비용을 제거한다
- ISM↔Actor 전환 시 EMassTranslationDirection으로 양방향 데이터 동기화를 수행한다
- 히스테리시스로 경계에서의 반복 전환(Ping-Pong)을 방지한다
- 프레임 분산과 우선순위 큐로 전환 비용을 관리하여 프레임 드롭을 방지한다
- Trait의 BuildTemplate에서 동기화 콜백을 등록하여 커스텀 데이터의 양방향 전환을 구현한다
mass.debug.Representation등의 콘솔 명령으로 표현 상태를 시각적으로 디버깅한다
도전 과제
배운 내용을 직접 실습해보세요
1000개의 Mass Entity를 ISM으로 렌더링하고, stat RHI 명령으로 Draw Call 수를 확인하세요. ISM 없이 개별 Actor로 렌더링할 때와 Draw Call 수를 비교하세요.
MassRepresentation LOD 설정에서 High LOD(Actor 전환) 거리를 조절하세요. 가까운 엔티티만 Actor로 전환되어 애니메이션이 재생되고, 먼 엔티티는 ISM으로 유지되는 것을 확인하세요.
가까운 거리에서 ISM -> Skeletal Mesh Actor로 전환되어 걷기 애니메이션이 재생되는 보행자 시스템을 구현하세요. 전환 시 부드럽게 블렌딩되도록 설정하세요.