Game Develop

[UE5] 언리얼에서의 생성자는 여러번 호출된다. 본문

UnrealEngine5/이것저것

[UE5] 언리얼에서의 생성자는 여러번 호출된다.

MaxLevel 2024. 3. 19. 07:05

필요없는 잡소리를 듣고싶지 않다면

 

https://forums.unrealengine.com/t/when-is-the-constructor-called/480067

 

When is the Constructor called?

Hey guys, first of all, i am sorry for my English. So i need to say that i am in the beginning of GameDev. I know the basics of c++, and so on, and therefore i wanted to look into Unreal Engine. My Problem is something like this: why is it not possible to

forums.unrealengine.com

 

이 글을 읽는걸 추천. 언리얼입문자라면 반드시 보는걸 추천한다.

언리얼엔진이라는 거대한 프레임워크가 어떻게 돌아가는지에 대한 출발점이라고도 볼 수 있기 때문이다.

 

일단 나같은경우, 작업을 하다가 문제가 발생했었는데 특정 액터컴포넌트의 생성자에서 외부csv파일을 불러오는 코드를 수행한다. 해당 코드를 수행하기위해서 서브시스템을 불러와야하기 때문에

 

{
    const auto dataManager = GetWorld()->GetGameInstance()->GetSubsystem<UDataManager>();
    dataManager->InitAttackInformations("DataTable'/Game/DataAsset/AttackInformation_Player.AttackInformation_Player'", m_AttackInformations);
}

 

이런 식으로 코드를 작성했다. 

그런데 이렇게 하고 빌드한다음 에디터를 켰더니, 켜지지도 않았다. GetWorld부분에서 터졌는데, 대충 로그를 찍어보니 GetWorld가 nullptr로 되어있었다. 즉 World가 정의 안되어있었다는 것.

 

그래서 일단 아래와 같이 수정 후, 빌드하고 에디터를 켰다.

if (GetWorld() != nullptr)
{
    const auto dataManager = GetWorld()->GetGameInstance()->GetSubsystem<UDataManager>();
    dataManager->InitAttackInformations("DataTable'/Game/DataAsset/AttackInformation_Player.AttackInformation_Player'", m_AttackInformations);
}

 

일단 다시 에디터를 키려고 작성한 코드라서, 데이터들은 못불러왔다고 생각했다.

GetWorld가 nullptr이라 생각했으니까..

근데 막상 게임시작을 하니까 잘됐다. 즉, 데이터가 잘 불러와졌다는 것이다;;;

 

생각해보니, 언리얼엔진은 생성자를 여러번 호출한다는 사실이 어렴풋이 생각났다.

그래서 대충 아래와 같은 추리를 했다.

 

1. nullptr체크 안하면 에디터킬 때 터지는 이유는, 에디터를 초기화하는 시점에서 생성자를 호출했을 때 World가 정의가 안되어있기 때문에 터지는 것.

 

2. nullptr체크 후 게임 시작하면 정상적으로 데이터를 불러오는 것은, 에디터를 초기화할 때는 생성자를 호출했는데 이때는 World가 정의 안되어있기 때문에 그냥 넘어가고 게임플레이를 시작했을 때 다시 생성자를 호출하는데, 이때는 World가 정의되어 있기때문에 데이터를 불러오는 것.

 

이렇게 생각하고 if문과 else에 각각 로그를 찍어서 테스트해봤는데 생각대로였다.

에디터를 딱 켰을때는 nullptr이라고 로그가 찍혀있었고 게임플레이를 눌렀을때 not nullptr이라는 로그가 찍혀있었다.

 

여기까지 해결하고, 맨 위 링크의 글을 읽으니까 이 현상에 대해서 전반적으로 이해가 됐었다.

CDO는 World가 정의되기 이전에 만들어지니, 당연히 터졌던 것이고 게임플레이를 누르면 또 생성자를 호출해주니 데이터가 정상적으로 불러와졌던 것이다.

 

그래서 GetOwner라던가, GetWorld처럼 게임플레이 이후에 정의되는 코드들에 대한 경우는 BeginPlay쪽에 작성하는게 좋을 수 있다..라고 되어있긴 하다.

근데 위의 코드처럼 내가하려는 것은, 경로를 직접 넣어서 해당 경로의 파일내용을 불러오는 것이기 때문에, BeginPlay 호출 이전에 데이터를 불러들여서 초기화시키려 했던건데...

사실 생성자에 코드를 작성해놨을 뿐, 결국 실제로는 게임이 시작되었을 때 데이터를 불러들이니 그냥 서브시스템으로 안쓰는게 나을법도 하다.

 

어느 글에서 우연히 읽은건데, 경로를 넣어서 특정파일의 데이터를 불러들이는 것 같은 경우는 게임시작시 불러들이는 것보다는 그 전에 불러들이는게 더 낫다는 글을 읽었는데 꽤나 그럴법해서 해당 규칙을 따르려고 한다.

결국 도로묵느낌이 나긴 하다만, 이런 시도덕분에 좀 더 구조를 잘 이해하게 됐으니 이득이라 생각한다.