프로젝트 매버릭 개발일지 14일차

June 17, 2026 (13d ago)

상호작용 → 대화 흐름

  1. 프롬프트 표시 BP_ThirdPersonCharacter에 붙은 MVInteractionDetectorComponent가 Tick마다 RefreshInteractable() 호출. DetectionInterval(기본값 0.1)마다 감지함. OverlapMultiByObjectType으로 반경 내 컴포넌트를 찾고 TryBuildCandidate()로 최종 상호작용 후보 검증함
  • MVInteractableInterface 구현체가 있는지
  • CanInteract()가 true인지
  • Detector 반경 안인지
  • bUseViewCone이 켜져있다면 시야각 안인지
  • bRequireLineOfSight가 켜져있다면 시야 선이 막혀있지 않은지

이후 점수 계산하여 최고 점수를 가진 대상이 프롬프트에 뜨게 됨

Priority(우선순위) * 100000 + ViewDot(보는 방향) * 1000 + DistanceScore(대상과의 거리) * 100

최종 후보가 정해지면 UpdateInteractionPrompt()FMVInteractionPromptData 구조체 안에 값을 채워 UISubsystem으로 전달함(UMVUISubsystem::ShowInteractionPrompt()) 2. 프롬프트 한 개만 띄우기 UMVUISubsystemActivePopup, ActiveInteractionPrompt, ActiveDialoguePopup 3개를 각각 추적함. 팝업 생성은 PushPopupByClass()를 거치는데 이 안에서 기존 ActivePopup을 먼저 닫고 새 팝업을 띄움

ShowInteractionPrompt()는 Dialogue가 떠있으면 새 프롬프트를 만들지 않음

if (IsPopupActive(ActiveDialougePopup)){	return nullptr;}
  1. 상호작용 입력 흐름 BP 쪽에서 IA_Interact 입력이 들어오면 TryInteract()가 호출됨. 이 함수는 최신 후보를 갱신하고 현재 FocusedInteractable이 유효한지 확인함. 그 다음 로직 순서:
  • UISubsystem->HideInteractionPrompt()
  • SuppressedInteractable = InteractableObject 저장(조건 필터링 후 최종 후보를 상호작용 대상으로 묶는 과정)
  • IMVInteractableInterface::Execute_Interact() 호출(인터페이스 구현체 가지고 있는 대상의 상호작용 함수 호출 지시)
  • 다시 RefreshInteractable()

이러면 실제 BP_Carcass 등에서 On Interaction Requested 이벤트에 연결된 ShowDialoguePopup()을 호출해 Dialogue를 띄움 4. DialoguePopup 생성 UMVUISubsystem::ShowDialoguePopupText() 함수는 다음과 같이 동작함

  • 먼저 HideInteractionPrompt()를 호출해 현재 재생 중인 상호작용 프롬프트 닫기
  • 이미 DialoguePopup이 떠 있으면 새로 만들지 않고 텍스트/타이머만 갱신
  • 없으면 DialoguePopupClass로 새 Popup 생성
  • SetAutoDismissSeconds()로 종료 시간 설정
  • SetDialogueText()로 텍스트 설정
  1. Popup 생명주기 모든 Popup은 UMVPopupBase 기반. ActivatableWidget이 아니므로 입력 모드를 바꾸거나 마우스를 잠그는 책임이 없음. Popup이 생성되면 NativeConstruct()에서 FadeIn이 시작되고, AutoDismissSeconds가 있으면 타이머가 시작됨. 타이머가 끝나면 HandleAutoDismissElapsed()ClosePopup() 으로 가고, ClosePopup()은 팝업을 바로 제거하지 않고 FadeOut 후 RemoveFromParent() 호출함. RemoveFromParent()에서 OnPopupClosed 델리게이트가 broadcast되고 UMVUISubsystem::HandlePopupClosed()에서 이 신호를 받아 ActivePopup, ActiveDialoguePopup, ActiveInteractionPrompt 포인터를 정리함
  2. Interactable 후보가 여러개일 때 UMVInteractionDetectorComponent::SelectInteractableByOffset() 함수로 InteractionCandidates를 순회하며 NewIndex를 계산해서 SetSelectedCandidateIndex(NewIndex)를 호출함. SetSelectedCandidateIndex()에선 SetFocusedInteractable()를 호출하며 이 함수에선 현재 대화중인 대상이 있는지, 있다면 반경 안팎인지 확인해 Dialogue를 종료하거나 포커스 변경을 금지하고, 묶어뒀던 상호작용 점유권을 해제한 뒤 포커스 변경을 시도함.
  3. Dialogue 종료 조건
  • 표시 시간이 끝남: AutoDismiss 타이머가 종료 이후에 ClosePopup()을 호출함.
  • 직접 스킵: UMVUISubsystem::SkipDialoguePopup()을 입력에 바인딩해 HideDialoguePopup()을 명시적으로 호출함(아직 바인딩 안돼있음)
  • Detector 반경 밖으로 나감: RefreshInteractable()에서 SuppressedInteractable이 존재하고 Dialogue가 Active이면 IsInteratableWithinDetectionRange()로 반경 내에 있는지 검사함. 밖이면 ReleaseSuppressedInteractable(true)가 호출되고 HideActiveDialoguePopup()이 호출됨. 이 때 시야각을 벗어난다고 Dialogue가 닫히진 않음. DetectionRange 밖으로 벗어났을 때만 종료됨
  1. Dialogue 종료 후 Prompt 복귀 Dialogue가 종료되면 UISubsystem의 ActiveDialoguePopup이 정리되고, 이후 다음 RefreshInteractable()에서 SuppressedInteractable은 점유해제되지 않았기 때문에 ReleaseSuppressedInteractable(false)로 점유를 해제함. 이후엔 다음 틱에서 UpdateInteractionPrompt()를 호출하고 ShowInteractionPrompt()가 새 프롬프트를 FadeIn으로 띄워 자연스럽게 복구됨.

DialoguePopup → DialogueWindow

엘든링 테스트해보니까 다이얼로그가 윈도우였음. 뭔말이냐면 팝업은 이동, look 모두 안막히고 자유롭게 게임플레이가 가능한데, 윈도우는 이동은 가능하지만 look은 막힌, 안막히게 할 수도 있지만 어쨌든 입력 모드를 제한하는 특수한 위젯임.

게임패드 우측스틱으로 카메라 회전이 가능한데, 마우스는 안되게 막았음

상호작용 거리는 250이라면 대화 탈출거리는 500쯤 되어보임

그래서 DialoguePopup을 DialogueWindow로 바꾸고, PopupBase에서 ActivatableWidgetBase로 바꿨음

Detection Range 300cm, ViewCone 각도 75도 안에 들어오면 초록색으로 상호작용 후보 탐색됨

Dialogue 시작되면 상호작용 대상으로부터 600cm의 DialogueEscapeRange가 설정되며 이 원을 벗어나면 즉시 dialogue가 종료됨

Dialogue 진행 시 상호작용 키를 눌러 현재 재생되고 있는 dialogue를 스킵할 수 있는 것도 부착함

Fade In/Out 처리는 UIFadeController 공용 모듈을 만들어서 WidgetBase, ActivatableWidgetBase 관계없이 FadeController의 함수를 호출해 사용할 수 있게 추상화했음

카메라 줌, 상호작용 키 연타 못하게 최소 스킵 불가시간 추가