언리얼에서 '콘텐츠 브라우저 → 유저 인터페이스 → 위젯 블루프린트' 의 방법으로 UI 위젯을 생성할 수 있다.
위젯 블루프린트를 키면 위와 같은 창이 나온다.
'팔레트 → 일반' 에서 자주 사용하는 button, image, progress bar 등의 기능을 드래그하여 넣을 수 있다.
이 UI를 cpp코드로 조작하기 위해서는 user widget class를 연결시켜주어야한다.
user widget class를 생성하고, 위젯 플루프린트의 '그래프 → 클래스 세팅 → 부모 클래스'를 생성한 user widget class로 변경하면 class를 연결할 수 있다.
// MyCharacter.h
UCLASS()
class UNREALINTRODUCTION_API AMyCharacter : public ACharacter
{
/.../
// 위젯 컴포넌트 추가
UPROPERTY(VisibleAnywhere)
class UWidgetComponent* HpBar;
};
Character 헤더에 컴포넌트를 추가하고
// MyCharacter.cpp
#include "Components/WidgetComponent.h" // widget component 경로
#include "MyCharacterWidget.h" // widget class 경로
AMyCharacter::AMyCharacter()
{
/.../
// 컴포넌트를 가져와서 Mesh위에 attach
HpBar = CreateDefaultSubobject<UWidgetComponent>(TEXT("HPBAR"));
HpBar->SetupAttachment(GetMesh());
HpBar->SetRelativeLocation(FVector(0.f, 0.f, 200.f));
// world / screen에서 화면에 나오는 screen으로 사용
HpBar->SetWidgetSpace(EWidgetSpace::Screen);
// finder로 위젯 경로에서 가져옴 (블루프린트의 경우 경로에 _C)
static ConstructorHelpers::FClassFinder<UUserWidget>UW(TEXT("WidgetBlueprint'/Game/UI/WBP_HpBar.WBP_HpBar'_C"));
if (UW.Succeeded()) {
HpBar->SetWidgetClass(UW.Class);
HpBar->SetDrawSize(FVector2D(200.f, 50.f));
}
}
void AMyCharacter::PostInitializeComponents()
{
/.../
HpBar->InitWidget(); // widget 초기화
}
Character cpp에서 HpBar 컴포넌트를 가져와 설정한다.
InitWidget의 경우 버전에 따라 필요 유무가 차이가 있다고 하는데 postinitialize components에서의 역할에 대해 더 알아볼 필요가 있을 것 같다.
언리얼 프로젝트를 생성한 이름으로 만들어진 .Build.cs 파일이 있을 것이다.
// projectName.Build.cs
public class UnrealIntroduction : ModuleRules
{
public UnrealIntroduction(ReadOnlyTargetRules Target) : base(Target)
{
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });
/.../
}
}
위와 같이 public dependency module names에 "UMG"를 추가하는 작업이 필요하다.
캐릭터의 Stat에 MaxHp를 추가하는 작업을 해보자.
// MyStatComponent.h
// 델리게이트 선언
DECLARE_MULTICAST_DELEGATE(FOnHpChanged);
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class UNREALINTRODUCTION_API UMyStatComponent : public UActorComponent
{
GENERATED_BODY()
/.../
publlic:
/.../
int32 GetMaxHp() { return MaxHp; }
float GetHpRatio() { return Hp / (float) MaxHp; }
private:
/.../
UPROPERTY(EditAnywhere, Category = Stat, Meta = (AllowPrivateAccess = true))
int32 MaxHp;
/.../
public:
// 델리게이트 함수 선언
FOnHpChanged OnHpChanged;
};
StatComponent 헤더에서 MaxHp 프로퍼티와 함수를 선언해준다. 델리게이트는 자료형이름 앞에 F를 붙여주어야하는 것이 규칙이다.
// MyStatComponent.cpp
void UMyStatComponent::SetLevel(int32 NewLevel)
{
auto MyGameInstance = Cast<UMyGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
if(MyGameInstance)
{
auto StatData = MyGameInstance->GetStatData(NewLevel);
if (StatData)
{
/.../
// 레벨 세팅 시 MaxHp관련 세팅 추가
SetHp(StatData->MaxHp);
MaxHp = StatData->MaxHp;
/.../
}
}
}
void UMyStatComponent::SetHp(int32 NewHp)
{
Hp = NewHp;
if (Hp < 0)
Hp = 0;
// 델리게이트가 바인딩된 모든 오브젝트에 뿌림
OnHpChanged.Broadcast();
}
델리게이트 멀티캐스트에서 broadcast를 실행할 경우 바인딩 된 모든 함수가 실행된다.
// MyCharacterWidget.h
UCLASS()
class UNREALINTRODUCTION_API UMyCharacterWidget : public UUserWidget
{
GENERATED_BODY()
public:
void BindHp(class UMyStatComponent* StatComp);
void UpdateHp();
private:
// 일반 포인터를 사용하지 않고 Weak 포인터를 사용
TWeakObjectPtr<class UMyStatComponent> CurrentStatComp;
// meta를 이용하면 위젯의 프로퍼티와 자동으로 연결해줌
UPROPERTY(meta = (BindWidget))
class UProgressBar* PB_HpBar;
};
위 코드에서는 스마트 포인터를 사용하였는데, 아직 공유포인터 등에 대해서 잘 알지 못하므로 다음에 자세히 알아보도록 하자.
// MyCharacterWidget.cpp
void UMyCharacterWidget::BindHp(class UMyStatComponent* StatComp)
{
// 포인터에 컴포넌트를 할당
CurrentStatComp = StatComp;
StatComp->OnHpChanged.AddUObject(this, &UMyCharacterWidget::UpdateHp);
}
void UMyCharacterWidget::UpdateHp()
{
// 포인터를 사용하는 경우 다음과 같이 사용
if (CurrentStatComp.IsValid())
PB_HpBar->SetPercent(CurrentStatComp->GetHpRatio());
}
// MyCharacter.cpp
void AMyCharacter::PostInitializeComponents()
{
/.../
HpBar->InitWidget();
// hp widget을 가져와서 bind
auto HpWidget = Cast<UMyCharacterWidget>(HpBar->GetUserWidgetObject());
if (HpWidget)
HpWidget->BindHp(Stat);
}
위 코드에서는 컴포넌트를 초기화할 때 hp bar bind 작업을 진행한다.
widget 컴포넌트에 Hp가 바뀔 때 호출되어야할 OnHpChanged 함수가 delegate로 선언되어있고,
캐릭터가 Attack을 받아 OnAttacked 함수를 통해 SetHp를 실행하면, OnHpChanged.Broadcast가 실행되므로 bind되어있는 함수 UpdateHp를 통해 Hp가 update된다.
'개발 · 컴퓨터공학' 카테고리의 다른 글
Learning Unreal 4 언리얼 공부일지 - AIController class와 Behaivor Tree (0) | 2022.02.06 |
---|---|
Learning Unreal 4 언리얼 공부일지 - class 삭제 or 이름 바꾸기 (0) | 2022.02.05 |
Learning Unreal 4 언리얼 공부일지 - GameInstance로 데이터 만들어 사용해보기 (0) | 2022.01.30 |
Learning Unreal 4 언리얼 공부일지 - 충돌을 이용한 아이템 습득 (0) | 2022.01.29 |
Learning Unreal 4 언리얼 공부일지 - 언리얼의 소켓 (0) | 2022.01.28 |