Significance Manager
거리 기반으로 게임플레이 객체의 기능을 동적으로 조절하기
Significance Manager란?
중요도 기반 최적화 시스템
Significance Manager는 플레이어로부터의 거리나 가시성에 따라 객체의 중요도(Significance)를 계산하고, 이에 따라 기능을 조절하는 시스템입니다. 대규모 오픈월드에서 수천 개의 NPC나 객체가 있을 때 필수적입니다.
Near: 고품질 | Mid: 중간 | Far: 최소 기능
프로젝트에서 SignificanceManager 모듈을 활성화해야 합니다. Build.cs에 "SignificanceManager"를 추가하세요.
Significance 레벨별 동작
거리에 따른 기능 스케일 다운
- ✓ 풀 프레임 Tick
- ✓ 고품질 애니메이션
- ✓ 모든 물리 시뮬레이션
- ✓ AI 의사결정 활성
- ✓ 사운드/VFX 활성
- ✓ Tick Interval 증가 (0.1s)
- ☒ 복잡한 IK 비활성화
- ☒ 물리 단순화
- ✓ 기본 AI만 활성
- ☒ 상세 VFX 비활성화
- ☒ Tick 비활성화
- ☒ 애니메이션 최저 품질
- ☒ 물리 비활성화
- ☒ AI 휴면 상태
- ☒ 사운드/VFX 비활성화
Significance Manager 설정
Viewport Client에서 업데이트 호출
UCLASS()
class UMyGameViewportClient : public UGameViewportClient
{
GENERATED_BODY()
public:
virtual void Tick(float DeltaTime) override;
};
#include "SignificanceManager.h"
void UMyGameViewportClient::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
UWorld* World = GetWorld();
if (!World) return;
// Significance Manager 가져오기
USignificanceManager* SignificanceManager =
FSignificanceManagerModule::Get(World);
if (!SignificanceManager) return;
// 모든 플레이어의 위치 수집
TArray<FTransform> ViewpointTransforms;
for (FConstPlayerControllerIterator It = World->GetPlayerControllerIterator();
It; ++It)
{
if (APlayerController* PC = It->Get())
{
if (APawn* Pawn = PC->GetPawn())
{
ViewpointTransforms.Add(Pawn->GetActorTransform());
}
}
}
// Significance 업데이트
if (ViewpointTransforms.Num() > 0)
{
SignificanceManager->Update(TArrayView<FTransform>(ViewpointTransforms));
}
}
Managed Object 구현
Significance 관리 대상 액터 구현
UCLASS()
class ASignificanceAwareActor : public AActor
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
protected:
// Significance 계산
float CalculateSignificance(
USignificanceManager::FManagedObjectInfo* ObjectInfo,
const FTransform& Viewpoint);
// Significance 변경 후 처리
void PostSignificanceUpdate(
USignificanceManager::FManagedObjectInfo* ObjectInfo,
float OldSignificance, float Significance, bool bFinal);
UPROPERTY(EditAnywhere, Category = "Significance")
float MaxSignificanceDistance = 5000.f;
UPROPERTY(EditAnywhere, Category = "Significance")
float HighSignificanceThreshold = 0.7f;
UPROPERTY(EditAnywhere, Category = "Significance")
float LowSignificanceThreshold = 0.3f;
private:
float CurrentSignificance = 1.f;
};
#include "SignificanceManager.h"
void ASignificanceAwareActor::BeginPlay()
{
Super::BeginPlay();
if (USignificanceManager* Manager =
FSignificanceManagerModule::Get(GetWorld()))
{
Manager->RegisterObject(
this,
FName("SignificanceAwareActor"),
// Significance 계산 함수 (Lambda)
[this](USignificanceManager::FManagedObjectInfo* ObjectInfo,
const FTransform& Viewpoint) -> float
{
return CalculateSignificance(ObjectInfo, Viewpoint);
},
// Post Significance 함수
USignificanceManager::EPostSignificanceType::Sequential,
[this](USignificanceManager::FManagedObjectInfo* ObjectInfo,
float OldSignificance, float Significance, bool bFinal)
{
PostSignificanceUpdate(ObjectInfo, OldSignificance, Significance, bFinal);
}
);
}
}
void ASignificanceAwareActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if (USignificanceManager* Manager =
FSignificanceManagerModule::Get(GetWorld()))
{
Manager->UnregisterObject(this);
}
Super::EndPlay(EndPlayReason);
}
float ASignificanceAwareActor::CalculateSignificance(
USignificanceManager::FManagedObjectInfo* ObjectInfo,
const FTransform& Viewpoint)
{
float Distance = FVector::Dist(
GetActorLocation(),
Viewpoint.GetLocation());
// 거리가 멀수록 Significance가 낮아짐 (0~1)
float Significance = 1.0f - FMath::Clamp(
Distance / MaxSignificanceDistance, 0.f, 1.f);
return Significance;
}
void ASignificanceAwareActor::PostSignificanceUpdate(
USignificanceManager::FManagedObjectInfo* ObjectInfo,
float OldSignificance, float Significance, bool bFinal)
{
CurrentSignificance = Significance;
// High Significance: 모든 기능 활성화
if (Significance >= HighSignificanceThreshold)
{
SetActorTickEnabled(true);
PrimaryActorTick.TickInterval = 0.f; // 매 프레임
// 고품질 모드 활성화
OnHighSignificance();
}
// Medium Significance: 일부 기능 제한
else if (Significance >= LowSignificanceThreshold)
{
SetActorTickEnabled(true);
PrimaryActorTick.TickInterval = 0.1f; // 10 FPS
// 중간 품질 모드
OnMediumSignificance();
}
// Low Significance: 최소 기능만
else
{
SetActorTickEnabled(false);
// 저품질/휴면 모드
OnLowSignificance();
}
}
NPC 예제
실제 NPC에 적용하기
void ASignificanceNPC::OnHighSignificance()
{
// 풀 애니메이션 품질
if (SkeletalMesh)
{
SkeletalMesh->SetComponentTickEnabled(true);
SkeletalMesh->VisibilityBasedAnimTickOption =
EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones;
}
// AI 컨트롤러 활성화
if (AIController)
{
AIController->SetActorTickEnabled(true);
AIController->GetBrainComponent()->RestartLogic();
}
// 사운드 활성화
if (AudioComponent)
{
AudioComponent->SetActive(true);
}
}
void ASignificanceNPC::OnMediumSignificance()
{
// 애니메이션 단순화
if (SkeletalMesh)
{
SkeletalMesh->SetComponentTickEnabled(true);
SkeletalMesh->VisibilityBasedAnimTickOption =
EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;
}
// AI 간소화 (BT 대신 간단한 로직)
if (AIController)
{
AIController->SetActorTickEnabled(true);
AIController->PrimaryActorTick.TickInterval = 0.5f;
}
// 사운드 감소
if (AudioComponent)
{
AudioComponent->SetVolumeMultiplier(0.5f);
}
}
void ASignificanceNPC::OnLowSignificance()
{
// 애니메이션 최소화
if (SkeletalMesh)
{
SkeletalMesh->VisibilityBasedAnimTickOption =
EVisibilityBasedAnimTickOption::OnlyTickMontagesWhenNotRendered;
}
// AI 휴면
if (AIController)
{
AIController->SetActorTickEnabled(false);
AIController->GetBrainComponent()->StopLogic("Low Significance");
}
// 사운드 비활성화
if (AudioComponent)
{
AudioComponent->SetActive(false);
}
}
핵심 요약
- Significance Manager - 거리 기반으로 객체 중요도를 0~1 사이 값으로 계산
- RegisterObject - BeginPlay에서 등록, EndPlay에서 해제
- High Significance - 가까운 객체는 풀 기능 활성화
- Low Significance - 먼 객체는 Tick 비활성화, AI 휴면
- 애니메이션 최적화 - VisibilityBasedAnimTickOption 활용
- 대규모 오픈월드 필수 - 수천 개의 NPC 관리에 필수적
NPC AI > Skeletal Mesh 애니메이션 > 물리 시뮬레이션 > 사운드/VFX 순서로 적용하면 가장 큰 성능 향상을 얻을 수 있습니다.
도전 과제
배운 내용을 직접 실습해보세요
USignificanceManager를 프로젝트에 설정하고, RPG NPC에 RegisterObject()로 중요도를 등록하세요. 중요도 함수에서 카메라 거리와 화면 크기를 기반으로 값을 반환하세요.
Significance 값에 따라 NPC의 틱 빈도, 애니메이션 LOD, AI 업데이트 빈도를 동적으로 조절하세요. PostSignificanceFunction 콜백에서 컴포넌트별 설정을 변경하세요.
수백 명의 NPC, 수천 개의 인터랙터블 오브젝트에 중요도 시스템을 적용하세요. 오브젝트 타입별(적, 아군 NPC, 환경, 이펙트) 중요도 버짓을 설계하고, 프레임 타임 목표치 내에서 최적화하세요.