Hello, in this article I want to share with readers my view on the approach to developing on the Unreal Engine 4 and using such a useful class as Actor Component.
I noticed that in different tutorials for Unreal Engine 4 often use a deep and complex hierarchy of class inheritance. Although the Unreal Engine 4 itself pushes to use the component approach based on the Actor Component.
If the reader is not familiar with what Actor, Actor Component is and the Blueprints visual scripting system, then first I recommend reading the following materials:
Unreal Engine 4 / Gameplay Programming / Actors or material in Russian
Introduction to the development of C ++ in UE4 Part 2 (section Gameplay classes: Object, Actor and Component)
Tutorial on the Unreal Engine. Part 2: Blueprints')
Problem
As an example, consider such a thing as a weapon in shooters. It is common practice to create the BaseGun Actor class and implement weapon firing logic in this class, scatter when shooting, create a shooting effect, instantiate Projectile (bullet), etc. This class usually describes most of the functionality of different types of weapons: pistol, rifle, machine. Later, new types of weapons are implemented by inheriting from BaseGun.
What to do if you need to add a shotgun or sniper rifle to the game?
Often, in this case, create descendants classes for new types of weapons. For a sniper rifle, the descendant class will implement additional logic to enable / disable the sniper scope and reduce scatter when shooting through the scope. In the case of a shotgun, you will need to rewrite (override) the logic for firing. After all, a shotgun does not shoot a bullet - it shoots a shot.
It turns out that when a new type of weapon is added to the game, the developer will perform the following set of actions:
- creating a descendant class from one of the already existing weapons or from BaseGun;
- modifying (override) or expanding the logic of the parent class.
This is where the problems arise. The deeper the class hierarchy, the more time it takes a programmer to figure out / remember how the logic of code execution is arranged. And with this approach, you will have to constantly make changes to the parent classes (up to BaseGun). The reason is that at the time of designing the base classes, the developer will almost certainly not be able to figure out which types of weapons will be in the final version of the game.
There is another way. Add logic for the shotgun and sniper rifle to the BaseGun class. But over time, we get a bold class (God object), which does "too much" and violates the principle of "divide and conquer." Making such classes is a bad development strategy.Decision
And if you transfer the logic of weapons in the components? First, determine the list of actions that a player can do with a weapon. All these actions are added to Enum E_WeaponActionType.

To create weapons, we need two classes: BP_BaseWeapon and BP_Weapon_Action.
BP_BaseWeapon
BP_BaseWeapon is Actor, for all weapons it will be the base and base class. In BP_BaseWeapon, we add a CustomAction to every action from the E_WeaponActionType. These actions will be the player interaction interface with the weapon. Also BP_BaseWeapon will contain a collection of objects of type BP_Weapon_Action.

BP_Weapon_Action
BP_Weapon_Action is an ActorComponent, the descendants of this class will implement the weapon's behavior: shot, reload, enable / disable the sniper scope, etc. Each BP_Weapon_Action contains a link to the weapon, as well as the type of action that a particular BP_Weapon_Action performs. Another BP_Weapon_Action contains a set of public functions for working with it from BP_BaseWeapon (StartUseAction, StopUseAction, IsCanUseAction), as well as several protected functions and properties.
BP_BaseWeapon and BP_Weapon_Action will be marked as abstract classes in order not to instantiate them by chance. But for their descendants we will not do that.
And now what a machine gun with a grenade launcher and a gun would look like. This will be a descendant of the class BP_BaseWeapon, in which you need to put three components:
- BP_Fire_Action - implements the behavior of firing cartridges, has a type of Main from E_WeaponActionType;
- BP_Scope_Action - implements on / off display of sniper scope, has the type Secondary of E_WeaponActionType;
- BP_Scope_Action - implements shooting from a grenade launcher, has the type Special from E_WeaponActionType.
When an event is triggered in BP_BaseWeapon, the corresponding behavior will be searched for and StartUseAction will be called for it. For example, when calling UseWeaponAction_Main, BP_Fire_Action will be found and our weapon will fire.

The advantage of this approach is that if a new type of weapon requires a new behavior, we do not rewrite BaseGun or some other parent class and do not override its logic. We create a child of BP_BaseWeapon, and then simply add and customize components that implement the behavior of a new type of weapon. So, we collect new classes from ready-made blocks. If there is no behavior, we simply add a new component based on BP_Weapon_Action.
The use of the described campaign is not limited to the creation of different types of weapons. It can be extended to other gaming subsystems:
- Gamemode for different game modes;
- game abilities of characters;
- create a variety of mobs;
- creation of various interactive game objects, etc.