컨텐츠 검색
[UE5 BP] _#9. 언리얼 엔진 블루프린트 통신 (3) - 액터 구하기

2025. 11. 27. 01:26Unreal Engine/개념

1. GameMode 중심 Blueprint 통신으로 Final Door 열기 구현하기

1.1. 🧩 문제 상황

UI는 Dispatcher를 통해 수집품 변화에 반응하도록 완성했다.
그러나 "마지막 문을 언제, 어떻게 열 것인가?" 가 남았다.

문제를 더 구체적으로 보면:

  • 마지막 문(BP Door Final)은 GameMode 내부에 존재하지 않는다.
  • GameMode는 레벨에 배치되지 않고 런타임에 스폰되는 객체이다.
  • 문은 레벨에 직접 배치된 액터이기 때문에
    GameMode는 디자인 타임에 문 참조를 끌어다 놓을 수 없다.
  • 문 액터는 수집품처럼 “등록(register)” 패턴을 쓰기 애매하다.
    → 문은 여러 개가 아니라 정확히 1개이며,
    플레이어와 직접 상호작용하는 액터도 아님.

즉, GameMode가 Count 변화 시점에 문을 열어야 하는데,
문에 대한 참조를 어떻게 얻을지가 핵심 문제
였다.


1.2. 🎯 구현 목적

  • 모든 코인을 수집한 순간 자동으로 문 열기
  • GameMode가 문 액터에 접근할 방법을 확보해야 한다.
  • 반복 스캔 없이 성능 부담이 없는 구조로 만들기
  • 문 클래스를 변경하거나 레벨을 재구성하더라도 코드를 최소 수정하도록 유연한 구조 만들기
  • UI, 수집품, 문이 서로 강하게 연결되지 않도록 GameMode 중심의 느슨한 구조 구축

1.3. 📌 액터 식별 전략(태그/클래스) 및 선택 이유

문을 찾는 방식은 크게 두 종류다.

① 태그 기반 검색

  • 문에 FinalDoor 같은 태그를 붙이고 Get All Actors with Tag 를 사용
  • 장점
    • 디자이너가 태그만 설정하면 Blueprint 변경 없이 작동
    • 다양한 문을 태그별로 구분 가능
  • 단점
    • 태그 미설정 시 에러 발생 가능
    • 문이 여러 개면 정확한 문을 고르기 위한 추가 필터 필요

② 클래스 기반 검색(Get Actor Of Class)

  • 레벨에 배치된 BP Door Final 클래스 인스턴스를 찾음
  • 장점
    • 클래스 구조만 유지하면 디자인 실수(태그 누락) 걱정 없음
    • “이 문은 하나다”라는 전제가 맞으면 정교하면서도 안정적
  • 단점
    • 복수 인스턴스가 존재하면 배열 필터 필요

→ 이번 프로젝트에서는 문이 “정확히 하나”이므로 클래스 기반이 최적 선택


1.4. 📌 Get Actor 계열 함수의 특성과 한계

Get Actor 계열 함수 목록

  • Get All Actors of Class
  • Get All Actors with Tag
  • Get All Actors with Interface
  • Get All Actors with Component
    (특정 컴포넌트를 가진 액터 검색)

작동 방식 요약

  • 호출 시 레벨 전체 액터를 스캔한다.
  • 조건에 맞는 액터를 모두 배열(Array)에 담아 반환한다.
  • 즉, 매우 편리하지만 비용이 큰 함수이다.

언제 사용하면 괜찮은가?

  • 게임 시작 시 (“한 번만”)
  • 특정 이벤트 발생 시 (“가끔”)
  • 물리적으로 액터 수가 많지 않은 작은 레벨

언제 쓰면 안 되는가?

  • Tick
  • 루프 안
  • 반복적인 Polling 구조
  • 대규모 씬에서 다수의 Actor가 존재할 때

→ 현재 프로젝트에서는 “Count==0일 때 한 번만 호출”이므로 최적 사용 상황


1.5. 📌 GameMode에서 문 액터 접근 구조

GameMode가 문을 찾는 흐름은 다음과 같은 이유 때문에 필요했다.

① GameMode는 레벨에 배치되지 않는다

  • GameMode는 월드가 시작될 때 엔진이 자동 생성하는 객체다.
  • 레벨 위젯이나 액터처럼 “월드 아웃라이너”에 존재하지 않는다.

② GameMode는 문에 대한 디자인 타임 참조를 가질 수 없다

  • 문은 레벨에 놓여 있기 때문에,
    GameMode 블루프린트 내부에서 “씬 액터를 참조 슬롯에 끌어넣기”가 불가능하다.

③ 문이 스스로 GameMode에 등록하는 방식은 적절하지 않다

  • 마지막 문은 레벨 디자인 요소이지
    시스템적으로 "등록 대상"이 되지 않기 때문이다.
  • 하나뿐인 문이 등록 패턴을 사용하면 오히려 구조가 복잡해진다.

따라서 GameMode는 “문이 필요할 때 직접 찾아오는 방식(GetActorOfClass)” 을 필요로 한다.


1.6. ⚙️ 전체 처리 흐름 (Dispatcher 이후 단계)

아래는 UI 업데이트 이후, GameMode 중심 문 열기까지의 전체 흐름이다.


① GameMode – CollectibleCountUpdated_Event 호출

Dispatcher는 Count 값을 전달하며 GameMode의 커스텀 이벤트를 실행한다.

  • 입력 파라미터 Count = 남은 코인 개수
  • GameMode는 이 값을 토대로 이후 로직을 실행한다.

② Count ≤ 0 비교 → Branch 처리

  • “모든 코인이 수집되었는가?”를 판단하는 조건문
  • <= 를 사용하는 이유:
    • 등록과 감소 과정에서 순서가 조금 앞뒤가 바뀌더라도 정확하게 문이 열리는 시점을 확보할 수 있음.

③ Branch True → Open Final Door 이벤트 호출

Count가 0 이하이면 GameMode 내부의 OpenFinalDoor 커스텀 이벤트를 호출

  • 이 시점이 문을 실제로 여는 로직의 시작점

④ OpenFinalDoor – 마지막 문 액터 검색

OpenFinalDoor의 내부 로직:

  1. Get Actor Of Class (BP Door Final)
    • 레벨에서 Final Door 클래스를 가진 액터를 모두 찾음
    • 배열의 Index 0이 바로 마지막 문
  2. 반환값 null 체크
    • 문이 존재하지 않거나 레벨에 배치되지 않았을 경우 Crash 방지
    • IsValid가 필수인 경우(null 가능성 높은 경우)
      함수 설명
      Get Actor Of Class Actor가 맵에 없으면 null
      레벨에 항상 하나만 둘 때는 생략 가능
      Get All Actors Of Class (Get[0]) 배열이 비면 null
      Cast To (Return Value) 캐스트 실패하면 null
      Find Component / Get Component By Class 없는 컴포넌트면 null
      Line Trace Hit → Hit Actor 아무것도 안 맞으면 null
      GetPlayerCharacter / GetPlayerController
      (특히 멀티플레이)
      플레이어 생성 전이면 null
      Widget Get / HUD Get Construct 되기 전이면 null
      기타 BeginPlay 초기 참조 요소 로딩 순서로 null일 수 있음
  3. 반환된 문 액터를 Target으로

⑤ 문 블루프린트의 Open Door 호출

  • 문 내부에 구현된 Open Door 이벤트(또는 함수)를 호출
  • 문 내부의 오픈 로직 예:
    • Timeline 재생
    • Static Mesh 회전 애니메이션
    • Collision 비활성화
    • 사운드 재생
    • VFX 재생

문은 “열리는 행동”만 구현하면 되고 GameMode가 “언제 열릴지”를 결정하는 구조를 갖추게 된다.


1.7. 💡 배운 점

1) 블루프린트 통신의 핵심 본질: 참조 접근(Reference Access)

Dispatcher는 “알림 신호”이고 실제 행동을 수행하려면
대상 액터에 대한 정확한 참조가 반드시 필요함을 이해했다.

2) GameMode는 레벨 액터와 분리된 존재이다

  • 다른 시스템처럼 “씬 안에서 끌어다 참조”가 불가능하다.
  • 따라서 검색(get) 방식 또는 등록(register) 방식 중 적절한 것을 선택해야 한다.

3) GetActor는 잘 쓰면 강력하지만, 남용하면 성능을 해칠 수 있다

  • 이번에는 “딱 한 번 호출되는 상황”이라 최적
  • Tick, 루프 안에는 절대 금지

4) 문을 등록시키는 방식보다, 클래스로 찾는 방식이 더 적합한 구조였다

  • 문이 여러 개가 아니다
  • 디자인 작업에 태그 실수 위험을 줄이고 싶다
  • 클래스 기반 검색으로 구조를 단순하게 유지할 수 있었다

5) GameMode 중심의 느슨한 결합 구조가 완성되었다

  • UI는 Dispatcher로 Count 변화만 듣는다
  • 문은 GameMode의 결정에 따라 Open Door만 실행한다
  • Collectible은 변화를 GameMode에 알릴 뿐 다른 시스템을 모른다
  • 서로 직접 연결되지 않기 때문에 이후 기능 확장(키로 문 열기, 퍼즐 조건 추가 등)에 유리하다