Download

Many instances of a class or a single instance and many structs containing only data

I have a base class called [FONT=courier new]UAttack that holds data and functions for my Pawns to perform attacks. This class can then be inherited to add functionality by overriding functions. Each Pawn can have multiple attacks and the [FONT=courier new]UAttack class needs to have a way to store its state for each pawn. Now I want to know how my Pawns would interact with this class in the most performant way possible. I could only think of these two options:

  1. Make the functions in [FONT=courier new]UAttack const and create one instance of each class at the beginning of the game. Then the Pawns would have a pointer to this single instance and pass these two parameters: a reference to themselves (the pawns) and a reference to a struct containing the state of the attack (which is owned by the pawn). This way there only exists one instance of each of these classes for all pawns to use and each pawn stores the state of its attacks.
  2. Each pawn instantiates its own instance of [FONT=courier new]UAttack and [FONT=courier new]UAttack holds its state (no [FONT=courier new]const functions here).

So the real question here would be: **How much overhead exists on a UE4 object? **([FONT=courier new]UAttackinherits from [FONT=courier new]UObject and [FONT=courier new]FTickableGameObject). Even if coding in “pure” C++ (out of UE4’s framework, without UObject’s overhead) I don’t know if there is a performance difference between those two methods of approaching the problem and I don’t even know how to search about that in Google.

As there are going to be a lot of pawns in the game and each pawn can have multiple attacks, what would be the most memory optimized way?

Some notes

  • If I would use method 1 then [FONT=courier new]UAttack wouldn’t inherit [FONT=courier new]FTickableGameObject and instead the pawn would call tick on the attack because since [FONT=courier new]UAttackwouldn’t be able to contain state we would need to pass it the struct containing its state.
  • I will later extend in BP the classes that inherit [FONT=courier new]UAttackso I can edit values easily.

For the ones that like some code examples, here they are.
[SPOILER]
Method 1


public class UAttack : public UObject
{
    virtual void BeginAttack(APawn* pawn, FPawnAttackState& state);
    virtual void EndAttack(FPawnAttackState& state);

    virtual void Tick(float DeltaTime, FPawnAttackState& state) override;
}

public class UBasicAttack  :  public  UAttack
{
    virtual void BeginAttack(APawn* pawn, FPawnAttackState& state) override;
    virtual void EndAttack(FPawnAttackState& state) override;

    virtual void Tick(float DeltaTime, FPawnAttackState& state) override;
}

Method 2


public class UAttack : public UObject, public FTickableGameObject
{
    APawn* thePawn;

    virtual void BeginAttack();
    virtual void EndAttack();

    virtual void Tick(float DeltaTime) override;
}

public class UBasicAttack  :  public  UAttack
{
    virtual void BeginAttack() override;
    virtual void EndAttack() override;

    virtual void Tick(float DeltaTime) override;
}

[/SPOILER]

Sorry for the long text, thank you for any help.

If only data is varying, then one approach is to use DataTable to store the settings for the various types and load these.
Much easier to edit and tweak.

It is common to implement attack directly in the Base Class for your enemy or hero and simply derive and override that as needed.

Another approach is to bundle your attack functions in an interface : https://wiki.unrealengine.com/Interfaces_in_C%2B%2B

I can’t use a data table because attacks will derive from UAttack and some attacks might have variables the other attacks don’t.

Implementing the attacks in the base class of my heroes/enemies would not work since there are going to have many types of attacks and a pawn/enemy will be able to have more than one attack. I also want to be able to reuse the attack code between heroes/enemies/creatures.

The interface also doesnt help me since it doesnt solve the original problem (1 instance vs many)