Part 1. Introduction. Creating a class and adding properties. Extending the C ++ class with Blueprint.Part 2. Classes of gameplay. Structures. Reflection (reflection) in Unreal. Object / Actor iterators. Memory manager and garbage collector.Part 3. Prefixes in class names. Integer types. Types of containers. Container iterators For-each loop, hash functions.
Part 4. Unreal Engine 4 for Unity developers.
Part 5. ...

From the Author: The beginning of the summer turned out to be hot for projects, so the translation was postponed for a long time, then it will be faster.
')
This article is a continuation of the translation of part of the documentation on UE4. You can find the original article by
following this link .
Nice to see you still with us. In the following sections, we will explore the hierarchy of gameplay classes. We start with the basic blocks and talk about how they are related to each other. We will also study how UE uses inheritance and composition to create custom gameplay features.
Gameplay classes: Object, Actor and Component
The four main gameplay classes are most often expanded. These are
UObject, AActor, UActorComponent and
UStruct. Further each of them will be described in detail. You can create types that do not extend any of these classes, but they will not allow you to use most of the built-in features of the engine. As a rule, classes that are created outside the
UObject hierarchy (not inherited from any depth from the
UObject ) exist for the following purposes: integration of third-party libraries, wrapping features of the operating system, etc.
Unreal Objects (UObject)
The base unit in a UE is the
UObject class. This class, together with the
UClass class, provides you with the most important basic engine features:
- Reflection of properties and methods
- Property serialization
- Garbage collector
- Search UObject by name
- Setting Property Values
- Network mode support for properties and methods
Each class inherited from
UObject contains a
UClass singleton created for it. This class contains all the metadata about a class instance. The capabilities of
UObject and
UClass lie (jointly) at the heart of all that the gameplay object does during its life cycle. It is best to think about the difference between
UClass and
UObject as if
UClass describes exactly how the
UObject instance looks, what properties are available for serialization, networking, etc. Basically, gameplay development is not connected with inheritance directly from
UObject , but with inheritance from classes
AActor and
UActorComponents . You do not need to know the details of how
UClass / UObject works. But knowledge of their existence will be useful.
AActor
The
AActor class represents an object that is part of the gameplay. An instance of this class is either hosted by the designer at the level, or created at runtime (using gameplay systems). All objects placed on a level are descendants of this class. For example -
AStaticMeshActor, ACameraActor and
APointLight. AActor is inherited from
UObject, that is, it uses the standard functions that were listed in the previous section.
AActor can be destroyed using game code (C ++ or Blueprint) or the standard garbage collection mechanism (when unloading a level from memory). It provides high-level behavior of our game objects, as well as being the base type that provides replication capability for the network mode. When replicating (in multi-user mode),
AActor also gives access to information about any of its
UActorComponent, which is required to support the operation of the network mode.
AActor has its own behaviors (specializes in inheritance), but other than that, they are containers for the
UActorComponents hierarchy (specializes in composition). This can be done with the help of our
AActor RootComponent member. It contains one
UActorComponent, which, in turn, may contain many others. Before placing an
AActor on a level, it must contain at least a
USceneComponent that stores the position, rotation, and scale of the
AActor.AActor provide access to several events that are triggered during the entire
AActor life cycle
. Below is a short list of some of them:
BeginPlay | Called once the object enters the game. |
Tick | Each frame is called to execute code during this frame. |
Endplay | Called when an object leaves the game. |
For a more detailed study of the class
AActor, you can follow the
link .
Duration of life cycle
In the previous section, the topic of the
AActor life cycle was
touched on a bit. For
Actors placed at the level of the life cycle, you can imagine the following - loading and the beginning of the existence of
Actor and his subsequent destruction (at the unloading level). Let's see what are the processes of creation and destruction during execution. Creating an
AActor is a bit more complicated than creating a regular object. This happens because the
AActor must be registered with different systems (running at run-time) - the physics engine, the manager responsible for the information coming in every frame, etc. Therefore, to create an object, there is the
UWorld :: SpawnActor () method
. After the required
Actor is created successfully, the
BeginPlay () method is
called, followed by the
Tick () method (in the next frame).
If
Actor has existed the time you need, you can destroy it by calling the
Destroy () method
. In this case, the
EndPlay () method will be called
, in which you can write the necessary code that is executed when the object is destroyed. Another option for
Actor's life
control is to use the
LifeSpan field
(lifetime). You can set this value in the constructor (or later at run-time). If the specified time expires, the
Destroy method will be called automatically.
You can learn more about creating
Actors by clicking on the
link.UActorComponent
UActor Components have their own behavior and, as a rule, are responsible for the functionality that is common to different
AActors — for example, displaying meshes, particle systems, camera work, and physical interactions. Unlike the
AActors, who provide the high-level logic of their behavior in your game, the
UActorComponents usually perform individual tasks required to support higher-level objects. Components can be children of other components. They can be attached to only one parent component or
Actor, but the component itself can have many child components. You can imagine this relationship as a tree of components. It should be remembered that the child components have a position, rotation and scale
relative to the parent components or
Actor.There are many options for using
Actors and components. One of the ways to imagine the relations of
Actors and components is the following -
Actor answers the question
“What is this thing? (what is this thing?) ”, and the components - to the question
“ What is it made of? (what is this thing made of?) "- RootComponent - An object that contains a top-level component in the AActor component tree .
- Ticking - A component that updates every frame (tick) is part of the Tick () method of its own AActor.
Dissect First Person Character
In the previous sections there were many words without demonstrations. To illustrate the relationship between
AActor and its
UActor Components, let's examine the
Blueprint that is generated when opening a project based on the
First Person template
. The picture below shows the component tree for the
FirstPersonCharacter Actor. Here
RootComponent is
CapsuleComponent. Attached to the
CapsuleComponent components are
ArrowComponent, Mesh and
FirstPersonCameraComponent. The most deeply nested vertex is the
Mesh1P component
, the parent of which is
FirstPersonCameraComponent. This means that the position of the mesh is relative to this camera.

Graphic this component tree looks like it is shown in the picture below, where you can see all the components in 3d space (except for the Mesh component)

This component tree is attached to one actor-class. As we can see, we can create complex gameplay objects using both inheritance and composition. Inheritance should be used when changing an existing
AActor or
UActorComponent, and composition, if the set of
AActor types must have similar functionality.
Ustruct
To use
UStruct, you do not need to inherit from any particular class, it is enough to note the structure with the
USTRUCT () macro and the build tools will do the main work for you. Unlike
UObject, the garbage collector does not track
UStruct. If you dynamically create instances of them, you must independently manage their life cycle.
UStructs are used so that
POD types have support for
UObject reflection for editing in the
UE, management via
Blueprint, serialization, networking, etc.
Now, after discussing the basics of hierarchies for creating our gameplay classes, it's time to choose your path again. You can learn more about the gameplay classes, explore our examples in search of more information, or continue to dive deeper into the features of C ++ to create a game.
Dive even deeper
Are you sure you want to know more? We will continue to study the work of the engine.
Unreal reflection system
Blog Post:
Property System in Unreal (Reflection)The gameplay classes use special markup, so before moving on to them, let's look at some basic things from the
Unreal properties system
. UE4 uses its own reflection system, which provides you with various dynamic capabilities — garbage collection, serialization, network replication, and
Blueprint / C ++ interaction
. These features are optional, that is, you must add the required markup yourself for your types, otherwise
Unreal ignores them and does not generate the necessary data for reflection. Below is a brief overview of the main markup elements:
- UCLASS () - used to generate reflection data for a class. The class must be a UObject descendant.
- USTRUCT () - used to generate reflection data for the structure
- GENERATED_BODY () - UE4 replaces this with all the necessary generic code that is created for the type.
- UPROPERTY () - allows the use of a UCLASS or USTRUCT member variable as an UPROPERTY. UPROPERTY has many use cases. This macro allows you to make a variable available for replication, serialization, and accessible from Blueprint. It is also used by the garbage collector to track the number of references to a UObject.
- UFUNCTION () - allows a UCLASS or USTRUCT class method to be used as a UFUNCTION. UFUNCTION can allow a method to be called from Blueprint and used as RPCs, etc.
Example of the UCLASS class definition:
#include "MyObject.generated.h" UCLASS(Blueprintable) class UMyObject : public UObject { GENERATED_BODY() public: MyUObject(); UPROPERTY(BlueprintReadOnly, EditAnywhere) float ExampleProperty; UFUNCTION(BlueprintCallable) void ExampleFunction(); };
You may notice for the first time the inclusion of the
“MyClass.generated.h” header
. Unreal will place all generated data for reflection in this file. In the declaration of your type (in the list of included files) this file should be located last.
You may also have noticed that it is possible to add additional qualifiers to markup macros. In the code above, some of the most common ones are added for demonstration. They allow you to specify the specific behavior that our types possess:
- Blueprintable - The class can be extended using Blueprint.
- BlueprintReadOnly - The property is read only from Blueprint, and is not writable.
- Category - Defines the section in which the property is displayed in the Details view in the editor. Used for organization.
- BlueprintCallable - The function can be called from Blueprint.
The number of qualifiers is very large, so we will not list them here, but give links to the relevant sections of the documentation:
Object / Actor iterators
Object iterators are a very useful tool for iterating through all instances of a particular type of
UObject and its subclasses.
// UObject for (TObjectIterator<UObject> It; It; ++It) { UObject* CurrentObject = *It; UE_LOG(LogTemp, Log, TEXT("Found UObject named: %s"), *CurrentObject.GetName()); }
You can limit the search by providing a more specific type of iterator. Suppose you have a class called
UMyClass, inherited from
UObject. You can find all instances of this class (and all that are its descendants) as follows:
for (TObjectIterator<UMyClass> It; It; ++It) {
WARNING: Using iterator objects in
PIE (Play in Editor) can produce unexpected results. While the editor is loaded, the object iterator will return all the
UObjects created for your instance of the game world, in addition to those used in the editor.
Actor iterators work in a similar way, just like object iterators, but they work only for the heirs from the
AActor. Actor iterators
do not have the problem mentioned above and return only those objects that are used in the current exam of the game world.
When an
Actor iterator is
created, it needs to pass a pointer to a
UWorld instance
. Many child classes from
UObject, for example,
APlayerController provide this method. If you are not sure, you can check the return value of the
ImplementsGetWorld method to see if a particular class
supports the GetWorld method
. APlayerController* MyPC = GetMyPlayerControllerFromSomewhere(); UWorld* World = MyPC->GetWorld(); // Like object iterators, you can provide a specific class to get only objects that are // or derive from that class for (TActorIterator<AEnemy> It(World); It; ++It) { // ... }
Since the
AActor is a heir from
UObject, you can use
TObjectIterator as well to find all
AActors instances
. But be careful in
PIE!Memory Manager and Garbage Collector
Actors are usually not collected by the garbage collector. You must manually call the
Destroy method after spawning
Actor. Removal will not happen immediately, but only during the next phase of garbage collection.
This case is most common if you have
Actors with
UObject properties
. UCLASS() class AMyActor : public AActor { GENERATED_BODY() public: UPROPERTY() MyGCType* SafeObject; MyGCType* DoomedObject; AMyActor(const FObjectInitializer & ObjectInitializer) : Super(ObjectInitializer) { SafeObject = NewObject<MyGCType>(); DoomedObject = NewObject<MyGCType>(); } }; void SpawnMyActor(UWorld * World, FVector Location, FRotator Rotation) { World->SpawnActor<AMyActor>(Location, Rotation); }
When we call this function, we generate
Actor in our world. Its constructor creates two objects. One is labeled
UPROPERTY, the other is a regular pointer. While
Actors are part of the root object,
SafeObject will not be collected by the garbage collector, since it can be accessed from this root.
DoomedObject, however, will have a different life cycle. Since it is not marked as
UPROPERTY, the garbage collector knows nothing about references to it and eventually it will be destroyed.
When a
UObject is collected by the garbage collector, all
UPROPERTY references will get
nullptr values
. This is done to safely check whether the object is destroyed by the garbage collector or not.
if (MyActor->SafeObject != nullptr) {
This is an important note, because, as mentioned earlier, the actors for whom the
Destroy () method was called are not deleted until the next phase of garbage collection. You can check whether the
UObject is waiting for deletion using the
IsPendingKill () method
. If the method returns true, the object is considered dead and should not be used.
UStructs
As mentioned earlier,
UStructs is a lightweight version of
UObject. UStructs cannot be collected by the garbage collector. If you are using a dynamic copy of
UStructs, you can use smart pointers, which we will talk about later.
Links not to UObject
Typically,
non-UObjects may also have the ability to add an object reference to prevent them being deleted by the garbage collector. To do this, the object must inherit
FGCObject and override the
AddReferencedObjects method
. class FMyNormalClass : public FGCObject { public: UObject * SafeObject; FMyNormalClass(Uobject * Object) : SafeObject(Object) { } void AddReferencedObjects(FReferenceCollector & Collector) override { Collector.AddReferencedObject(SafeObject); } }
We use the
FReferenceCollector to manually add a hard link to the required
UObject, which should not be collected by the garbage collector. When an object is deleted (the destructor is triggered), all links added by it will be deleted.
PS: I ask all suggestions for correcting errors and inaccuracies sent in a personal.