언리얼C++ 기초
레벨 전환 시 나타나야 할 트랜지션 위젯의 텍스트(Level, Wave)의 진행상황이 업데이트가되지않았다. 또 게임을 진행하면서 웨이브나 레벨을 넘어가다가 가끔씩 트랜지션 위젯이 여러개가 중첩돼서 나타나는 걸 확인했다.
// TransitionWidget.h
void ShowTransitionWidget();
// PlayerController.h
TSubclassOf<UTransitionWidget> TransitionWidgetClass;
UTransitionWidget TransitionWidgetInstance;
void ShowTransition();
문제는 여러가지가 있었는데
PlayerController에서 Create할때 그냥 ShowTransition()을 쓴 곳마다 만들도록 되어있었다. (여러개 생성 오류)NativeConstruct에서 변수 초기화이렇게가 문제였다. 그래서 어떻게 고쳤나면
void ASprataPlayerController::ShowTransition(){
if (TransitionWidgetClass)
{
if (!TransitionWidgetInstance)
{
TransitionWidgetInstance = CreateWidget<UTransitionWidget>(this, TransitionWidgetClass);
}
if (TransitionWidgetInstance) {
TransitionWidgetInstance->ShowTransitionWidget();
}
}
}
첫번째 문제는 한번 만들고 !TranstionWidgetInstance로 없다면 만들도록 체크했다. 그리고 또 애초에 CreateWidget과 트랜지션위젯 클래스, 인스턴스의 자료형을 UUserWidget*으로 만들었었는데 다 UTransitionWidget으로 바꿨다. 이 편이 좀더 짧게 쓸 수 있다. 즉, Cast나 다른 예외처리를 할때 짧게 쓸 수 있게 됐다.
// 예외처리 생략
if (SpartaGameInstance)
{
CurrentLevelIndex = SpartaGameInstance->CurrentLevelIndex;
CurrentWaveLevelIndex = SpartaGameInstance->CurrentWaveLevelIndex;
UE_LOG(LogTemp, Warning, TEXT("[%p] Level %d, Wave %d GameInstance"), this, CurrentLevelIndex+1,CurrentWaveLevelIndex+1);
}
// 예외처리 생략
if (UTextBlock* LevelText = Cast<UTextBlock>(TWI->GetWidgetFromName(TEXT("TransitionLevel"))))
{
LevelText->SetText(FText::FromString(FString::Printf(TEXT("Level %d"), CurrentLevelIndex + 1)));
}
if (UTextBlock* WaveText = Cast<UTextBlock>(TWI->GetWidgetFromName(TEXT("TransitionWave"))))
{
WaveText->SetText(FText::FromString(FString::Printf(TEXT("Wave %d"), CurrentWaveLevelIndex + 1)));
}
UE_LOG(LogTemp, Warning, TEXT("[%p] Level %d, Wave %d"), this, CurrentLevelIndex+1,CurrentWaveLevelIndex+1);
두번째로, UI Text 바인딩을 할때 제대로 작동하지 않아 UE_LOG로 Level과 Wave, 주소를 확인해봤다. 결과는 주소는 똑같이 나오는데 GameInstance에서 받은 Level과 WaveLevel이 위에서는 증가하지만 아래에서는 변하지않는걸 확인했다.
void NativeConstruct()
{
CurrentLevelIndex = 0;
CurrentWaveLevelIndex = 0;
}
Widget 클래스는 생성자를 사용하지않고 대신 NativeConstruct()를 써서 호출할때마다 불러오는 생성자역할을 대신한다.
TranstionWidgetInstance->AddToViewport(100);를 호출할때 NativeConstruct()를 거쳐 나오기때문에 계속 0으로 초기화 돼서 그런 것이였다.
그래서 헤더파일에서 변수 초기화를 진행해주었음.
단순 변수 초기화는 객체가 생성될 때 딱 한번만 실행되는 변수선언문에서 처리하고, NativeConstruct에서는 초기화를 지양해야한다.
왜 UUserWidget을 상속받은 클래스들은 생성자대신 NativeConstruct()를 사용해야할까?
그 이유는 생성자 = 객체가 메모리에 할당될 때 딱 한번 실행되지만, 이 시점에서 블루프린트에서 만든 버튼, 텍스트 같은 위젯 요소들이 아직 생성되지 않은 상태이기 때문이다.
NativeConstruct() = UI 요소에 데이터를 넣거나 초기 세팅을 하는 작업 정도만 여기에 넣어서 사용한다.