icon안동민 개발노트

물리 시뮬레이션 성능 조정


 언리얼 엔진의 물리 시뮬레이션은 게임의 현실감과 상호작용성을 크게 향상시키지만, 동시에 상당한 계산 리소스를 요구합니다.

 이 절에서는 물리 시뮬레이션의 성능을 최적화하고 조정하는 다양한 방법을 살펴보겠습니다.

물리 시뮬레이션 서브스테핑 설정

 서브스테핑은 프레임 간 물리 계산의 정확도를 높이는 기법입니다.

UWorld* World = GetWorld();
FPhysScene* PhysScene = World->GetPhysicsScene();
 
FPhysSubstepSetup SubstepSetup;
SubstepSetup.MaxSubstepCount = 2;
SubstepSetup.MaxSubstepDeltaTime = 1.0f / 60.0f;
SubstepSetup.MaxSubstepImpactFraction = 0.5f;
 
PhysScene->SetSubsteppingParams(SubstepSetup);

LOD(Level of Detail)을 활용한 최적화

 거리에 따른 물리 시뮬레이션 복잡도 조절

void APhysicsActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
 
    float DistanceToCamera = (GetActorLocation() - CameraLocation).Size();
    
    if (DistanceToCamera < NearDistance)
    {
        SetDetailedPhysics();
    }
    else if (DistanceToCamera < FarDistance)
    {
        SetSimplifiedPhysics();
    }
    else
    {
        DisablePhysics();
    }
}
 
void APhysicsActor::SetDetailedPhysics()
{
    // 고품질 물리 설정
    MeshComponent->SetSimulatePhysics(true);
    MeshComponent->SetEnableGravity(true);
    MeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
}
 
void APhysicsActor::SetSimplifiedPhysics()
{
    // 간소화된 물리 설정
    MeshComponent->SetSimulatePhysics(true);
    MeshComponent->SetEnableGravity(true);
    MeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
}
 
void APhysicsActor::DisablePhysics()
{
    // 물리 비활성화
    MeshComponent->SetSimulatePhysics(false);
    MeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}

비동기 물리 시뮬레이션 구현

 물리 연산을 별도의 스레드에서 처리

UWorld* World = GetWorld();
World->bAsyncPhysicsTickEnabled = true;
World->AsyncPhysicsThreadSettings.MaxPhysicsTaskTime = 0.016f; // 16ms

물리 객체의 슬립 상태 관리

 불필요한 연산 방지를 위한 슬립 상태 관리

UPrimitiveComponent* PrimComponent = GetComponentByClass<UPrimitiveComponent>();
if (PrimComponent)
{
    PrimComponent->PutRigidBodyToSleep();
    PrimComponent->SetEnableGravity(false);
}
 
// 필요할 때 깨우기
void AWakeUpActor::WakeUp()
{
    UPrimitiveComponent* PrimComponent = GetComponentByClass<UPrimitiveComponent>();
    if (PrimComponent)
    {
        PrimComponent->WakeRigidBody();
        PrimComponent->SetEnableGravity(true);
    }
}

충돌 필터링 최적화

 불필요한 충돌 검사 제거

UPrimitiveComponent* PrimComponent = GetComponentByClass<UPrimitiveComponent>();
if (PrimComponent)
{
    PrimComponent->SetCollisionResponseToChannel(ECC_Pawn, ECR_Ignore);
    PrimComponent->SetCollisionResponseToChannel(ECC_Vehicle, ECR_Ignore);
}

물리 프록시 메시 활용

 복잡한 메시의 물리 연산 간소화

UStaticMeshComponent* MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
MeshComponent->SetSimulatePhysics(true);
 
// 간소화된 콜리전 메시 설정
UBodySetup* BodySetup = MeshComponent->GetBodySetup();
BodySetup->CollisionTraceFlag = CTF_UseSimpleAsComplex;

대규모 물리 시뮬레이션 시나리오 관리

 파괴 가능한 환경 최적화

void ADestructibleEnvironment::OptimizePhysics()
{
    TArray<AActor*> FoundActors;
    UGameplayStatics::GetAllActorsOfClass(GetWorld(), ADestructibleActor::StaticClass(), FoundActors);
 
    for (AActor* Actor : FoundActors)
    {
        ADestructibleActor* DestructibleActor = Cast<ADestructibleActor>(Actor);
        if (DestructibleActor)
        {
            // 작은 파편은 물리 시뮬레이션에서 제외
            DestructibleActor->GetDestructibleComponent()->SetSimulatePhysics(false);
            DestructibleActor->GetDestructibleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
        }
    }
}

모바일 및 VR 플랫폼을 위한 물리 최적화

 모바일 최적화 예시

#if PLATFORM_ANDROID || PLATFORM_IOS
    // 모바일 플랫폼에서의 물리 설정
    UPhysicsSettings* PhysicsSettings = UPhysicsSettings::Get();
    PhysicsSettings->bSimulateSkeletalMeshOnDedicatedServer = false;
    PhysicsSettings->bDisableActiveActors = true;
    PhysicsSettings->bEnableStabilization = true;
#endif

 VR 최적화

// VR에서의 물리 업데이트 빈도 조절
UWorld* World = GetWorld();
World->bAsyncPhysicsTickEnabled = true;
World->AsyncPhysicsTickRate = 120; // 120Hz 물리 업데이트

성능 병목 지점 식별

 언리얼 인사이트 활용

  1. 'Window > Developer Tools > Session Frontend' 열기
  2. 'Unreal Insights' 탭 선택
  3. 'Physics' 섹션에서 물리 연산 시간 및 병목 지점 분석

 코드 기반 프로파일링

SCOPE_CYCLE_COUNTER(STAT_PhysicsSimulation);
 
// 물리 시뮬레이션 코드
 
// 로그 출력
UE_LOG(LogTemp, Warning, TEXT("Physics Simulation Time: %f"), FPlatformTime::Seconds() - StartTime);

물리 시뮬레이션 품질과 성능 균형

 1. 중요도 기반 최적화

  • 플레이어 근처의 객체에 대해 고품질 물리 적용
  • 원거리 객체는 간소화된 물리 또는 애니메이션으로 대체

 2. 동적 품질 조정

void APhysicsManager::AdjustPhysicsQuality()
{
    float CurrentFPS = GetWorld()->GetDeltaSeconds();
    if (CurrentFPS < TargetFPS)
    {
        LowerPhysicsQuality();
    }
    else if (CurrentFPS > TargetFPS + Threshold)
    {
        IncreasePhysicsQuality();
    }
}

 3. 물리 객체 풀링

class FPhysicsObjectPool
{
public:
    AActor* GetOrCreatePhysicsObject()
    {
        if (Pool.Num() > 0)
        {
            return Pool.Pop();
        }
        return World->SpawnActor<APhysicsActor>(PhysicsActorClass);
    }
 
    void ReturnToPool(AActor* Actor)
    {
        Actor->SetActorHiddenInGame(true);
        Actor->SetActorEnableCollision(false);
        Pool.Push(Actor);
    }
 
private:
    TArray<AActor*> Pool;
};

 언리얼 엔진의 물리 시뮬레이션 성능을 최적화하는 것은 고품질의 게임플레이 경험을 유지하면서 리소스 사용을 효율적으로 관리하는 중요한 과정입니다.

 서브스테핑 설정을 통해 물리 계산의 정확도를 높이고 LOD 시스템을 활용하여 거리에 따라 물리 복잡도를 조절함으로써 전체적인 성능을 개선할 수 있습니다.

 비동기 물리 시뮬레이션은 멀티코어 시스템에서 유용하며 메인 게임 스레드의 부하를 줄여 전반적인 게임 성능을 향상시킬 수 있습니다.

 물리 객체의 슬립 상태 관리와 충돌 필터링 최적화는 불필요한 연산을 줄이는 데 효과적입니다.

 대규모 물리 시뮬레이션 시나리오에서는 객체 풀링, 간소화된 물리 프록시 사용, 그리고 중요도 기반의 시뮬레이션 관리가 중요합니다.

 특히 파괴 가능한 환경이나 다수의 상호작용 객체가 있는 경우 이러한 최적화 기법들이 큰 효과를 발휘합니다.