
프로젝트에 레이트레이싱을 적용한 뒤, 심리스 트래블 시 Instanced Static Mesh(ISM) 때문에 발생하던 크래시를 추적하고 해결한 과정을 정리해 두려고 합니다. 동일한 구조의 네트워크/멀티플레이 프로젝트에서 레이트레이싱을 켜면 비슷한 문제를 겪을 수 있기 때문에, 원인과 대응 패턴을 기억해 두면 좋겠다는 생각이 들었습니다.
Array index out of bounds: 1 into an array of size 1FInstancedStaticMeshSceneProxy::GetDynamicRayTracingInstances()RayTracing::FDynamicRayTracingInstancesContext::GatherDynamicRayTracingInstances_Internal()```text
Assertion failed: (Index >= 0) & (Index < ArrayNum)
[File:...Array.h] [Line: 1067]
Array index out of bounds: 1 into an array of size 1
...
FInstancedStaticMeshSceneProxy::GetDynamicRayTracingInstances()
RayTracing::FDynamicRayTracingInstancesContext::GatherDynamicRayTracingInstances_Internal()
Crash in runnable thread Foreground Worker #1
```
GatherDynamicRayTracingInstances)에서 발생.FInstancedStaticMeshSceneProxy::GetDynamicRayTracingInstances() 내부에서 ISM 관련 배열 접근 시 인덱스 범위가 꼬인 상태.PerInstanceRenderData / 인스턴스 배열 크기 정보가 어긋난 상태로 참조되는 상황.FInstancedStaticMeshSceneProxy::GetDynamicRayTracingInstances() 가ArrayNum == 1 인 배열을 Index == 1로 접근하는 식의 out-of-bounds 접근을 시도하다가 Assert 에 걸림.EndPlay / BeginDestroy 에서 개별적으로 정리ACMPlayerController::PostSeamlessTravel() 을 오버라이드.UInstancedStaticMeshComponent 를 순회하며 ClearInstances() 호출.Source/CrimsonMoon/Public/Controllers/CMPlayerController.hSource/CrimsonMoon/Private/Controllers/CMPlayerController.cpp헤더에 오버라이드 선언 (이미 추가되어 있음)
CMPlayerController.h (클래스 내부):
virtual void PostSeamlessTravel() override;역할:
- 심리스 트래블 완료 후, 로컬 월드의 모든 ISM 인스턴스를 정리
- 그 다음, 캐릭터 재스폰 및 화면/사운드 페이드 인
#include "Components/InstancedStaticMeshComponent.h"
#include "EngineUtils.h" // TActorIterator
void ACMPlayerController::PostSeamlessTravel()
{
Super::PostSeamlessTravel();
// 로컬 컨트롤러가 아닌 경우 아무 것도 하지 않음 (서버 전용 PC 등 제외)
if (!IsLocalController())
{
return;
}
// 1) 로컬 월드의 모든 InstancedStaticMeshComponent 정리
if (UWorld* World = GetWorld())
{
for (TActorIterator<AActor> It(World); It; ++It)
{
AActor* Actor = *It;
if (!IsValid(Actor))
{
continue;
}
TArray<UInstancedStaticMeshComponent*> ISMComponents;
Actor->GetComponents<UInstancedStaticMeshComponent>(ISMComponents);
for (UInstancedStaticMeshComponent* ISMComp : ISMComponents)
{
if (!ISMComp)
{
continue;
}
// 레이트레이싱/충돌 문제를 방지하기 위해 모든 인스턴스를 제거
ISMComp->ClearInstances();
ISMComp->MarkRenderStateDirty();
}
}
}
// 2) 심리스 트래블 후 캐릭터 재스폰 요청 (기존 로직)
NotifyServerPlayerReadyWithCharacter();
// 3) 화면/사운드 페이드 인 (검정 → 정상, 기존 연출)
if (PlayerCameraManager)
{
PlayerCameraManager->StartCameraFade(
/*FromAlpha*/ 1.0f,
/*ToAlpha*/ 0.0f,
/*Duration*/ 1.0f,
/*Color*/ FLinearColor::Black,
/*bShouldFadeAudio*/ true,
/*bHoldWhenFinished*/ false
);
}
}
ACMPlayerController 에서는 심리스 트래블 이후에 다음 작업을 하고 있었음NotifyServerPlayerReadyWithCharacter()StartCameraFade(1 → 0, bShouldFadeAudio=true)이번 수정 이후, 레이트레이싱을 활성화한 빌드에서 심리스 트래블을 반복 테스트했을 때,
기존에 발생하던 FInstancedStaticMeshSceneProxy::GetDynamicRayTracingInstances 관련 Assert 크래시는 더 이상 재현되지 않았습니다.
물론 근본적으로는 ISM 을 소유하는 각 액터가 자신의 라이프사이클에 맞춰 적절히 정리되도록 설계하는 것이 가장 좋겠지만,
이번과 같이 레이트레이싱 + 심리스 트래블 조합에서 내부 상태 꼬임으로 인한 크래시가 발생할 때에는,
앞으로는 ISM 을 대량으로 사용하는 시스템을 설계할 때,