영상을 불러오지 못했습니다

전체화면 PIE 종료 시 ForEachAttachedActors 재귀 크래시

770 단어
4 분
전체화면 PIE 종료 시 ForEachAttachedActors 재귀 크래시

저장 데이터를 불러온 뒤 F11로 전체화면 PIE를 종료하면 editor가 AActor::ForEachAttachedActors 안에서 스택 오버플로를 일으키며 종료됐다.

전체화면 PIE에서 문제를 재현한 게임 화면
전체화면 PIE에서 문제를 재현한 게임 화면

문제 현상#

크래시는 전체화면 전환 때 나타났지만 실제 call stack에서는 attached actor를 순회하는 동일한 프레임이 반복됐다.

ForEachAttachedActors가 반복된 스택 오버플로 call stack
ForEachAttachedActors가 반복된 스택 오버플로 call stack

적용 범위#

대상은 런타임에 NewObject로 scene component를 만드는 UE 프로젝트다. 이를 다른 actor가 소유한 component나 socket에 부착하는 코드가 있었다. 종료·월드 정리 시점에만 크래시가 보인다면 생성 당시의 owner와 attachment 관계도 함께 확인할 가치가 있다.

원인#

문제가 된 코드는 새 UStaticMeshComponent의 Outer를 현재 객체로 지정했다. 그런 다음 별도의 전시 actor에 owned component로 추가했다. 이 component를 또 다른 actor가 소유한 mesh에 붙이면서 UObject 소유권, actor의 owned component 목록, scene attachment 계층이 서로 다른 대상을 가리켰다.

UStaticMeshComponent* DisplayComponent =
NewObject<UStaticMeshComponent>(this, TEXT("RuntimeDisplayMesh"));
DisplayActor->AddOwnedComponent(DisplayComponent);
DisplayComponent->AttachToComponent(
ItemActor->GetMesh(),
FAttachmentTransformRules::SnapToTargetNotIncludingScale,
SocketName);

이 불일치는 월드를 정리하면서 attached actor를 순회할 때 순환 관계처럼 드러났다. F11은 문제의 원인이 아니다. 숨어 있던 잘못된 계층이 종료 경로에서 노출된 계기였다.

재현 조건#

  1. 저장 데이터에서 전시 actor와 부착 대상 actor를 복원한다.
  2. 런타임 component를 생성하고 대상 socket에 부착한다.
  3. PIE를 전체화면으로 전환한 뒤 종료한다.
  4. call stack에서 ForEachAttachedActors가 같은 actor들을 반복하는지 확인한다.

해결 방법#

런타임 component의 Outer와 owner를 실제로 수명 관리할 actor로 일치시켰다. 부착 전에는 대상 component와 socket이 유효한지도 검사했다.

if (!IsValid(DisplayActor) || !IsValid(ItemActor) ||
!IsValid(ItemActor->GetMesh()) ||
!ItemActor->GetMesh()->DoesSocketExist(SocketName))
{
return;
}
UStaticMeshComponent* DisplayComponent =
NewObject<UStaticMeshComponent>(DisplayActor, TEXT("RuntimeDisplayMesh"));
DisplayActor->AddInstanceComponent(DisplayComponent);
DisplayComponent->RegisterComponent();
DisplayComponent->AttachToComponent(
ItemActor->GetMesh(),
FAttachmentTransformRules::SnapToTargetNotIncludingScale,
SocketName);

특정 API 이름보다 component를 소유하고 파괴할 주체를 명확히 정하는 일이 중요하다. component를 DisplayActor가 소유할지 ItemActor가 소유할지는 실제 생명주기에 맞춰 하나로 정해야 한다.

검증 방법#

  • 저장 데이터 로드 전후에 owner, Outer, attach parent와 owning actor를 로그로 비교한다.
  • PIE 일반 창·전체화면 종료를 여러 번 반복한다.
  • level transition, actor destroy, 저장 데이터 재로드에서도 component가 중복 등록되지 않는지 확인한다.
  • EndPlay에서 attachment를 명시적으로 끊어야 하는 설계인지 검토한다.

주의점#

AddOwnedComponent, AddInstanceComponent, RegisterComponent는 서로 완전히 같은 역할이 아니다. 생성 방식과 editor instance component로서의 저장 필요 여부에 맞게 선택해야 한다. 종료 전에 component를 강제 삭제해 call stack만 없애면 잘못된 소유 관계가 다른 경로에서 다시 나타날 여지가 있다.

참고 자료#

글 공유하기

이 글이 도움이 되셨다면 다른 사람과 공유해 주세요!

전체화면 PIE 종료 시 ForEachAttachedActors 재귀 크래시
https://hon454.github.io/posts/unreal-editor-fullscreen-foreachattachedactors-crash/
작성자
Jihoon Jeon
게시일
2024-01-17
라이선스
CC BY-NC-SA 4.0
관련 게시글스마트
1
접속 시 레벨 스트리밍 상태 RPC 줄이기
Unreal EngineNight of the Dead에서 접속한 클라이언트에 스트리밍 레벨 상태를 보내는 RPC를 줄이기 위해 패키지 이름을 테이블 기반 코드로 바꾸고 플래그를 비트로 묶은 방법과, 그 앞뒤에 놓인 클라이언트 준비 게이트와 에셋 사전 로딩을 정리한다.
2
Unreal Engine PIE에서 클라이언트와 서버 값을 한 줄로 비교하기
Unreal EnginePIE에서 클라이언트 코드가 같은 프로세스의 서버 월드와 서버 측 PlayerController를 찾아 위치·속도를 한 줄로 비교하는 에디터 전용 디버그 코드와, 네트워크 에뮬레이션으로 확인한 측정 결과를 정리합니다.
3
면접 오답노트: 말하지 못한 주요 개념, Net Relevancy
Unreal Engine면접에서 제대로 답하지 못한 Net Relevancy를 다시 정리한다. AActor::IsNetRelevantFor의 판정 순서와 문서와 다른 부분, Owner가 판정을 바꾸는 방식, 연관성을 잃고 되찾을 때 RelevantTimeout과 액터 파괴, BeginPlay, OnRep, RPC에 생기는 일, ForceNetRelevant와 Replication Graph, Iris에서의 주의점, Dormancy, Priority와의 차이를 다룬다.
4
언리얼 엔진의 리플리케이션 및 RPC 순서 보장
Unreal Engine프로퍼티 복제와 OnRep, RPC의 순서 보장 범위를 살펴보고, 일시적인 상태 불일치와 액터 간 준비 순서를 처리하는 방법을 예시로 정리한다.
5
[노트] Unreal Engine Skills for Claude Code
Unreal EngineEpic Games가 공개한 Claude Code 플러그인 Unreal Engine Skills for Claude Code의 스킬 3개와 SessionStart 훅을 읽고, 스킬의 트리거 설계와 문서화 규칙, 엔진 쪽 Agent Skill, 보안 경고의 범위, MCP와 스킬의 역할 분담, Claude Code용만 나온 점에 대한 생각을 정리한다.
무작위 게시글무작위

댓글

Profile Image of the Author
Jihoon Jeon
안녕하세요! 서브컬처와 자전거를 사랑하는 개발자 전지훈입니다.
공지사항
제 블로그에 오신 것을 환영합니다! 예시 공지입니다.
카테고리
태그
최신 일상
사이트 통계
게시글
73
카테고리
10
태그
143
총 단어 수
133,527
운영 일수
0일
최근 활동
0일 전
사이트 정보
빌드 플랫폼
GitHub Actions
블로그 버전
Firefly v6.16.9
라이선스
CC BY-NC-SA 4.0
목차