The basis of the gameplay for Unreal Engine 4 provides the developer with a powerful set of classes for creating a game. Your project can be a shooter, farm simulator, deep RPG - it doesn’t matter, the foundation is very versatile, does some of the hard work for you and sets some standards. It is quite integrated into the engine, so I recommend that you stick to these classes, rather than trying to reinvent your own game base, as is often the case with engines like Unity3D. Understanding this framework is very important for successful and effective work on projects.
Who is this article for?
For everyone who is interested in creating games in UE4, specifically in C ++, and who wants to learn more about the basis of the Unreal gameplay. This post discusses the base classes that you will use at the heart of the gameplay, explains their use, the process of creating their instances with the engine, and how to access these classes from other parts of the game code. Most information is also valid for blueprints.
If you want to get acquainted with the basics of Unreal Engine 4, then study my previous
manual . I also have a separate guide dedicated to
virtual reality for beginners . It is useful to those who study the specifics of VR in the Unreal Engine 4.
When creating games in Unreal Engine 4, you will encounter many ready-made boilerplate blanks. There are several classes that you will often use when creating games in C ++ or in blueprints. We will look at each of these classes, their nice features, and learn how to refer to them from other parts of the code. Most of the information in this guide also applies to blueprints, however, I use code fragments in C ++ and therefore some functions will not be available in blueprints and are useful only for C ++ users.
')
Actor
Probably the most frequently used class in games. Actor is the basis for any object on a level, including players controlled by AI of enemies, doors, walls and gameplay objects. Actors are created using ActorComponents (see the next section), such as StaticMeshComponent, CharacterMovementComponent, ParticleComponent, and many others. Even classes such as GameMode (see below) are actors (although GameMode does not have a “real” position in the world). Let's discuss a couple of aspects that you need to know about actors.
Actor is a class that can be replicated over a network (for multi-user mode). This is easily done using a call in the SetReplicates (true) constructor. To create effective network programming actors need to take into account many aspects that I can not consider in this article.
Actors support the concept of taking damage. Damage can be applied directly to the actor using MyActor-> TakeDamage (...) or via UGameplayStatics :: ApplyDamage (...). It is worth considering that there are variations: PointDamage (for example, for weapons, the hit from which is calculated by the ray tracing (hitscan)) and RadialDamage (for example, for explosions). The official Unreal Engine website has a great introductory article
Damage in UE4 .
Create a new instance of the actor in the code, you can simply using GetWorld () -> SpawnActor <T> (...); where T is the return class, for example, AActor for one of your own classes — AGadgetActor, AGameplayProp, etc.
Here is an example code in which an actor is created during the execution of an application:
FTransform SpawnTM; FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; SpawnParams.Owner = GetOwner(); SpawnParams.Instigator = Cast<APawn>(GetOwner()); ASEquippableActor* NewItem = GetWorld()->SpawnActor<ASEquippableActor>(NewItemClass, SpawnTM, SpawnParams);
There are many ways to access actors. Usually you will have a pointer / link to a specific actor that you need. In the example shown above, we store a pointer to the actor of the worn object in a variable and begin to manipulate the actor instance through it.
A very useful function that can be used for prototyping or mastering the engine is
UGameplayStatics :: GetAllActorsOfClass (...) . It allows us to get an array of all the actors of the passed class (including the generated classes; if you pass as a class Actor, then we get ALL the objects of the level). This function is often feared and avoided as a not very effective way of interacting with the environment, but sometimes it is the only available tool.
Actors do not have their own transfer, rotation or scale. All this is set and obtained using RootComponent, i.e. the top-level component in the SceneComponents hierarchy (more on SceneComponents is described below). The most commonly used functions like MyActor-> GetActorLocation () actually go to the RootComponent and return its location in the world.
Here are some more useful functions that are used in the context of an actor:
- BeginPlay // “First” function, called after the creation and full initialization of the actor. This is a convenient place to set the base logic, the timer, and make changes to the properties, because the actor is already fully initialized and can perform queries to its environment.
- Tick // Called every frame. For most actors, you can disable it for performance reasons, but by default it is enabled. Great for quickly setting up dynamic logic and checking conditions in each frame. Gradually, you will begin to move more and more code related to the events of logic from the timers to logic that operates at lower frequencies.
- EndPlay // Called when the actor is removed from the world. Contains “EEndPlayReason” indicating the reason for the call.
- GetComponentByClass // Finds one instance of a component of a particular class. It is very useful when you do not know the exact type of actor, but you know that it must contain a certain type of component. There is also a GetComponentsByClass that returns all instances of the class, and not just the first one found.
- GetActorLocation // And all its variations - * Rotation, * Scale, including SetActorLocation , etc.
- NotifyActorBeginOverlap // Convenient for checking overlays caused by any of its components. In this way, you can quickly customize gameplay triggers.
- GetOverlappingActors // Finds which other actors intersect with the selected one. There is also an option for components: GetOverlappingComponents
Actor contains a huge functionality and a lot of variables - it is the foundation of the gameplay core in the Unreal Engine, so this is not surprising. For further study of this class, it would be nice to open the header file Actor.h in Visual Studio and see what functionality it has. In the article we still have a lot to consider, so let's move on to the next class in the list.
Actorcomponent
The components are located inside the actors, the standard components are StaticMeshComponent, CharacterMovementComponent, CameraComponent and SphereComponent. Each of these components handles its own particular task, for example, movement, physical interaction (for example, the amount of collision to clearly check interacting actors) or visually display something in the world, for example, the mesh of a player.
A subclass of this component is
SceneComponent - this is the base class for everything associated with Transform (Position, Rotation, Scale) that supports pinning. For example, we can attach a CameraComponent to a SpringArmComponent to configure a third-person camera. Both transform and attachment are required to set the relative position correctly.
Most often, components are created in the actor constructor, but you can also create and destroy them during execution. To begin, let's consider one of the constructors of my actor.
ASEquippableActor::ASEquippableActor() { PrimaryActorTick.bCanEverTick = true; MeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("MeshComp")); MeshComp->SetCollisionObjectType(ECC_WorldDynamic);
USkeletalMeshComponent is created using the
CreateDefaultSubobject <T> (actor function) and requires the name to be specified (this name can be seen in the list of blueprint components). If you are writing game code in C ++, you will often use this function, but ONLY within the context of the constructor.
You may also notice that we set MeshComp as the new RootComponent. All Scene Components should now be attached to this mesh, which can be easily done using the following line:
WidgetComp = CreateDefaultSubobject<UWidgetComponent>(TEXT("InteractWidgetComp")); WidgetComp->SetupAttachment(MeshComp);
SetupAttachment will handle the initial attachment; it is expected that it will be called in the constructor for ALL components of the scene, except for the RootComponent itself. One may wonder why my ItemComponent does not call this SetupAttachment function. It happened so simply because this component is an ActorComponent, but NOT a SceneComponent and does not have a Transform (position, rotation, scale), and therefore should not be added to the hierarchy. However, the component will still register with Actor. Being separated from the hierarchy means that functions like MyActor-> GetComponentByClass will return all ActorComponents and SceneComponents.
Along with Actor, these components are crucial for creating games in both C ++ and blueprints. They are the building blocks of the game. You can easily create your own components so that they handle some specific aspects of the game, such as the HealthComponent, which stores health points and responds to the damage taken by its parent actor.
Using the code below, you can create your own components during the execution process. This is different from the CreateDefaultSubobject behavior, which is used only for constructors.
UActorComponent* SpawnedComponent = NewObject<UActorComponent>(this, UStaticMeshComponent::StaticClass(), TEXT("DynamicSpawnedMeshCompoent")); if (SpawnedComponent) { SpawnedComponent->RegisterComponent(); }
Here is some of the useful functionality of ActorComponents:
- TickComponent () // Like the actor's Tick (), each frame is executed to handle high-frequency logic.
- bool bIsActive and related functions like Activate, Deactivate, ... Used to fully enable / disable a component (including TickComponent) without destroying the component and removing it from the actor.
To ensure the replication of ActorComponent, it is necessary to call the function SetIsReplicated (true), whose name is slightly different from the function of the actor. This is necessary only when you need to replicate a specific part of the component's logic, for example, a variable when you call a function, that is, you do not need to replicate all components of the replicated actor.
Playercontroller
This is the base class for the player receiving input from the user. PlayerController itself is not visually displayed in the environment, instead it controls the Pawn instance, which determines the visual and physical representation of this player in the world. During gameplay, a player may have several different Pawn (for example, a vehicle or a fresh copy of Pawn in respawn), and an instance of PlayerController remains the same throughout the level. This is important, because at some points PlayerController may not have any pawn at all. This means that things like opening the menu should be added to the PlayerController, and not to the Pawn class.
In multiplayer games, PlayerController exists only on the client that owns it and on the server. This means that in a 4-player game, the server has 4 player controllers, and each client only has one. It is very important to understand when it is necessary to use variables; if all players require replication of a player variable, then it should exist not in PlayerController, but in Pawn or even in PlayerState (discussed below).
Accessing PlayerControllers
- GetWorld () -> GetPlayerControllerIterator () // GetWorld is available in any Actor instance
- PlayerState-> GetOwner () // the owner state players is of type PlayerController, and you must transfer it to PlayerController yourself.
- Pawn-> GetController () // Set only when the player already owns (ie controls) PlayerController.
This class contains the
PlayerCameraManager , which handles the camera’s view targets and transforms, including shaking. Another important class that PlayerController manages is HUD (discussed below). It is used for rendering on Canvas (now used less frequently because there is a UMG) and it can be used to manage the data that needs to be transferred to the UMG interface.
When a new player connects to GameMode, PlayerController is created for this player in the GameModeBase class using Login ().
Pawn
It is a physical and visual representation of what the player (or AI) controls. It can be a car, a warrior, a tower, or anything else that represents a character in a game. The standard Pawn subclass is Character, which implements SkeletalMesh and, more importantly, CharacterMovementComponent with many options to fine-tune the player’s movement around the environment using the usual motion shooter.
In multiplayer games, each instance of Pawn is replicated to other clients. This means that there are 4 pawn instances in the game for 4 players both on the server and on each client. Quite often, a Pawn instance is “killed” when a player dies, while a respawn creates a new instance. Keep this in mind when storing data that should be saved after the end of the player’s life (or completely abandon this pattern and keep the pawn instance alive at all times)
Accessing Pawn
- PlayerController-> GetPawn () // Only when PlayerController owns Pawn
- GetWorld () -> GetPawnIterator () // GetWorld is available for any Actor instance and returns ALL Pawn, including AI.
Creature
GameModeBase creates a pawn using the SpawnDefaultPawnAtTransform. The GameModeBase class also determines which Pawn class to create.
Gamemodebase
Base class that defines which classes to use (PlayerController, Pawn, HUD, GameState, PlayerState). Often used to set game rules in modes such as “Capture the Flag”; he can handle flags or waves of enemies. Handles other important functions, such as player creation.
GameMode is a subclass of GameModeBase. It contains a few more features that were originally used in the Unreal Tournament, such as MatchState and other shooter features.
In multiplayer mode, the GameMode class exists only on the server! This means that no client has an instance of it. In single player games, it has no influence. You can use GameState, which exists on all clients and is designed specifically for this purpose, to replicate the functions and store the data necessary for GameMode.
Accessing GameMode
- GetWorld () -> GetAuthGameMode () // GetWorld is available for any Actor instance.
- GetGameState () // returns the gamestate for replicating functions and / or variables
- InitGame (...) // initializes some of the rules of the game, including those specified in the URL (for example, “MyMap? MaxPlayersPerTeam = 2”) that can be transmitted when loading levels in the game.
Hud
This is the user interface class. It contains a lot of Canvas code, which is a user interface drawing code written before the appearance of
UMG . Today, the main work on drawing the user interface is engaged in UMG.
The class exists only in the client. Replication is not possible. It is owned by PlayerController.
Accessing HUD
PlayerController-> GetHUD () // Available in local PlayerController.
Creature
It is created using SpawnDefaultHUD (creates a regular AHUD) inside PlayerController, which owns the HUD, and then overwritten by GameModeBase using the InitializeHUDForPlayer HUD class specified in GameModeBase.
My personal notes
I began to use this class less and less, and I use UMG, which can be controlled through PlayerController. Don't forget - before creating widgets in multiplayer games, you need to make sure that the player's controller is IsLocalController ().
World
UWorld is a top-level object representing the map on which actors and components will exist and render. Contains a constant level and many other objects, such as gamestate, gamemode, as well as lists of Pawns and Controllers on the map.
Line tracing and all its variations are performed through the World using functions such as
World- >
LineTraceSingleByChannel and many other similar variations.
Accessing the World
To gain access, just call GetWorld () inside the actors.
When you need to get an instance of World in
static functions , you need to pass WorldContextObject, which is essentially a word for any actor that you can use to call -> GetWorld (). Here is an example from one of my header files:
static APlayerController* GetFirstLocalPlayerController(UObject* WorldContextObject);
Gameinstance
GameInstance has one instance that continues to exist for the duration of the entire game. When navigating between maps and menus, the same instance of this class will be saved. You can use this class to create event handlers or handle network errors, load user data such as game parameters, and functions that are not related to just one game level.
Accessing GameInstance
- GetWorld () -> GetGameInstance <T> (); // where T is the class type, for example, GetGameInstance <UGameInstance> () or you own native type.
- Actor-> GetGameInstance ()
My personal notes
Usually not used in the early stages of a project. Do not do anything critical, unless you dive into the development (can control aspects such as gaming sessions, playing a demo or transferring data between levels)
Playerstate
Container for variables replicated between client / server for an individual player. In multiplayer games, it is not designed to perform logic and is simply a data container, since PlayerController is not available to all clients, and Pawn is often destroyed when the game player dies, therefore not applicable to data that should be stored after death.
Accessing PlayerState
Pawn contains it as a variable Pawn-> PlayerState, also available in Controller-> PlayerState. PlayerState in Pawn is assigned only when Pawn owns the Controller; otherwise, it is nullptr.
A list of all available PlayerState instances (for example, all players in a match) can be obtained via GameState-> PlayerArray.
Creature
The creating class is assigned in GameMode (PlayerStateClass) and created in AController :: InitPlayerState ()
My personal notes
Useful only when working on multiplayer games.
GameStateBase
Looks like PlayerState, but provides clients with GameMode information. Since the GameMode instance does not exist in clients, but only on the server, this class is a useful container for replicating information, such as the end of a match, team points, etc.
It has two variations - GameState and GameStateBase. GameState handles additional variables required by GameMode (unlike GameModeBase)
Accessing GameStateBase
- World-> GetGameState <T> () // where T is the class being called, for example GetGameState <AGameState> ()
- MyGameMode-> GetGameState () // is stored and accessible in the gamemode instance (only needed on the server that owns a single GameMode instance); customers must use the above call.
My personal notes
Use GameStateBase instead of GameState, only if the gamemode is not inherited from GameMode instead of GameModeBase.
Uobject
The basic object for almost everything in the engine. Actors are inherited from UObject, as well as other base classes, such as GameInstance. It should never be used for rendering, but is very useful for storing data and functions when a struct is not suitable for you.
Creating UObjects
UObjects do not spawn like actors, but are created using NewObject <T> (). For example:
TSubclassOf<UObject> ClassToCreate; UObject* NewDesc = NewObject<UObject>(this, ClassToCreate);
Personal notes
It is unlikely that you will create classes directly from a UObject, unless you have mastered the engine well and do not want to delve into creating your own systems. I, for example, use it to store lists of information from a database.
It can be used for the network, but additional configuration of the object class is required, and the objects must be stored in the actor.
Gameplaystatics
Static classes are used to handle various standard game functions, for example, playing sounds and creating particle effects, creating actors, applying damage to actors, getting a player's Pawn, PlayerController, etc. This class is very useful for all sorts of access to gameplay features. All functions are static, that is, you do not need a pointer to an instance of this class and you can call functions directly from anywhere, as shown in the example below.
Accessing GameplayStatics
Since GameplayStatics is a UBlueprintFunctionLibrary, you can access it from anywhere in the code (or blueprint)
UGameplayStatics::WhateverFunction();
My personal notes
In this class, many useful features and it must be known when creating any game. I recommend
to study it to learn about its capabilities.
Links
Recommended for learning the basics of gameplay and programming in the Unreal Engine 4 materials.