Post

2026-05-12 TIL (55일차)

2026-05-12 TIL (55일차)

터널 진입 로직 - 델리게이트(Delegate)를 활용한 의존성 분리

오늘은 자율주행 차량이 터널에 진입했을 때(어두운 환경, GPS/Lidar 노이즈 발생 등) 각 센서와 주행 컴포넌트들의 수치를 변경하는 로직을 설계했다.

처음 기획했던 방식에서 벗어나, 언리얼 엔진의 멀티캐스트 델리게이트(Multicast Delegate)를 활용하여 컴포넌트 간의 결합도를 획기적으로 낮춘 리팩토링 과정을 기록한다.


기존 설계의 문제점: 강한 결합

처음 기획한 로직은 다음과 같았다. 트리거 감지 -> Pawn 이동 -> Pawn이 Camera, Lidar, SplineFollower 컴포넌트를 직접 참조하여 각각의 ApplyTunnelProfile() 함수를 수동으로 호출

이 방식은 직관적이지만 치명적인 단점이 있다. Pawn이 자기 몸에 붙어있는 모든 컴포넌트의 존재를 알고 있어야 한다는 것이다. 만약 나중에 ‘Radar 센서’나 ‘오디오 컴포넌트’가 추가된다면, 터널에 들어갈 때마다 Pawn 클래스의 코드를 열어서 함수 호출 줄을 계속 추가해야 한다. (유지보수 최악의 형태)

개선된 설계: 방송국(Pawn)과 구독자(Components)

이 문제를 해결하기 위해 델리게이트(Delegate)를 활용한 이벤트 기반(Event-Driven) 아키텍처로 설계를 변경했다. Pawn은 그저 “터널에 들어왔다!”라고 방송(Broadcast)만 하고, 각 컴포넌트들이 이 방송을 스스로 듣고 각자의 수치를 변경하도록 책임을 분리했다.


변경된 터널 진입/이탈 로직 흐름도

1단계: 터널 입구에서의 신호 포착 (감지 파트)

먼저 레벨(맵)에 배치된 터널 트리거(BP_TunnelTrigger)가 안테나 역할을 한다. 차량이 터널 입구에 설치된 투명한 콜리전 박스를 통과하는 순간 OnActorBeginOverlap 이벤트가 발생한다. 트리거는 닿은 물체가 자동차(Pawn)가 맞는지 캐스팅(Cast)하여 확인한 뒤, 맞다면 Pawn의 SetInTunnel(true) 함수를 호출하여 진입 사실을 보고한다.

(참고: 여러 트리거 박스가 겹쳐 있을 경우를 대비해 내부적으로 TunnelOverlapCount를 증감시키는 카운터 패턴을 사용하여 겹침 버그를 방지했다.)

2단계: 각 컴포넌트에게 방송 (델리게이트 파트)

진입 또는 이탈이 최종 확인되면, 차량(Pawn)은 델리게이트 시스템을 통해 모든 컴포넌트에게 상태 변화를 알린다.

Pawn 헤더 파일에 아래와 같이 채널을 정의해 둔다.

1
2
3
4
5
// 1. 사용할 방송 채널의 규격(Parameter 타입) 정의
DECLARE_MULTICAST_DELEGATE_OneParam(FOnTunnelStateChangedDelegate, bool);

// 2. 다른 파일들이 구독할 수 있는 공개 방송국 변수 생성
FOnTunnelStateChangedDelegate OnTunnelToggleDelegate;

이후 SetInTunnel 함수 내부에서 카운터가 변동될 때마다 OnTunnelToggleDelegate.Broadcast(true/false) 명령을 쏜다. 이 방송은 “지금 터널 상태가 바뀌었으니, 구독자들은 각자 할 일을 해라!”라는 전역 명령과 같다.

3단계: 각 부품의 자율적인 대응 (컴포넌트 파트)

각 센서 컴포넌트들은 게임이 시작될 때(BeginPlay) Pawn의 방송 채널에 구독 신청(AddUObject)을 해둔다.

1
2
3
4
5
6
// 컴포넌트의 BeginPlay 내부 예시
if (OwnerPawn)
{
    // 방송이 울리면 내 클래스의 ApplyTunnelProfile 함수를 실행하라고 연결
    OwnerPawn->OnTunnelToggleDelegate.AddUObject(this, &USplineFollowerComponent::ApplyTunnelProfile);
}

이제 Broadcast(true)가 실행되면, Pawn이 굳이 명령하지 않아도 각 컴포넌트는 스스로 ApplyTunnelProfile을 실행한다.

4단계: 터널 탈출 후 자동 복구

차량이 터널 끝에 도달해 트리거 박스를 완전히 벗어나면, OnActorEndOverlap이 발생하며 다시 Pawn에게 SetInTunnel(false)를 호출한다.

중앙 통제실(Pawn)은 중첩 카운터가 0이 된 것을 확인하고 Broadcast(false) 방송을 보낸다. 이를 수신한 각 컴포넌트들은 캐싱해두었던 원래의 기본 수치들로 시스템을 복구한다.

요약

“중앙 집중식 통제(Direct Call)에서 벗어나, 이벤트 구독(Delegate) 방식으로 전환했다.”

This post is licensed under CC BY 4.0 by the author.