Make Unreal REAL.
article thumbnail
이득우의 언리얼 C++ 게임 개발의 정석

 

UI를 다루는 방법도 애니메이션을 다룰 때 C++로 Anim Instance를 제작해 로직을 담당하고 그것을 상속 받은 애니메이션 블루프린트에서 설계 작업을 했던 것과 비슷하다.

 

C++로 User Widget을 제작해 로직을 담당하고 위젯 블루프린트에서 그것을 상속 받아 꾸미거나 추가적인 작업을 할 수 있다.

 

UI_HPBar 위젯 블루프린트를 먼저 만들어 놓았으므로 User Widget C++ 클래스를 제작해 부모로 지정하는 방식으로 진행한다.

 

UUserWidget을 상속 받은 ABCharacterWidget 클래스를 만든다.

 

 

공격력, HP 등 레벨에 해당하는 캐릭터의 스탯을 관리하는 ABCharacterStatComponent에서 델리게이트를 하나 선언한다.

  • HP가 바뀔 때 체력바 위젯을 갱신하는 용도로 사용된다.
  • 의존성을 갖지 않게 하기 위해 델리게이트를 사용한다.

 

 

HP를 설정하는 함수와 현재 HP 비율을 가져오는 함수를 추가적으로 선언한다.

  • SetHP() 함수에서 현재 HP를 갱신하고 OnHPChanged 델리게이트를 발동해 위젯도 갱신한다.

 

 

현재 HP인 CurrentHP를 바로 설정하던 부분을 SetHP() 함수를 사용하도록 바꾼다.

 

 

SetHP() 함수에서 현재 HP를 설정하고 OnHPChanged 델리게이트를 통해 등록된 함수들을 실행한다.

 

HP가 0이 되면 OnHPIsZero 델리게이트를 발동해 HP가 0이 되었다고 알린다.

  • 부동 소숫점은 오차 범위를 갖는다.
  • 실수 값을 비교할 때는 0보다는 무시 가능한 오차를 측정하기 위해 언리얼 엔진이 제공하는 KINDA_SMALL_NUMBER 매크로를 사용하는 것이 좋다.

 

 

GetHPRatio() 함수에서 최대 HP로 나눠야 하는데 0이면 오류가 발생하므로 검사해준다.

 

 

UUserWidget을 상속 받고 위젯 블루프린트의 부모로 지정할 ABCharacterWidget 클래스이다.

 

새로 생성했으므로 CoreMinimal.h를 ArenaBattle.h로 변경해준다.

 

 

ABCharacterStatComponent의 OnHPChanged 델리게이트에 바인딩하는 함수를 선언한다.

 

ABCharacterStatComponent 컴포넌트의 형식을 언리얼 엔진에서 제공하는 스마트 포인터 중 하나인 TWeakObjectPtr로 지정했다.

  • 참조로부터 자유로운 언리얼 오브젝트의 약 포인터 형식이다.
  • 현재 ABCharacterWidget은 캐릭터가 죽으면 ABCharacterStatComponent와 함께 사라지기 때문에 쓰지 않아도 되지만, 여기서는 연습용으로 사용한 것이다.
  • 만약 위젯이 캐릭터가 아닌 별도의 액터로 존재한다면 TWeakObjectPtr(약 포인터)를 사용하는 것이 바람직하다.

 

다음과 같이 전방 선언에 *이 붙은 형식이 아님에 유의해야 한다.

TWeakObjectPtr<class UABCharacterStatComponent*> CurrentCharacterStat;

 

 

캐릭터 스탯 컴포넌트를 받아 변수에 설정하고 OnHPChanged 델리게이트에 현재 HP 비율을 테스트로 찍어보는 함수를 등록한다.

  • CurrentCharacterStat은 약 포인터이기 때문에 .IsValid() 함수로 사용하기 전에 유효성을 검사해야 한다.
  • 델리게이트에 등록한 함수는 나중에 위젯의 Progress Bar를 갱신하는 함수로 교체된다.

 

 

체력바 위젯 블루프린트로 이동해 UUserWidget을 상속 받은 ABCharacterWidget을 부모 클래스로 지정해주도록 한다.

 

Desinger 에서 Graph 편집 창으로 이동한 후 Class Settings 패널을 연다.

 

 

Parent Class를 User Widget에서 ABCharacter Widget으로 변경해주고 컴파일 하면 부모 클래스가 성공적으로 바뀐다.

 

 

캐릭터 클래스에서 UWidgetComponent 타입인 HPBarWidget에서 ABCharacterWidget 클래스의 BindCharacterStat() 함수를 호출하기 위해 헤더를 포함한다.

 

 

BeginPlay() 함수에서 ABCharacterWidget 클래스의 BindCharacterStat() 함수에 ABCharacterStatComponent를 전달해 스탯 컴포넌트의 OnHPChanged 델리게이트에 함수를 바인딩하도록 한다.

  • 언리얼 엔진 4.21 버전부터는 위젯의 초기화 시점이 PostInitializeComponents() 함수에서 BeginPlay() 함수로 변경되었다.

 

Widget Component와 User Widget 클래스는 다르다.

  • Widget Component는 액터에 붙이는 컴포넌트이고 위젯 블루프린트를 위젯으로 지정한다.
  • User Widget은 뷰포트에 띄우는 위젯이며, Anim Instance처럼 위젯 블루프린트의 부모가 될 클래스이다.

 

Widget Component에서 User Widget에 접근하기 위해서는 아래와 같이 해야 한다.

UUserWidget* Widget = Cast<UUserWidget>(UWidgetComponent->GetUserWidgetObject());

 

 

이제 컴파일 후 플레이해 캐릭터를 때려 본다.

 

ABCharacterWidget에서 ABCharacterStatComponent 컴포넌트의 OnHPChanged 델리게이트에 등록한 HP 비율 출력 람다 함수가 실행되어 로그 창에 출력된다.

 

 

스탯 컴포넌트의 OnHPChanged 델리게이트가 실행되며 위젯에 HP 비율이 전달되는 것을 확인했다.

이제 HP 비율 출력 부분을 Progress Bar 체력바를 갱신하도록 바꾸면 된다.

 

NativeConstruct() 함수는 UI를 생성하거나 Viewport에 등록할 때마다 호출된다.

  • UI 생성이 완료된 후 위젯을 초기화하는 시점이다.

 

Prograss Bar 체력바와 체력바를 갱신하는 함수를 만든다.

 

 

Prograss Bar를 사용하기 위해 헤더를 포함한다.

 

 

약 포인터인 CurrentCharacterStat 스탯 컴포넌트가 유효한지 확인한 후 체력바를 갱신한다.

  • UProgressBar->SetPercent() 함수를 이용해 바를 얼마나 채울지 설정할 수 있다.

 

 

NativeConstruct() 함수에서 체력바를 받아오고 갱신한다.

 

 

스탯 컴포넌트에서 HP가 변경될 때 로그를 출력하던 부분을 체력바를 갱신하도록 변경한다.

 

 

실제 체력과 체력바 UI를 연동하는 데 성공했다.

 

 

UI의 초깃값 설정이나 초기화를 할 때 고려해야 하는 것이 있다.

 

뷰포트 위젯 생성은 일반적으로 플레이어 컨트롤러의 BeginPlay() 함수에 작성하고, 액터 컴포넌트의 위젯 생성 시점도 BeginPlay() 함수이다.

 

UWidgetComponent->SetWidgetClass(TSubclassOf<UUserWidget>) 함수를 실행한다고 해서 바로 생성되는 것이 아니다.

  • Play 후에 실행됐다면 위젯까지 생성하지만, Play 전이면 말 그대로 위젯 클래스만 설정한다.
  • 아래는 캐릭터 클래스의 생성자에서 위젯 블루프린트를 설정하고 NULL 체크를 하는 모습이다.
  • 아직 Play 전으로 위젯이 생성되지 않았으므로 에러 로그를 출력한다.

 

 

따라서, UI 위젯의 초기화 코드는 폰이나 플레이어 컨트롤러의 AActor::PostInitializeComponents() 함수보다는 위젯의 NativeConstruct() 함수에서 하는 것이 좋다.

  • UI 생성이 완료되면 초기화 시점에 NativeConstruct() 함수가 호출된다.
  • BeginPlay() 함수 이전에 실행한 명령은 반영되지 않을 수 있기 때문이다.
profile

Make Unreal REAL.

@diesuki4

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그