[UE ยท blueprint bottom layer] understand NativeClass, GeneratedClass, BlueprintClass and ParentClass

This article will give an in-depth explanation of several UClass member variables NativeClass, GeneratedClass, BlueprintClass and ParentClass of the blueprint class UBlueprint. After reading it, you will have a new understanding of the blueprint. The content of this article is very dry and difficult to learn. It is recommended to read the explanation of UClass related concepts I wrote earlier:
[UE · bottom layer] understand StaticClass, GetClass and ClassDefaultObject

preparation

First, customize a UserWidget (MyUserWidget), and then create two UMG blueprints. The first parent class is MyUserWidget, named UMG_Parent. The second blueprint name is UMG_Child, ParentClass is selected as UMG_Parent. Then generate the UMG in the level blueprint. The following logic will be written in the nativeconstruction function of MyUserWidget.

ParentClass is a parent class and not a parent class

ParentClass is a parameter that we often need to operate in the blueprint, which can be said to be familiar. So, is it a parent class? Yes. Is it the parent of the blueprint? no

From the perspective of naming and usage, ParentClass is easily misunderstood as the parent class of the blueprint. This is completely wrong. ParentClass is the parent class of the object to be edited. We need to know the essence of the blueprint to understand this sentence. I'll talk more about GenerateClass later. Now we only need to know that the blueprint and the objects we edit through the blueprint are two things. For example, for an Actor, when our game runs, we don't need the blueprint of the Actor, but only the information of the Actor.
Let's test the code. First, load the blueprint UMG with LoadObject_ Child, note that there is no path here_ C. Output the SuperClass information of ParentClass and blueprint class respectively:

UObject* Obj = LoadObject<UObject>(nullptr,TEXT("WidgetBlueprint'/Game/UMG/UMG_Child.UMG_Child'"));
	
if(UBlueprint* BpObj = Cast<UBlueprint>(Obj))
{
	if (auto BpParent = BpObj->ParentClass)
	{
		FString ObjName = BpParent->GetFName().ToString();
		UE_LOG(LogTemp, Warning, TEXT("ParentName %s"), *ObjName);
	}
	
	if (auto BpSuper = BpObj->GetClass()->GetSuperClass())
	{
		FString ObjName = BpSuper->GetFName().ToString();
		UE_LOG(LogTemp, Warning, TEXT("SuperClassName %s"), *ObjName);
	}
}

Output results:

LogTemp: Warning: ParentName UMG_Parent_C
LogTemp: Warning: SuperClassName BaseWidgetBlueprint

ParentClass results in one more class selected in the blueprint editor_ C (the reason will be explained later). The SuperClass result is BaseWidgetBlueprint, which is the parent class of the blueprint. Because we use UMG for testing, the blueprint class corresponding to UMG is UWidgetBlueprint, and its parent class is UBaseWidgetBlueprint, the output result is BaseWidgetBlueprint (UE4 will hide the initials).

Blueprint and GeneratedClass game modifiers and Archives

First, sort out several classes of the Blueprint, WidgetBlueprint, BaseWidgetBlueprint, Blueprint and BlueprintCore.

  • WidgetBlueprint UMG blueprint class
  • BaseWidgetBlueprint UMG blueprint base class
  • Blueprint blueprint class, which is widely used in the engine
  • BlueprintCore blueprint core class

The relationship is as follows, in which the blueprint class will be sent to generate various specific types of blueprint classes, including UMG blueprint and animation blueprint, etc

GeneratedClass (blueprint generation class) is a member variable of BlueprintCore blueprint core class. It is a type of TSubclassOf, which is the templating of UClass. TSubclassOf indicates that this is a UClass of type T and not any other type.

Now back to the previous question:

What is the essence of blueprint? The essence of blueprint is the editor of class.
Which class editor? GeneratedClass.

Blueprint and GeneratedClass are like the relationship between modifier and archive. If we regard the world in which UE4 runs as an ancient game without real-time archive function, we need game Archive (GeneratedClass) to play, and we can only use specific modifier (specific type of blueprint) to modify the file. After the game runs, it doesn't need modifiers at all, just the game archive. Every time we click the Compile button of the blueprint, it is equivalent to that we use the modifier to edit the archive and click save. Only when we click save, our modifications will be effective.

Then there is another small problem:

_ What the hell is the C suffix? No_ C is the blueprint, plus_ C is the GeneratedClass.

Having said so much,

Where is the generated class? Let me Kangkang!

Let's go back to the UE4 resource browser interface. When we save the file, the uasset here actually serializes the blueprint information, and the GeneratedClass in the blueprint will be serialized together. That is to say, although there seems to be only one file, there are actually two things. And by adding_ C suffix, we can only load GenerateClass instead of blueprint.

The next step is code testing, loading UMG_Child_C and UMG_Parent_C and print:

UObject* ChildObj = LoadObject<UObject>(nullptr,TEXT("WidgetBlueprint'/Game/UMG/UMG_Child.UMG_Child_C'"));
UObject* ParentObj = LoadObject<UObject>(nullptr,TEXT("WidgetBlueprint'/Game/UMG/UMG_Parent.UMG_Parent_C'"));
if (ChildObj && ParentObj)
{
	FString ChildObjName = ChildObj->GetFName().ToString();
	UE_LOG(LogTemp, Warning, TEXT("ChildObjName %s"), *ChildObjName);
	FString ParentObjName = ParentObj->GetFName().ToString();
	UE_LOG(LogTemp, Warning, TEXT("ParentObjName %s"), *ParentObjName);
}

Output results:

LogTemp: Warning: ChildObjName UMG_Child_C
LogTemp: Warning: ParentObjName UMG_Parent_C

Now we know_ What are C and GeneratedClass? Let's go back to the content of ParentClass in the previous section. Previously, we said that ParentClass is the parent of the class to be edited in the blueprint, that is, it is the parent of the GeneratedClass of the blueprint. In order to verify this conclusion, continue to write code test and still load blueprint UMG_Child (note no _c):

UObject* Obj = LoadObject<UObject>(nullptr,TEXT("WidgetBlueprint'/Game/UMG/UMG_Child.UMG_Child'"));
if(UBlueprint* BpObj = Cast<UBlueprint>(Obj))
{
	//Parent class of blueprint
	if (auto BpParent = BpObj->ParentClass)
	{
		FString ObjName = BpParent->GetFName().ToString();
		UE_LOG(LogTemp, Warning, TEXT("ParentName %s"), *ObjName);
	}
	//GeneratedClass of blueprint
	if (auto BpGen = BpObj->GeneratedClass)
	{
		FString ObjName = BpGen->GetFName().ToString();
		UE_LOG(LogTemp, Warning, TEXT("GenClassName %s"), *ObjName);
		//The parent class of the GeneratedClass of the blueprint
		FString GenSuperClassName = BpGen->GetSuperClass()->GetFName().ToString();
		UE_LOG(LogTemp, Warning, TEXT("GenSuperClassName %s"), *GenSuperClassName);
	}
}

Output results:

LogTemp: Warning: ParentName UMG_Parent_C
LogTemp: Warning: GenClassName UMG_Child_C
LogTemp: Warning: GenSuperClassName UMG_Parent_C

It is perfectly consistent with the conjecture that ParentClass is the parent class of GeneratedClass.

NativeClass pedigree orthodox

After reading so much of the previous content, some readers have fainted, so here is a relatively simple concept NativeClass. NativeClass is not a variable or a type, but a concept. Each UClass has a function IsNative() to judge whether the UClass is Native. If so, we usually call the UClass NativeClass.

So, when is Native True?

Simply put, UClass generated by C + + class is NativeClass, and UClass generated by blueprint class is not NativeClass (such as the generated class mentioned above).

NativeClass is like a Chinese of pure blood. How to have children in the C + + world is a pure Chinese. However, if you go abroad and have a child with blueprint, you will be a hybrid (it means to create a blueprint, and ParentClass is selected as C + +), and this hybrid will have a child with a foreign country (it means to create a blueprint, and ParentClass is selected as the previous blue chart), you will certainly not be a Chinese of pure descent (it means not NativeClass).

In UObject, there is a method GetParentNativeClass that can help us find out which generation of parents of this hybrid child's grandparents are pure Chinese. Continue writing code test:

//The loading blueprint is still in front, which is omitted
...
if (auto BpGenNative = GetParentNativeClass(BpObj->GeneratedClass))
{
	FString ObjName = BpGenNative->GetFName().ToString();
	UE_LOG(LogTemp, Warning, TEXT("ParentGenNativeName %s"), *ObjName);
}

Output results:

LogTemp: Warning: ParentGenNativeName MyUserWidget

In other words, we can use GetParentNativeClass to find out which C + + class the Blueprint's GeneratedClass derived from in the beginning. So here's another small assignment. For Blueprint, is it a NativeClass? The answer is in the project source code I provide, which is left to you to solve by yourself.

BlueprintClass

The Blueprint class also has a function GetBlueprintClass, which is also mentioned here. This time, we write code test first and then study it:

if (auto BpClass = BpObj->GetBlueprintClass())
{
	FString ObjName = BpClass->GetFName().ToString();
	UE_LOG(LogTemp, Warning, TEXT("BpClassName %s"), *ObjName);
}

Output results:

LogTemp: Warning: BpClassName WidgetBlueprintGeneratedClass

What is WidgetBlueprintGeneratedClass? First, F12 look at the codes of UBlueprint::GetBlueprintClass() and UWidgetBlueprint::GetBlueprintClass():

Take another look at the definitions of UBlueprintGeneratedClass and UWidgetBlueprintGeneratedClass:

Therefore, GetBlueprintClass of blueprint returns the StaticClass of its corresponding specific GeneratedClass. The GeneratedClass (xxx_C) we talked about earlier is a UClass, and it is also a UWidgetBlueprintGeneratedClass. The diagram is as follows:

Continue to write test code:

//Load blueprint, omit
...
if (auto BpGen = BpObj->GeneratedClass)
{
	if (Cast<UWidgetBlueprintGeneratedClass>(BpGen))
	{
		FString ObjName = BpGen->GetFName().ToString();
		UE_LOG(LogTemp, Warning, TEXT("GenClassName %s"), *ObjName);
	}
	//The parent class of the GeneratedClass of the blueprint
	UClass* BpGenSuperClass = BpGen->GetSuperClass();
	if (Cast<UWidgetBlueprintGeneratedClass>(BpGenSuperClass))
	{
		FString GenSuperClassName = BpGenSuperClass->GetFName().ToString();
		UE_LOG(LogTemp, Warning, TEXT("GenSuperClassName %s"), *GenSuperClassName);
	}
	//The parent class of the GeneratedClass of the blueprint
	UClass* BpGenSSClass = BpGen->GetSuperClass()->GetSuperClass();
	if (Cast<UWidgetBlueprintGeneratedClass>(BpGenSSClass) == nullptr)
	{
		FString GenSSClassName = BpGenSSClass->GetFName().ToString();
		UE_LOG(LogTemp, Warning, TEXT("GenSSClassName %s"), *GenSSClassName);
	}
}

Output results:

LogTemp: Warning: GenClassName UMG_Child_C
LogTemp: Warning: GenSuperClassName UMG_Parent_C
LogTemp: Warning: GenSSClassName MyUserWidget

As you can see, only the last MyUserWidget can not be converted to UWidgetBlueprintGeneratedClass.

Runtime objects and GeneratedClass game characters and game archives

The relationship between runtime objects and GeneratedClass is like the relationship between game characters and game archives. It is a similar analogy before. This time, the world in which UE4 runs is regarded as a broken online game that has no archiving function but can be played by multiple people online. A technology leader has changed a cow force file with a modifier (referring to modifying the generated class with a blueprint). Then, each player uses this archive to enter the game and play his own game character (refers to generating multiple runtime objects. The UClass of each runtime object is the same and the same as the generated class). Although it is said that you can play strange games in the game to make money and upgrade to make the character attributes different (referring to modifying class variables at runtime), after exiting the game, everyone's archive will be the same as what you got at the beginning (referring to that the generated class information in the blueprint will not be changed).

In order to distinguish modifiers, archive game character names and reduce the trouble of naming, a set of naming scheme is established by convention:

  • The name of the modifier is defined by the technical leader (referring to the developer naming the blueprint UMG_Child)
  • The game archive name is added after the modifier_ C suffix (UMG_Child_C)
  • The character name in the game is the game archive name plus the number (UMG_Child_C_5)

Their relationship diagram is as follows:

GeneratedClass is both UClass and UObject

At the beginning of the last article, I told you that UClass is the Type of C#. In fact, this sentence is not completely accurate. It only reflects the function of UClass to record class information, but does not reflect the fact that UClass is also an object. So later in the last article, I couldn't understand the result of multiple calls to GetClass.
What are the characteristics of the objects in the game? Of course, there are many features. I only mention one here: for objects generated by the same UClass, calling GetClass on these objects is equal. Whether it's BP in the last article_ Actor is also the UMG of this article_ Child. As long as you use the same blueprint to generate multiple objects in the scene, their calls to GetClass must be equal.

What about UClass? Review the class diagram in the previous article:

UClass is also derived from UObject, so it is common for the above features. That is, for objects generated by the same UClass, calling GetClass on these objects is equal. So, in our case, UMG_Parent_C and UMG_Child_C these two blueprints are generated by the UClass widgetblueprintgenerated class. Therefore, calling GetClass on these two objects must be equal. The WidgetBlueprintGeneratedClass is also generated by UClass. It and other BlueprintGeneratedClass generated by UClass call GetClass, which must be the same.
The test code is as follows:

//Loading UMG blueprints and normal blueprints
UObject* ChildObj = LoadObject<UObject>(nullptr,TEXT("WidgetBlueprint'/Game/UMG/UMG_Child.UMG_Child_C'"));
UObject* ParentObj = LoadObject<UObject>(nullptr,TEXT("WidgetBlueprint'/Game/UMG/UMG_Parent.UMG_Parent_C'"));
UObject* ActorObj = LoadObject<UObject>(nullptr,TEXT("Blueprint'/Game/BP/BP_MyActor.BP_MyActor_C'"));
if (this->GetClass() == ChildObj)
{
	UE_LOG(LogTemp, Warning, TEXT("Runtime UClass And blueprints GeneratedClass identical"),);
}
//inequality
if(ChildObj != ParentObj)
{
	UE_LOG(LogTemp, Warning, TEXT("Child and Parent Different"),);
}
//identical
if(ChildObj->GetClass() == ParentObj->GetClass())
{
	UE_LOG(LogTemp, Warning, TEXT("Child of UClass and Parent of UClass identical"),);
}
//identical
if(ChildObj->GetClass()->GetClass() == ActorObj->GetClass()->GetClass())
{
	UE_LOG(LogTemp, Warning, TEXT("Child of UClass of UClass and Actor of UClass of UClass identical"),);
}

summary

This article is over here. I have written so much unconsciously. There is a lot of content, about twice as much as the last article. In fact, there are many articles about the bottom on the Internet, but it's hard to see. One is very abstract in concept, and the other is not writing code for testing. So in the process of writing this article, I try to use some metaphors to make it easy for you to understand, and take you to write some code for testing. In later articles, I will use the knowledge mentioned in this article to develop a practical blueprint function.

Learning materials

In depth Unreal blueprint development: understand the blueprint technical architecture
UE4 blueprint analysis (I)

About the author

  • Shuiyao day chicken, a game programmer who likes ACG. Once participated in the development of Sony China Star project "hard core mecha". At present, he is working on UE4 project in a large factory.

CSDN blog: https://blog.csdn.net/j756915370
Zhihu column: https://zhuanlan.zhihu.com/c_1241442143220363264
Game peer chat group: 891809847

Tags: C++ Game Development UE4 unreal

Posted on Wed, 01 Dec 2021 00:41:09 -0500 by gladiator83x