3 minute read

‘이득우의 언리얼 c++ 게임 개발의 정석’ 책을 참고하여 작성한 포스트입니다.


테이블 데이터를 언리얼 엔진에 불러온다.
체력을 표시하는 UI 기능을 구현해 본다.


엑셀 데이터의 활용

  • 캐릭터 스텟 데이터는 보통 변하지 않는 데이터이므로 게임 앱이 초기화될 때 불러들인다.
  • 게임 앱을 관리하기 위한 용도로 게임 인스턴스 라는 언리얼 오브젝트가 있다.
  • 게임 인스턴스를 이용해 캐릭터 스탯 데이터를 불러들이고, 관리하도록 해본다.
  • 새롭게 생성한 게임 인스턴스가 게임 앱을 관리하기 위해서는 프로젝트 세팅 -> 맵&모드 에서 게임 인스턴스 항목을 변경해 주어야 한다.
  • 게임 앱이 초기화되면 언리얼 엔진은 GameInstance의 Init 함수를 호출한다.
  • 주요 함수의 호출 순서는 다음과 같다.
    1. 게임 앱의 초기화
      • UGameInstance::Init
    2. 레벨에 속한 액터의 초기화
      • AActor::PostInitializeComponents
    3. 플레이어의 로그인
      • AGameMode::PostLogin
    4. 게임의 시작
      • AGameMode::StartPlay
      • AActor::BeginPlay

엑셀 불러오기

  • 언리얼 엔진은 행과 열로 구성된 테이블을 csv파일 형식으로 불러올 수 있다.
  • 불러들이기 전에 테이블 데이터의 각 열의 이름과 유형이 동일한 구조체를 선언해 주어야 한다.
  • FTableRowBase 구조체를 상속받은 구조체를 게임 인스턴스의 헤더에 선언해 준다.
  • 첫 열은 자동으로 키 값으로 사용되기 때문에 두 번째 열부터 선언해주면 된다.
  • 다음은 열이 Name, Level, MaxHP 인 데이터의 예이다.
    ```cpp #include “Engine/DataTable.h”

USTRUCT(BlueprintType) struct FLSCharacterData : public FTableRowBase { GENERATED_BODY()

public: FLSCharacterData() : Level(1), MaxHP(100) {}

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
int32 Level;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
int32 MaxHP; } ``` - 구조체 선언 후 콘텐츠 브라우저에서 Csv 파일을 설정한 구조체로 임포트해주면 데이터 에셋을 생성할 수 있다. - 이 에셋을 더블 클릭하여 각 필드의 데이터도 변경 가능하다. - c++ 에서는 이 에셋을 UGameData 타입으로 다루면 된다. - row는 FindRow를 이용해 찾는다.

UDataTable

  • Imported spreadsheet table.

UDataTable::GetRowMap

virtual const TMap< FName, uint8* >& GetRowMap()

RowMap

  • Map of name of row to row data structure.
TMap< FName, uint8 * > RowMap

UDataTable::FindRow

  • Function to find the row of a table given its name.
template<class T>
T * FindRow
(
    FName RowName,
    const FString ContextString,
    bool bWarnIfRowMissing
) const
LSCharacterTable->FindRow<FLSCharacterData>(*FString::FromInt(Level), TEXT(""));

액터 컴포넌트의 제작

  • 스탯 관리를 액터 컴포넌트를 이용해 해본다.
  • c++ 클래스에서 ActorComponent를 부모로 생성하면 된다.
  • 액터 컴포넌트를 생성하면 BeginPlay와 TickComponent가 메서드로 제공된다.
  • 액터의 PostInitializeComponents에 대응하는 컴포넌트의 함수는 InitializeComponent 함수이다. 이 함수는 액터의 PostInitializeComponents가 호출 되기 바로 전에 호출된다.
  • InitializeComponent를 사용하려면 생성자에서 bWantsInitializeComponent 값을 true로 설정해 주어야 한다.
  • stat 관련 함수들은 이 액터 컴포넌트에 정의하고 사용해 주자. 게터 세터 등등.

캐릭터 위젯 UI 제작

  • 콘텐츠 브라우저에서 신규 생성으로 유저 인터페이스->위젯 블루프린트를 생성해서 UI를 만든다.
  • 전체 스크린에 부착할 위젯은 Canvas Panel을 이용하면 되고, 아닌 경우 제거하면 된다.
  • 팔레트 상의 Progress Bar 등의 메뉴를 이용해 UI 위젯을 완성시키면 된다.
  • Progress Bar의 경우 Percent 속성을 이용해 원하는 양을 채울 수 있다.

모듈과 빌드 설정

  • UWidgetComponent 클래스는 액터에 UI 위젯을 부착할 수 있게 해준다.
  • 이 컴포넌트를 사용하기 위해서는 빌드 파일에 “UMG” 모듈을 추가해 주어야 한다.
#include "Components/WidgetComponent.h"

HPBarWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("HPBarWidget"));
HPBarWidget->SetupAttachment(GetMesh());
HPBarWidget->SetRelativeLocation(0.0f, 0.0f, 180.0f);
HPBarWidget->SetWidgetSpace(EWidgetSpace::Screen);
static ConstructorHelpers::FClassFinder<UUserWidget> UI_HUD(...);
HPBarWidget->SetWidgetClass(UI_HUD.Class);
HPBarWidget->SetDrawSize(FVector2D(150.0f, 50.0f));

EWidgetSpace

enum EWidgetSpace
{
	// The widget is rendered in the world as mesh,
	// it can be occluded like any other mesh in the world.
    World, 
	// The widget is rendered in the screen,
	// completely outside of the world, never occluded.
    Screen,
}

UWidgetComponent::SetWidgetClass

  • Sets the widget class used to generate the widget for this component
void SetWidgetClass
(
    TSubclassOf< UUserWidget > InWidgetClass
)

UWidgetComponent::SetDrawSize

  • Sets the draw size of the quad in the world
void SetDrawSize
(
    FVector2D Size
)

UI와 데이터의 연동

  • 위젯 블루프린트가 사용하는 기반 c++ 클래스는 UserWidget 이다.
  • 상속받는 클래스를 생성하고 stat component와 연동해 구현하자.
  • UI와 캐릭터가 서로 다른 액터인 경우 캐릭터 스탯 컴포넌트를 약포인터로 연결하는 것이 좋다.
  • 위젯의 초기화 시점은 BeginPlay 이다. 캐릭터 클래스에서 위젯 설정을 할 경우 BeginPlay에서 구현해 준다.
  • 이후 생성한 UI에셋의 부모 클래스를 새로 생성한 위젯 클래스로 설정해준다.
  • UI 시스템이 초기화되면 위젯 클래스의 NativeConstruct 함수가 호출되는데, UI 생성은 플레이어 컨트롤러의 BeginPlay에서 호출되므로 이전에 호출된 명령들은 UI에 반영되지 않는다.
#include "Components/ProgressBar.h"

void ULSCharacterWidget::NativeConstruct()
{
	Super::NativeConstruct();
	HPProgressBar = Cast<UProgressBar>(GetWidgetFromName(TEXT("PB_HPBAR")));
	UpdateHPWidget();
}

void ULSCharacterWidget::UpdateHPWidget()
{
	...
	HPProgressBar->SetPercent(CurrentCharacterStat->GetHPRatio());
}

Leave a comment