Game Develop

[UE5] USTRUCT를 Value로 하는 TMap을 사용할 때 주의할 점 본문

UnrealEngine5/이것저것

[UE5] USTRUCT를 Value로 하는 TMap을 사용할 때 주의할 점

MaxLevel 2024. 12. 2. 15:28

 

이슈저장용글에 가깝다.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
USTRUCT(Atomic) 
struct FSkillList
{
    GENERATED_USTRUCT_BODY() 
 
public:
    UPROPERTY(Transient)
    TMap<FName, TObjectPtr<USkill>> skillList;
};
 
 
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class HYOBINSPROJECT_API USkillComponent : public UActorComponent
{
    GENERATED_BODY()
 
public:
    USkillComponent();
 
    const TMap<FName, FSkillList>* GetSkillList() const { return &m_SkillList; }
    
protected:
    UPROPERTY(Transient, VisibleDefaultsOnly)
    TMap<FName, FSkillList> m_SkillList;
};
cs

 

 

위와 같은 코드가 있고, FSkillList의 skillList변수에 Value값을 보면 USkill의 포인터를 넣게 되어있는데, 스킬컴포넌트 생성자에서 USkill을 상속받은 자식클래스들을 NewObject로 생성 후, 넣는다.

 

 

1
2
3
4
// DefaultOnGround
    UNormalAttack_OnGround* normalAttack_OnGround = Utility::NewBlueprintObjectInConstructor<UNormalAttack_OnGround>
    ("Blueprint'/Game/MainPlayerAsset/SkillBlueprint/NormalAttack_OnGround.NormalAttack_OnGround'",GetTransientPackage());
    m_SkillList["DefaultOnGround"].skillList.Add("NormalAttack_OnGround", normalAttack_OnGround);
cs

 

이렇게.

 

 

 

이런 코드들이 있을 때,

m_SkillList에도 UPROPERTY가 있어야 하고 해당 TMap의 Value에 해당하는 FSkillList 구조체의 변수 skillList에도 UPROPERTY가 있어야 한다.

 

둘 중 하나라도 없으면 정상적으로 되지 않는다.

 

만약 클래스의 멤버변수 m_SkillList에 UPROPERTY가 없다면, 게임을 실행한 후에는 곧바로는 잘 되지만 이후 GC가 작동되는 타이밍에 수집해가서 USkill의 함수를 수행하면 크래쉬가 발생한다. (GC가 수집한곳을 참조했으니)

 

 

 

만약 USTRUCT인 FSkillList의 멤버변수인 skillList의 UPROPERTY가 없다면, 게임플레이를 누르자마자 

Pure virtual function being called while application was running (GIsRunning == 1)

에러가 뜬다.

 

내가 호출한 함수는 순수가상함수가 아니라 그냥 가상함수기는 한데.. 어쨌든 이것저것 확인해 본 결과, 일단 결과는 다음과 같다. 구조체에는 USkill의 자식클래스를 넣어놨는데 가리키는 포인터는 USkill이다.

이 때, 메모리상에서 자식클래스에 해당하는 영역만 해제가되어있고, 부모에 해당하는 USkill부분은 살아있다.

실제로 실험결과 가상함수가 아니라 USkill만의 독립함수를 수행하는것은 문제없이 실행되었다.

 

완전 정확히는 잘 모르겠지만, ChatGPT를 열심히 독촉하니까 큰 틀에서는 아래와같이 이해했다.

게임 시작시 리플렉션 및 직렬화 과정을 먼저 하는데, 이 과정에서 언리얼구조체를 직렬화할 때 UPROPERTY가 안붙어있는 구조체필드들은 역직렬화 과정에서 제외가 되고, GC는 이 필드(UObject를 상속받은)를 참조되지 않는 객체로 간주해 수집한다..

 

큰 틀이라고 쓴건... 완전 디테일하게 머리로 이해하진 않아서...

확실한건 에디터에서 게임플레이 시, 레벨과 액터들을 스폰하는 과정에서 USkill의 자식부분 메모리를 수거해가는 것 같다.

 

구조체의 UPROPERTY가 없는 상태에서 해당 자식클래스를 AddToRoot로 강제로 에디터의 루트셋에 추가시키면 저 Pure어쩌고 에러가 안뜨고 그냥 잘 된다.