NPC는 던전 내에 일정 확률로 등장하게 되는데
가까이 다가가면 말을 걸 수 있게 T 표시가 나오며
T를 누를 경우 다이얼로그에 대화창이 나오면서 예 아니오를 눌러 선택할 수 있게 해보자.
NPC 블루 프린트
NPC는 CharacterBase를 상속 받는다.
물론 지금은 움직일 수 없는 오브젝트로 만들 계획이기 때문에 필요 없을 수 있지만
나중에 움직일 수 있는 NPC를 제작할 수 도 있기에 그렇게 설계 했다.

NPC랑 대화할 때 카메라 줌인을 위해 NPC에게 전용 카메라를 두었다.
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Character/RASCharacterBase.h"
#include "RASNpc.generated.h"
/**
*
*/
UCLASS()
class PROJECTRAS_API ARASNpc : public ARASCharacterBase
{
GENERATED_BODY()
public:
ARASNpc();
void InteractionUIOn();
void InteractionUIOff();
void InteractionWithPlayer();
void InteractionWithPlayerEnd();
virtual void BeginPlay() override;
virtual void Tick(float DeltaSeconds) override;
void AcceptInteraction();
void CancelInteraction();
FString& GetNpcText() { return NpcText; }
protected:
UPROPERTY(EditAnywhere)
TObjectPtr<class UWidgetComponent> NPCInteractionWidget;
UPROPERTY(EditAnywhere)
TObjectPtr<class UCameraComponent> NPCInteractionCamera;
UPROPERTY(EditAnywhere)
FString NpcText;
TObjectPtr<class ARASPlayer> Player;
TWeakObjectPtr<AActor> OriginalViewTarget;
bool bIsInteracting = false;
bool bIsHealPotion = false;
};
헤더는 이렇게 작성해준다.
NPC와 상호작용한 거리에 도달했을 경우 그것을 표기 하기 위해 위젯을 추가한다.

이 것을 위젯 컴포넌트로 Screen으로 설정해서 위에 띄우도록 하자.
Cpp 코드
void ARASNpc::InteractionWithPlayer()
{
if (bIsHealPotion) return;
FVector PlayerLocation = Player->GetActorLocation();
FVector NpcLocation = GetActorLocation();
float Distance = FVector::Dist(PlayerLocation, NpcLocation);
if (Distance < 200.f)
{
bIsInteracting = true;
InteractionUIOff();
// 카메라 전환
if (NPCInteractionCamera)
{
APlayerController* PlayerController = GetWorld()->GetFirstPlayerController();
OriginalViewTarget = PlayerController->GetViewTarget();
if (PlayerController)
{
PlayerController->SetViewTargetWithBlend(this, 0.5f);
}
}
Player->GetUIComponent()->ShowNpcUI(this);
}
}
void ARASNpc::InteractionWithPlayerEnd()
{
bIsInteracting = false;
// 카메라 복원
APlayerController* PlayerController = GetWorld()->GetFirstPlayerController();
PlayerController->SetViewTargetWithBlend(OriginalViewTarget.Get(), 0.5f);
}
중요한 건 이 부분이다. 플레이어가 상호작용을 원할 때(T를 눌러) 플레이어와 NPC의 거리를 측정하고 가능한 거리라면 카메라 뷰를 전환 시켜준다.
그리고 NPC 스크립트 UI를 띄워준다.
상호작용이 끝나면 다시 원래대로 카메라를 복원하게 된다.
void ARASNpc::AcceptInteraction()
{
Player->GetUIComponent()->HideNpcUI();
Player->GetCombatComponent()->RecoverPotion();
bIsHealPotion = true;
InteractionWithPlayerEnd();
}
void ARASNpc::CancelInteraction()
{
Player->GetUIComponent()->HideNpcUI();
InteractionWithPlayerEnd();
}
예, 아니오 버튼을 눌렀을 때 작동할 함수이다.
포션을 회복해주고 상호작용을 끝내는 것이다.
UI
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "RASNpcUI.generated.h"
/**
*
*/
UCLASS()
class PROJECTRAS_API URASNpcUI : public UUserWidget
{
GENERATED_BODY()
public:
URASNpcUI(const FObjectInitializer& ObjectInitializer);
void Setup(class ARASNpc* InOnwerNpc);
void StartTyping(const FString& InText, float InSpeed);
void AddNextCharacter();
UFUNCTION(BlueprintCallable, Category = "NPC UI")
void ClickYes();
UFUNCTION(BlueprintCallable, Category = "NPC UI")
void ClickNo();
protected:
FTimerHandle TypingTimerHandle;
FString FullText;
int32 CurrentIndex = 0;
float TypingSpeed = 0.05f;
UPROPERTY(meta=(BindWidget))
TObjectPtr<class UTextBlock> NpcText;
UPROPERTY(meta=(BindWidget))
TObjectPtr<class UButton> YesButton;
UPROPERTY(meta=(BindWidget))
TObjectPtr<class UButton> NoButton;
TObjectPtr<class ARASNpc> OwnerNpc;
};

이런식으로 구성되어 있다.
// Fill out your copyright notice in the Description page of Project Settings.
#include "UI/RASNpcUI.h"
#include "Components/TextBlock.h"
#include "Components/Button.h"
#include "Character/Npc/RASNpc.h"
#include "Audio/RASAudioSubsystem.h"
URASNpcUI::URASNpcUI(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
}
void URASNpcUI::Setup(class ARASNpc* InOnwerNpc)
{
OwnerNpc = InOnwerNpc;
if (NpcText)
{
NpcText->SetText(FText::FromString(TEXT("")));
}
if (YesButton)
{
YesButton->SetVisibility(ESlateVisibility::Collapsed);
}
if (NoButton)
{
NoButton->SetVisibility(ESlateVisibility::Collapsed);
}
}
void URASNpcUI::StartTyping(const FString& InText, float InSpeed)
{
FullText = InText;
FullText.ReplaceInline(TEXT("\\n"), TEXT("\n"));
CurrentIndex = 0;
TypingSpeed = InSpeed;
GetWorld()->GetTimerManager().SetTimer(TypingTimerHandle, this, &URASNpcUI::AddNextCharacter, TypingSpeed, true);
}
void URASNpcUI::AddNextCharacter()
{
if (CurrentIndex < FullText.Len())
{
FString Sub = FullText.Left(CurrentIndex + 1);
NpcText->SetText(FText::FromString(Sub));
GetGameInstance()->GetSubsystem<URASAudioSubsystem>()->PlaySFX(TEXT("Typing"), OwnerNpc->GetActorLocation());
CurrentIndex++;
}
else
{
GetWorld()->GetTimerManager().ClearTimer(TypingTimerHandle);
YesButton->SetVisibility(ESlateVisibility::Visible);
NoButton->SetVisibility(ESlateVisibility::Visible);
}
}
void URASNpcUI::ClickYes()
{
if (OwnerNpc)
{
OwnerNpc->AcceptInteraction();
}
}
void URASNpcUI::ClickNo()
{
if (OwnerNpc)
{
OwnerNpc->CancelInteraction();
}
}
타이핑 하는 기능을 추가해서 원하는 대사를 타이핑 할 수 있다.
결과
'Unreal Engine 5 > ProjectRAS' 카테고리의 다른 글
| [UE5] ProjectRAS - 미니맵, 월드맵 만들기 (1) | 2025.06.23 |
|---|---|
| [UE5] ProjectRAS - 메인 Title UI와 메뉴 UI 만들기 (0) | 2025.06.11 |
| [UE5] ProjectRAS - 몬스터 스폰하기, Clothes 적용하기 (0) | 2025.05.28 |
| [UE5] ProjectRAS - UI 스킬 쿨타임 과 포션 마시기 (0) | 2025.05.21 |
| [UE5] ProjectRAS - 보스 패턴 제작하기 (0) | 2025.05.16 |