Reflection is the ability of a program to self-test at run time. It is very useful and basic technology in Unreal engine that supports features such as the details panel in the editor, serialization, garbage Collection, network replication, and the blueprint and C + + interaction. However, C + + native does not support any form of reflection, so the Unreal engine has its own system for using, querying, and manipulating information about C + + classes, structs, functions, member variables, and enumerations. We deliberately call reflection the property system, because reflection is also a graphical term.
The reflective system can be optionally added. You need to annotate the type or attribute that is exposed to the reflection system so that the Unreal Header Tool (UHT) uses that information to generate specific code when it compiles the project.
Mark
To mark a header file that contains a reflection type, you need to add a special include file at the top of the file. This makes UHT aware that it needs to consider this file and is also needed in the implementation of the reflection system.
#include "Filename.generated.h"
You can now use Uenum (), Uclass (), Ustruct (), Ufunction (), and Uproperty () to annotate different types and member variables in the header file. Each macro appears in front of the type or member variable and can contain additional modifier keywords. Let's take a look at a real example (from Strategygame intercept part):
Base Class for mobile units (soldiers) # Include "StrategyTypes.h" #include "strategychar.generated.h" Uclass (Abstract) class Astrategychar:public Acharacter, Public Istrategyteaminterface{generated_uclass_body ()/** How many resources this pawn was worth when it dies. */uproperty ( Editanywhere, Category=pawn) int32 resourcestogather; /** set attachment for weapon slot */ufunction (blueprintcallable, category=attachment) void Setweaponattachment (class ustrategyattachment* weapon); Ufunction (blueprintcallable, category=attachment) bool isweaponattached (); protected:/** Melee Anim */uproperty (editdefaultsonly, category=pawn) uanimmontage* Meleeanim; /** Armor Attachment Slot */uproperty () ustrategyattachment* armorslot; /** team number */uint8 myteamnum; [More code omitted]};
This header file declares a class that inherits from Acharacter called Astrategychar. It uses Uclass () to indicate that it is a type that needs to be reflected, and it also contains a macro for the Generated_uclass_body () inside a C + + definition. Generated_uclass_body ()/generated_ustruct_body () are necessary in classes or structs that need to be reflected because they add additional functions and typedef to the inside of the class.
The first displayed property is Resourcestogather, which contains annotations for Editanywhere and Category=pawn. This means that this property can be edited in any detail panel in the edit and is displayed in the pawn category. There are several functions that use blueprintcallable and classification as annotations, which means they can be called into these C + + functions in blueprints.
As the definition of Myteamnum shows, mixing properties that require reflection in the same class and properties that do not need reflection are allowed, and you only need to remember that non-reflective properties are not visible to all systems that rely on reflection (such as storing a non-reflective uobject bare pointer is dangerous, Because the garbage collection system doesn't know you're quoting it. )
Each modifier (for example, Edityanywhere, Blueprintcallabe) is defined in ObjectBase.h with a short comment explaining its meaning or purpose. If you don't know what a keyword is, alt+g will usually take you to the definition of ObjectBase.h.
Check out the gameplay programming reference for more information.
Limit
UHT is not a true C + + parser, it only understands a subset of the language and tries to skip text that it does not need. Focus only on those with reflection types, functions, and attributes. However, some usage will still confuse it, so when you need to add a reflection type to a header file, you may need to rewrite some code or use the # if cpp/#endif. You should also avoid using #if/#endif (except With_editor and with_editoronly_data) to include annotation properties or functions, because the generated code will reference them and will cause compilation errors in any project configuration where define is false.
Most of Lou's common types work correctly, but the property system does not represent all possible C + + types (only some template types such as Tarray and tsubclassof are supported, and their template types cannot be nested types). If you annotate a different representation type, UHT will give a more detailed description of the error at run time.
Using reflection data
Most game code can ignore the property system at runtime and enjoy the benefits it brings to you, but you'll find it useful when you're writing tool code or building a gameplay system.
The type hierarchy of the property system is approximately as follows:
Ustruct is the underlying type of all aggregate structures (types that contain other members, such as a C + + class, struct, or function), and should not be confused with struct (that is, uscriptstruct) in C + +. Uclass can contain functions, properties, and their children, while ufunction and ustriptstruct can only contain attributes.
You can get the Uclass and uscriptstruct of the reflection type by using Utypename::staticclass () or ftypename::staticstruct (), and you Also An instance of Uobject gets the type through Instance->getclass (), which cannot be obtained by a struct instance because the struct does not have a common base class or required storage space.
To iterate over all members of a ustruct, you can use Tfielditerator:
For (tfielditerator<uproperty> Propit (GetClass ()); Propit; ++propit) {uproperty* property = *propit;//does something with the property}
Tfielditerator template parameters as a filter (so you can see all the properties and functions by using Ufield, or just one of them). The second parameter of the iterator constructor is used to indicate whether you want only the item introduced by the specified class or struct or include its parent class/struct (the default value). It has no effect on the function.
Each type has a series of tokens (Eclassflags + hasanyclassflag, etc.) and contains a metadata (metadata) storage system that inherits from Ufield. Keyword modifiers are stored as tags or metadata, depending on whether they are required by the game at run time, or simply as a function of the editor. This allows metadata that is useful only to the editor to be removed to save memory, while the runtime markup is always valid.
You can use reflection data to do a lot of different things (enumerate properties, get them in a data-driven way, set values, call reflection functions, even create new objects) and go deep into some of the cases that are said here, Instead, look at the code in UnrealType.h and Class.h and examine one of those code examples that are similar to what you want to do with functionality.
A brief description of the reflection implementation mechanism
If you just want to use a reflection system, you can pass through this chapter, but knowing how it works can help you to motivate some decisions and limit it in the header file that contains the reflection system.
The Unreal build tool (UBT) and Unreal Header tool (UHT) two work together to generate the data required for runtime reflection. The Ubt property records the module for any header file that has at least one reflection type by scanning the header file. If any of the header files change from the last compilation, then UHT is called to take advantage of and update the reflection data. UHT parses the header file, creates a series of reflection data, and generates C + + code that contains the reflected data (placed in the moulde.generated.inl of each module). Note: The latest version will be generated into moudle.generated.cpp, along with various helper functions and thunk functions (each header file. generated.h)
One of the biggest benefits of using generated C + + code to store reflection data is that it guarantees synchronization with the binary. You will never load stale or outdated reflection data, because it is compiled at the same time as other code of the engine, and it uses C + + expressions to calculate member offsets when the program starts, rather than reverse engineering by a combination of specific platform/compiler/optimizations. UHT is built as a separate program that does not use any generated header files, so it avoids the problem of chicken eggs and eggs, which has been criticized in the script compiler of Unreal 3.
The generated Staticclass (), staticstruct () functions are intended to make the current type better at getting reflection data, and that this conversion function (thunks) is used to invoke C + + functions in blueprints or network replication. These must be declared as part of a class or struct, which explains why the Generated_uclass_body () or generated_ustruct_body () macros are included in the type of your reflection system, and include " These macros are defined in the header file for typename.generated.h.
Unreal 4 Property system (reflection) translation