서론
지금도 캐릭터의 체력을 나타내는 위젯과 퀘스트를 표시하는 위젯이 존재 하지만 게임 진행과 동시에
캐릭터와 퀘스트에 대한 정보를 확인할 수 있도록 새로운 HUD위젯을 추가하려고 합니다.
해당 위젯은 특정 키를 눌러서 나타나게 하는것이 아닌 캐릭터의 생성과 동시에 뷰포트에 추가하여
항상 캐릭터의 상태를 나타내게 하도록 하겠습니다.
체력 상태를 나타내는 HUD위젯
최대 체력과 현재 체력을 받아 체력 퍼센트를 나타내는 위젯은 이미 만들었기 때문에 전에 사용했던 HpBar 위젯을
추가해 줍니다. 그리고 숫자를 통해 체력을 나타낼 수 있게 텍스트 블럭들을 추가해 주었습니다.
void ARPGCharacter::SetupWidget(URPGUserWidget* InUserWidget)
{
...
// HUD 위젯
URPGHUDWidget* HUDWidget = Cast<URPGHUDWidget>(InUserWidget);
if (HUDWidget)
{
HUDWidget->UpdateHpBar(StatComp->GetCurrentHp(), StatComp->GetMaxHp());
StatComp->OnHpChanged.AddUObject(HUDWidget, &URPGHUDWidget::UpdateHpBar);
}
}
HUD위젯은 캐릭터 내부에 생성하였습니다. SetupWidget을 통해 HUD위젯이 스탯 컴포넌트의 OnHpChanged 델리게이트를 구독하게 하여서 체력이 변할경우 변화를 인식해서 HUD 내부의 체력 위젯의 값도 변화할 수 있도록 설정하였습니다.
// Fill out your copyright notice in the Description page of Project Settings.
#include "RPGHUDWidget.h"
#include "RPGHpBarWidget.h"
#include "RPGWidgetInterface.h"
#include "Components/TextBlock.h"
void URPGHUDWidget::NativeConstruct()
{
Super::NativeConstruct();
HpBar = Cast<URPGHpBarWidget>(GetWidgetFromName(TEXT("WidgetHpBar")));
ensure(HpBar);
IRPGWidgetInterface* WidgetInterface = Cast<IRPGWidgetInterface>(GetOwningPlayerPawn());
if (WidgetInterface)
{
WidgetInterface->SetupWidget(this);
}
}
void URPGHUDWidget::UpdateHpBar(float NewCurrentHp, float CurrentMaxHp)
{
HpBar->UpdateHpBar(NewCurrentHp, CurrentMaxHp);
UTextBlock* CurrentHpText = Cast<UTextBlock>(GetWidgetFromName(TEXT("CurrentHpText")));
ensure(CurrentHpText);
int32 CurrentMaxHpInt = FMath::FloorToInt(NewCurrentHp);
FString CurrentHp = FString::Printf(TEXT("%d"), CurrentMaxHpInt);
CurrentHpText->SetText(FText::FromString(CurrentHp));
UTextBlock* MaxHpText = Cast<UTextBlock>(GetWidgetFromName(TEXT("MaxHpText")));
ensure(MaxHpText);
int32 MaxHpInt = FMath::FloorToInt(CurrentMaxHp);
FString MaxHp = FString::Printf(TEXT("%d"), MaxHpInt);
MaxHpText->SetText(FText::FromString(MaxHp));
}
HUD위젯 클래스 내부의 UpdateHpBar 함수입니다.
델리게이트가 브로드 캐스팅 되어 해당 함수가 호출되면 HpBar 위젯의 UpdateHpBar 함수를 호출하여
체력에 비례해서 퍼센트를 수정합니다.
또한 매개변수로 받은 CurrentHp와 MaxHp를 이용해 현재 체력과 최대 체력을 이용하여
텍스트 블럭의 값을 바꿔줍니다.
구현결과
전투에 의해서 체력이 감소하는 모습과 처치 후 체력이 증가하는 과정까지 모두 적용된 모습입니다.
수락한 퀘스트 표시
수락한 퀘스트를 HUD에 표시하고 해당 퀘스트의 진행사항을 나타내도록 구현하였습니다.
위젯은 다음과 같이 구성하였고
다음과 같이 텍스트로 구성된 QuestCountingWidget을 만들어서 QuestVerticalBox의 자식으로
들어가도록 하여 퀘스트 정보가 나타나도록 하였습니다.
void URPGHUDWidget::UpdateQuestList()
{
UVerticalBox* QuestVerticalBox = Cast<UVerticalBox>(GetWidgetFromName(TEXT("QuestVerticalBox")));
ensure(QuestVerticalBox);
// 기존 위젯 제거
QuestVerticalBox->ClearChildren();
TArray<FRPGQuestData> QuestTable = RPGGameInstance->QuestTable;
for (FRPGQuestData Quest : QuestTable)
{
// 퀘스트가 완료 되었거나 수락하지 않았다면 건너뜀
if (Quest.Completed || !Quest.Accepted)
{
continue;
}
QuestCountingWidget = CreateWidget<URPGQuestCountingWidget>(GetWorld(), QuestCountingWidgetClass);
if(QuestCountingWidget)
{
QuestCountingWidget->CreateCountingWidget(Quest.QuestName, Quest.QuestID);
QuestVerticalBox->AddChild(QuestCountingWidget);
}
}
UpdateQuestCount();
}
퀘스트 리스트의 갱신을 위한 함수입니다.
HUD위젯의 VerticalBox를 불러오고 게임 인스턴스에서 퀘스트 테이블을 불러옵니다.
이후 반복문을 통해서 퀘스트가 완료되었는지, 퀘스트가 아직 수락되지 않았는지를 검사하여 예외처리를 해 줍니다.
해당 과정을 거쳐 아직 완료되지 않은 퀘스트 중 수락된 퀘스트를 찾아냈다면 퀘스트 카운팅 위젯을 생성하고
해당 퀘스트의 이름과 ID를 CreateCountingWidget 함수를 통해서 넘기고 VerticalBox의 자식으로 추가합니다.
void URPGQuestCountingWidget::CreateCountingWidget(FString Name, int32 ID)
{
QuestNameText = Cast<UTextBlock>(GetWidgetFromName(TEXT("QuestNameText")));
ensure(QuestNameText);
QuestNameText->SetText(FText::FromString(Name));
QuestID = ID;
}
CreateCountingWidget 함수는 다음과 같이 구성되어 있습니다. 건네받은 QuestName을 이용해 퀘스트 이름을 나타내는
텍스트 블록의 값을 수정하고 해당 위젯의 QuestID를 저장합니다.
이후 캐릭터의 블루프린트에서 퀘스트 메인 위젯의 종료를 처리하는 노드 사이에 Update Quest List를 추가해 줍니다.
이유는 퀘스트 메인 화면에서 퀘스트를 수락하거나 완료하여 구조체의 값에 변화가 생겼기 때문에 해당 변화를 적용하기
위해서 Update Quest List 함수를 호출해 다시 퀘스트 리스트를 추가합니다.
QuestVerticalBox->ClearChildren();
여기서 다음과 같은 VerticalBox의 자식을 모두 제거하는 코드를 넣지 않는다면 퀘스트 목록이 갱신되는것이 아닌
기존에 목록에 새로운 퀘스트 목록을 다시 추가하게 되니 반드시 기존 퀘스트의 제거가 필요합니다.
구현 결과
다음과 같이 성공적으로 수락한 퀘스트의 목록이 나타나는 것을 확인할 수 있습니다!
퀘스트의 진행사항 표시
이번엔 퀘스트의 진행사항을 나타내도록 구현해 보겠습니다.
퀘스트 카운팅 위젯 내부의 텍스트 블록을 바꾸는 것이 목표이므로 우선 퀘스트 카운팅 위젯에
텍스트 블록을 바꾸기 위한 함수를 추가합니다.
void URPGQuestCountingWidget::UpdateQuestCount()
{
KilledCntText = Cast<UTextBlock>(GetWidgetFromName(TEXT("KilledCntText")));
ensure(KilledCntText);
RequireCntText = Cast<UTextBlock>(GetWidgetFromName(TEXT("RequireCntText")));
ensure(RequireCntText);
TArray<FRPGQuestData> QuestTable = RPGGameInstance->QuestTable;
for (FRPGQuestData Quest : QuestTable)
{
if (Quest.QuestID == QuestID)
{
// 잡은 몬스터 수
FString KilledCnt = FString::Printf(TEXT("%d"), Quest.KilledCnt);
KilledCntText->SetText(FText::FromString(KilledCnt));
// 잡아야할 몬스터 수
FString RequireCnt = FString::Printf(TEXT("%d"), Quest.RequireCnt);
RequireCntText->SetText(FText::FromString(RequireCnt));
continue;
}
}
}
자신의 QuestID와 동일한 퀘스트를 구조체 배열에서 찾아서 텍스트 블록의 값을 바꾸는 함수입니다.
void URPGHUDWidget::UpdateQuestList()
{
...
UpdateQuestCount();
}
HUD위젯에서 카운팅 위젯을 생성할 때 마지막 과정에서 해당 함수를 호출하기 때문에 생성과 동시에 텍스트 블록에 대한 값이 수정될 것 입니다.
구현 결과
표시되는 퀘스트 정보와 동일한 카운트 값이 나타나는 모습입니다.
몬스터 처치 시 표시되는 진행사항 갱신
위에서 구현한 결과로 퀘스트 메인창을 닫을 때 위젯이 새로 생성되며 변경된 퀘스트 카운팅을 나타낼 것 입니다.
하지만 구현 목표는 몬스터 처치시 실시간으로 퀘스트 진행사항이 갱신되는 것 이므로 해당 기능을 구현해 보도록 하겠습니다.
void URPGHUDWidget::UpdateQuestCount()
{
UVerticalBox* QuestVerticalBox = Cast<UVerticalBox>(GetWidgetFromName(TEXT("QuestVerticalBox")));
if (QuestVerticalBox)
{
int32 NumChildren = QuestVerticalBox->GetChildrenCount();
for (int32 Index = 0; Index < NumChildren; Index++)
{
URPGQuestCountingWidget* ChildWidget = Cast<URPGQuestCountingWidget>(QuestVerticalBox->GetChildAt(Index));
if (ChildWidget)
{
ChildWidget->UpdateQuestCount();
}
}
}
}
해당 함수는 HUD 위젯 내부의 VerticalBox의 자식을 가져오는 기능을 하고 있습니다.
VerticalBox의 자식의 개수를 int형으로 저장하여 해당 변수와 반복문을 이용해 VerticalBox의 자식에 순서대로 접근합니다.
VerticalBox의 자식을 URPGQuestCountingWidget로 형변환 하여 저장하였기 때문에 UpdateQuestCount에 접근할 수 있습니다. 이후 해당 자식 위젯이 들고있는 QuestID를 이용해 자신의 퀘스트 진행사항을 갱신할 것 입니다.
화면에 표시하는 퀘스트의 진행사항을 갱신하는 함수를 만들었으니 이제 남은건 이 함수를 호출할 위치를 정하는 것 입니다. 몬스터의 SetDead에서 호출하려 했지만 캐릭터의 Reward 함수에서 호출하는 것 역시 몬스터의 처치 시 리워드 인터페이스를 통해 호출되는 함수라 의도한 상황에 맞다고 생각했습니다.
void ARPGCharacter::StatPointReward(int RewardPoint)
{
URPGStatWidget* StatWidget = Cast<URPGStatWidget>(StatWidgetComp->GetWidget());
StatWidget->IncreaseStatPoint(RewardPoint);
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), RewardParticle, GetActorLocation());
RPGHUDWidget->UpdateQuestCount();
}
스탯 포인트 리워드 함수 마지막에 UpdateQuestCount 함수를 호출하여 몬스터 처치 시 퀘스트 진행사항을 갱신하도록
구현하였습니다.
구현 결과
몬스터를 처치 시 퀘스트 카운트가 상승하는 모습입니다.
또한 퀘스트 클리어 시 해당 퀘스트의 Completed 변수가 true로 바뀌어 더이상 위젯이 생성되지 않아
퀘스트 목록에서 사라진 모습입니다.
구현은 여기서 마치고 다음 포스팅에서는 플레이할 맵을 꾸미고 배경음악과 게임 시작 화면을 추가해
게임의 완성도를 올려보겠습니다.
감사합니다!
'[게임 개발] 개발 일지 > RPG' 카테고리의 다른 글
24. 개발 마무리 (0) | 2023.10.07 |
---|---|
22. 몬스터 사망모션 캔슬 버그 수정 및 컨텐츠 추가 (0) | 2023.10.06 |
21. 퀘스트 시스템 - 카운팅과 보상 (0) | 2023.10.06 |
20. 퀘스트 시스템 - 퀘스트 표시와 수락 (0) | 2023.10.06 |
19. 퀘스트 시스템 - 데이터 테이블 구축 (0) | 2023.10.05 |