Subcomponents for logic?

Hi,

I have migrated over here from the CryEngine and am having a little trouble with UE4s class templates. I am currently building a character properties system that is split into different subsystems. So for example there is a system that manages health and a different system that handles a characters movement setup. The idea behind this setup is that you can combine different templates for the different systems into different characters. I had already programmed this for CryEngine and it worked. As I am currently migrating this into UE4 I ran into a problem with the UE4 classes.
To enable reflection and blueprint setup, which seems to be key in UE4, my classes need to be subclasses of the UE class templates. I read the overview for the classes and decided to let my classes inherit from UActorComponent as my system would be a subobject of an Actor and it should be tickable. That seemed to work for a single component. But apparently ActorComponents can’t be subobjects of ActorComponents, which admiditly makes sense but is also somewhat problematic as I now don’t know from what class to inherit my classes…

To the technical part:

I want to build the following hierachy:
Actor->HealthStats->Individual health components (Array)

Here the class HealthStats:

    #pragma once
    
    #include "HealthComponent.h"
    #include "Components/ActorComponent.h"
    #include "CharacterHealthStats.generated.h"
    
    UCLASS()
    class UCharacterHealthStats : public UActorComponent
    {
    	GENERATED_UCLASS_BODY()
    
    	enum EHealthComponents {
    		HEALTH = 0,
    		ARMOR,
    		LAST
    	};
    	UPROPERTY(EditDefaultsOnly, Category = HealthComponents)
    	TSubobjectPtr<UHealthComponent> m_healthComponents[LAST];

        Update(float frameTime);
    };

// .CPP starts here

UCharacterHealthStats::UCharacterHealthStats(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
	m_healthComponents[HEALTH] = PCIP.CreateDefaultSubobject<UHealthComponent>(this, TEXT("Health"));
	m_healthComponents[ARMOR] = PCIP.CreateDefaultSubobject<UHealthComponent>(this, TEXT("Armor"));
}

UCharacterHealthStats::Update(float frameTime) {
//Call Update(frameTime) for all components
}

Here is the code for UHealthComponent:

#pragma once

#include "HealthComponent.generated.h"

UCLASS()
class UHealthComponent : UActorComponent
{
	GENERATED_UCLASS_BODY()

	UFUNCTION(BlueprintCallable, Category = Functions)
	void Update(float time);

	UFUNCTION(BlueprintCallable, Category = Functions)
	void Drain(float drain);

	UPROPERTY(EditDefaultsOnly, Category = Values)
	int32	m_maxValue;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Values)
	float	m_value;
};

UHealthComponent::UHealthComponent(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
	m_maxValue = 100;
	m_value = 100;
}

void UHealthComponent::Update(float frameTime) {
//Do what needs to be done
}

A few last things to note:

  • As you can see neither of the two need any kind of “worldly” representation
  • It doesn’t matter to me wether the system itself or the actor ticks the classes, but I would prefer the Actor to do it.

So I hope this gives you enough information to be able to help me.

Best regards!

Main question is why would you want to make nested actor components ? It doesn’t bring any benefits expect more complicated design.

What do you want to do, is to create AttributeComponent (or something like that), which will be simply attached to actor.

AttributeComponent will contain any attributes you want as UPROPERTY(), and any function helpers you may need to modify those attributes.

Also remember to override OnRegister(), as this is the place where you want to register all changes to your attributes if you have more complex setup like one attribute calculated from another.

You might also want to create UBlueprintFunctionLibrary with static functions which will be helping to work with your attributes.

If you want to take quick look at what I mean you can look at engine source for AbilitySystem module or on my Action RPG sample (in the works):
https://github.com/iniside/ActionRPGGame/tree/master/Source/ActionRPGGame/Componenets
For AttributeComponenets. I have finished implementing this (it boiled down to more pressing issues), but you can see beginings of the system. It also uses reflection system, to take arbitary properties out of class by specified their name.

Thanks for the quick reply!

After a quick look at your code I think it does what I want to do just one step higher in the hierachy. The nesting makes sense when you get to a higher level of complexity in the attributes you have to organize. The code I posted was a short version of my real system to convey the idea of the structure. My long version has 14 subcomponents with 9 attributes each which would clutter up the character-class and also make deriving from the character a lot more difficult.

Is there a template for an AttributeComponent or would I have to write it myself? And if I have to write it myself would I derive from Object or another class?

Well for me the fact there there might 100 possible attributes in single class is not an problem. I never access them directly, I access them trough function and by specifing their name like:

GetAttribute(“Health”);

If that still doesn’t suit you, you can pack attributes into structs like:

USTRUCT(BlueprintType)
struct FMyAttribute
{
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Attribute")
  float Health;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Attribute")
float Mana;
}

And then just declare them as:

FMyAttribute MyAttribute:

Believe me it’s better solution, that trying to over abstract and over organize everything ;). I’ve tried the more generic the better approach before, and all it does it. increase complexity in exponentially.

You can also create hierarchy of Componenets like:

MyAttributeComponent : UActorComponent


MyAwsomeAttribute : MyAttributeComponent;

etc.

But it will require some planning ahead of what you want at each level of hierarchy.

Least but not least. You can create nested Subobjects when you use UObject instead of ActorComponent.
But this makes it hard to expose back to blueprint and edit properties inside editor.