상호작용 → 대화 흐름
- 프롬프트 표시
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. 프롬프트 한 개만 띄우기
UMVUISubsystem은 ActivePopup, ActiveInteractionPrompt, ActiveDialoguePopup 3개를 각각 추적함. 팝업 생성은 PushPopupByClass()를 거치는데 이 안에서 기존 ActivePopup을 먼저 닫고 새 팝업을 띄움
ShowInteractionPrompt()는 Dialogue가 떠있으면 새 프롬프트를 만들지 않음
if (IsPopupActive(ActiveDialougePopup)){ return nullptr;}- 상호작용 입력 흐름
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()로 텍스트 설정
- Popup 생명주기
모든 Popup은
UMVPopupBase기반. ActivatableWidget이 아니므로 입력 모드를 바꾸거나 마우스를 잠그는 책임이 없음. Popup이 생성되면NativeConstruct()에서 FadeIn이 시작되고,AutoDismissSeconds가 있으면 타이머가 시작됨. 타이머가 끝나면HandleAutoDismissElapsed()→ClosePopup()으로 가고,ClosePopup()은 팝업을 바로 제거하지 않고 FadeOut 후RemoveFromParent()호출함.RemoveFromParent()에서OnPopupClosed델리게이트가 broadcast되고UMVUISubsystem::HandlePopupClosed()에서 이 신호를 받아ActivePopup,ActiveDialoguePopup,ActiveInteractionPrompt포인터를 정리함 - Interactable 후보가 여러개일 때
UMVInteractionDetectorComponent::SelectInteractableByOffset()함수로InteractionCandidates를 순회하며 NewIndex를 계산해서SetSelectedCandidateIndex(NewIndex)를 호출함.SetSelectedCandidateIndex()에선SetFocusedInteractable()를 호출하며 이 함수에선 현재 대화중인 대상이 있는지, 있다면 반경 안팎인지 확인해 Dialogue를 종료하거나 포커스 변경을 금지하고, 묶어뒀던 상호작용 점유권을 해제한 뒤 포커스 변경을 시도함. - Dialogue 종료 조건
- 표시 시간이 끝남:
AutoDismiss타이머가 종료 이후에ClosePopup()을 호출함. - 직접 스킵:
UMVUISubsystem::SkipDialoguePopup()을 입력에 바인딩해HideDialoguePopup()을 명시적으로 호출함(아직 바인딩 안돼있음) - Detector 반경 밖으로 나감:
RefreshInteractable()에서SuppressedInteractable이 존재하고 Dialogue가 Active이면IsInteratableWithinDetectionRange()로 반경 내에 있는지 검사함. 밖이면ReleaseSuppressedInteractable(true)가 호출되고HideActiveDialoguePopup()이 호출됨. 이 때 시야각을 벗어난다고 Dialogue가 닫히진 않음. DetectionRange 밖으로 벗어났을 때만 종료됨
- 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의 함수를 호출해 사용할 수 있게 추상화했음
카메라 줌, 상호작용 키 연타 못하게 최소 스킵 불가시간 추가