Post

2026-07-09 TIL (94일차)

2026-07-09 TIL (94일차)

언리얼 데디케이티드 서버 AWS 연동 테스트 및 EOS의 필요성 깨달음

오늘의 목표

  • 언리얼 엔진 데디케이티드 서버를 패키징하여 AWS EC2 인스턴스에 올리고, 로컬 클라이언트에서 접속 테스트 진행.
  • 현재 LAN(Null) 환경의 한계를 이해하고, EOS(Epic Online Services) 연동의 필요성 파악.

배운 내용 및 트러블 슈팅

1. 클라이언트 크래시 (Out of Memory) 해결

  • 문제: 로컬 PC 한 대에서 에디터, 서버, 클라이언트 2개를 동시에 띄우고 테스트하다가 클라이언트가 알 수 없는 이유로 자꾸 꺼지는(Crash) 현상 발생.
  • 원인 분석: 로그 확인 결과 error 8007000e (DirectX 12 메모리 부족 에러) 확인. RTX 3070(VRAM 8GB) 환경에서 레이 트레이싱을 켠 상태로 다수의 언리얼 인스턴스를 실행하여 VRAM이 초과됨.
  • 해결 방안: 클라이언트 실행 시 가벼운 그래픽 환경으로 강제 실행하는 인수를 추가하여 메모리 다이어트 진행.
    • 적용 명령어: -Client.exe -d3d11 -windowed -resx=1280 -resy=720
    • 결과: 크래시 없이 쾌적하게 클라이언트 다중 실행 성공.

2. AWS EC2 데디케이티드 서버 세팅 및 직접 접속 테스트

  • AWS EC2(t3.small)에 서버 빌드 파일 업로드 후 실행.
  • 서버 실행 명령어: CrazyRooServer.exe TestMap -log (GUI 없는 Headless 모드이므로 그래픽 옵션 불필요)
  • 방화벽 설정의 중요성: AWS 인스턴스의 ‘보안 그룹’과 내부 ‘Windows 방화벽’ 양쪽 모두 UDP 7777 포트를 허용해야 접속 가능함.
  • 다이렉트 접속(콘솔 명령어): 아직 UI의 ‘방 찾기’ 기능이 인터넷 망에서 동작하지 않으므로, 클라이언트 콘솔(~)을 열고 open [AWS_퍼블릭_IP]:7777 명령어로 다이렉트 접속 테스트 성공.
  • 방장 권한의 비밀: 데디케이티드 서버 환경에서는 [방 생성] 버튼을 누르든 콘솔로 접속하든, 빈 서버에 가장 먼저 접속한 사람(JoinOrder 1번)이 방장 권한을 획득함.

3. Null(LAN) 시스템의 한계와 EOS 연동의 필요성

오늘 가장 큰 깨달음을 얻은 부분. 왜 잘 만들어진 [방 검색] UI가 AWS 서버를 찾지 못했는가?

  • 현재 (OnlineSubsystemNull): - FindSessions 실행 시, 로컬 공유기(LAN) 내부에 브로드캐스트를 날려 서버를 찾음. (“동네방네 소리치기”)
    • 외부 인터넷망에 있는 AWS 서버에는 신호가 닿지 않아 UI 목록에 표시되지 않음.
  • 미래 (OnlineSubsystemEOS):
    • 서버가 구동될 때 에픽게임즈 중앙 서버(매치메이킹 서버)에 자신의 IP를 스스로 등록함. (“공용 게시판 전단지 붙이기”)
    • 클라이언트가 FindSessions 호출 시, 로컬 망이 아닌 에픽게임즈 서버의 게시판을 읽어 방 목록을 가져옴.
    • 결론: IP가 매번 바뀌어도 개발자가 수동으로 등록할 필요 없이 코드가 알아서 처리해주며, 인터넷 환경에서도 UI의 ‘방 목록’이 정상 작동하게 됨.

내일 할 일 (To-Do)

  • AWS EC2 사용 종료 후 과금 방지를 위해 반드시 인스턴스 [중지] 상태 확인하기.
  • 현재 작성된 세션 UI 코드 구조 유지하면서 Phase 5 (EOS 연동) 작업 준비하기.

회고

처음엔 코드가 잘못된 줄 알고 당황했지만, 네트워크 검색 방식의 근본적인 차이(LAN vs 매치메이킹 서버)를 이해하고 나니 로드맵의 다음 단계가 왜 필요한지 명확해졌다. 로그 보는 법과 명령 줄 인수 활용법을 익힌 것도 큰 수확!

[TIL] Unreal Engine 5: 카오스 물리(Chaos Physics) 파괴 연출 시 즉시 증발 및 추락 문제 해결


1. 배경 및 문제 상황

러닝 게임에서 플레이어가 라인트레이스(총)로 장애물을 쏠 때, 파편이 튀며 산산조각 나는 연출(Geometry Collection)을 구현 중이었다. HP가 0이 되면 SetLifeSpan(3.0f)을 주어 3초 뒤에 액터가 삭제되도록 C++ 코드를 작성했으나, 두 가지 심각한 버그가 발생했다.

  • 문제 1 (즉시 증발): 총으로 쏘자마자 3초를 기다리지 않고 파편이 즉시 화면에서 사라짐.
  • 문제 2 (무한 추락): 파편들이 바닥(땅)에 부딪히지 않고 그대로 바닥을 뚫고 쑥 떨어져 버림.

2. 원인 분석

원인 1: 강한 임펄스 + 콜리전 꺼짐 → KillZ(월드 밖) 판정으로 자동 Destroy

  • 파괴 시점에 조각을 흩어지게 하려고 Add Radial Impulse의 Strength를 500,000으로 매우 강하게 설정했음.
  • 이때 조각의 콜리전이 꺼져 있어서 바닥을 뚫고 지나갔고, 엄청난 속도로 월드 밑바닥(KillZ)을 벗어나버림.
  • 결과적으로 엔진이 비정상 이탈로 간주하여 강제로 Destroy 시켜버림 (EndPlay Reason=0). 즉, SetLifeSpan이 작동하기 전에 액터가 삭제된 것.

원인 2: 물리 파편의 콜리전 미설정

  • 원본 메시를 숨기고 지오메트리 컬렉션을 보여줄 때, 파편 조각들이 바닥(WorldStatic)을 인식하게 만들어야 하는데 NoCollision 상태로 두었음. 이로 인해 땅에 쌓이지 않고 그대로 추락함.

3. 해결책 및 구현 흐름

문제를 해결하기 위해 블루프린트(BP)의 노드 순서를 바로잡고, 임펄스 수치를 현실적으로 조정했다.

올바른 Blueprint (BP_PlayDestruction) 노드 순서

1
2
3
4
5
6
7
8
Event BP_PlayDestruction
  ├─> Set Visibility (True)                       // 파편 보이게 활성화
  ├─> Set Simulate Physics (True)                 // 물리 엔진 켜기
  ├─> Set Collision Enabled (Query and Physics)   // ★ 핵심: 파편이 땅을 인식하도록 콜리전 켜기
  ├─> Crumble Active Clusters                     // 클러스터 강제 분해
  └─> Add Radial Impulse 
        - Strength    = 3000                      // ★ 핵심: 50만에서 3천으로 대폭 낮춤 (우주 밖으로 날아감 방지)
        - bVel Change = True

📌 C++ 로직 흐름 (정상 작동 확인)

C++ 구조 자체는 처음부터 문제가 없었으며, 철저하게 서버/클라이언트 및 로직과 연출을 분리한 구조다.

  1. 사격 명중: FireByAimRay → 장애물 HP 감소 → 0 도달
  2. HandleDeath() (서버): Multicast_PlayDestruction() 호출 → 델리게이트 브로드캐스트 → SetLifeSpan(3.0f) 설정
  3. Multicast_PlayDestruction() (서버+클라): 원본 메시 숨김 & 콜리전 끔 → BP_PlayDestruction() 이벤트 호출
  4. BP 파괴 연출: 위에서 세팅한 순서대로 물리 파괴 연출 실행
  5. 정리: 3초 뒤 액터 전체 Destroy

4. 추가 이슈: 파편이 플레이어에게 데미지를 주거나 길을 막는다면?

파편에 Query and Physics를 켜면서 물리가 적용되었기 때문에, 이 파편들이 캐릭터를 밀거나 러닝 액션에 방해를 줄 수 있는 가능성이 생겼다.

Q. 파편에 맞으면 데미지를 입는가?

A. 입지 않는다. 데미지 판정은 원본 CollisionBox의 델리게이트를 통해 발생하는데, 파괴되는 순간(HandleDeath) 이 투명 박스의 콜리전과 Overlap 이벤트를 모두 꺼버리기 때문에 데미지 판정은 들어가지 않는다.

Q. 파편이 길을 막거나 밀어내는 물리적 방해는?

A. 발생할 수 있다. (해결 필요) 파편이 지형(바닥)에는 부딪히되, 플레이어(캐릭터)는 통과하도록 콜리전 채널을 세팅해야 한다.

[해결 방법] 지오메트리 컬렉션(Geometry Collection) 컴포넌트의 Collision Responses 세팅을 다음과 같이 조정한다.

  • WorldStatic = Block (바닥에 떨어져서 쌓임)
  • Pawn = Ignore (플레이어 캐릭터는 파편을 그냥 뚫고 지나감)

5. 오늘 얻은 디버깅 교훈

  1. “내 코드를 추가하니까 이상해졌다”는 직감 믿기 문제가 발생했을 때, 새로 추가한 C++ 함수(Multicast_PlayDestruction) 안의 로직을 하나씩 주석 처리해 가며 범인을 좁혀간 것이 가장 빠르고 정확한 접근이었다.
  2. 변수 통제의 중요성 BP 노드를 하나씩 빼보면서 “임펄스를 빼니 추락만 하고”, “콜리전을 켜니 정상 작동한다”는 식으로 증상 변화를 체크한 것이 핵심이었다.
  3. EndPlay Reason 활용 디버깅 시 로그에 찍힌 파괴 이유(Reason=0, 수동 파괴)를 확인하여 타이머에 의한 자연 소멸이 아님을 깨달은 것이 훌륭한 힌트가 되었다.
This post is licensed under CC BY 4.0 by the author.