How can I achieve an update of my class when one of its parts changed?

Hi there,

Abstract:
I am currently trying to build a well structured class hierarchy for a game, where you can fly with planes.
My plane has some (sub-)parts which must have the ability to inform my plane that something has changed,
so that the plane updates its current settings.

Specific:
I have a plane with properties like health, speed and upgrades. Upgrades can either improve health, speed or both at the same time.
I don’t want to store all these data(health, regeneration, …, speed, acceleration …) in one single class. So I wrap them up into

  • Health
  • Engine(Propulsor - handles speed)
  • Upgrades

or in code:


UCLASS()
class FLYING_API UPlane: public UObject
{
	GENERATED_BODY()
public: 
	class UHealth* Health;
	class UPropulsor* Propulsor;
	class UUpgradeManager* UpgradeManager;
	
	void CalculateCurrentSettings();
}

My problem is especially the UpgradeManager.
The UpgradeManager holds all the Upgrades which were added to my Plane.

To modify my upgrades, I would call: MyPlane->UpgradeManager->changeUpgrade(/whatever options/);

Since Upgrades are holding additional data which can influence my health or my speed, my Plane
has to know when it has to calculate its current settings again.

This means after a call of
MyPlane->UpgradeManager->changeUpgrade(/whatever options/);

I have to call
MyPlane->CalculateCurrentSettings();
from within the UpgradeManager.

Here are my ideas:
At first I thought about something like an Observer Pattern.
My Plane listens on my Upgrademanager for changes. And if the Upgrademanager informed the Plane of a change, the Plane would call the CalculateCurrentSettings()-function.
I am not sure If I want to expose a listener or addListener/removeListener-functions in the UpgradeManager.

I also thought about initializing my Upgrademanager with a function pointer to my MyPlane->CalculateCurrentSettings()-function. Then I could simply execute the function behind this pointer
after I changed an Upgrade.
In this case, I have no clue how this will work, if I further inherit classes from UPlane.
(Is the pointer to the CalculateCurrentSettings() still holding in the UpgradeManger even if I override my CalculateCurrentSettings-function in my inherited class?
Do I have to initialize my UpgradeManager again?)

Questions:

  1. Is this class hierarchy well designed?
  2. What is your opinion to these 2 ideas?
  3. Does another design pattern exist for this case?
  4. I also heard something of delegates, but I am not entirely sure of the use right now.

Some other questions in this context:

Currently I want to design my AFlyingPawn like this:


class AFlyingPawn: public APawn
{
	GENERATED_BODY()

	class UPlane* MyPlane;

	// ... more code
}

With switching my MyPlane, I can “load” my properties for this pawn.
(MyPlane holds again health, speed and upgrades).

My player controller would create/posses such an AFlyingPawn.
Then it would init it with my UPlane, which is stored in my player controller(or somewhere else).
AFlyingPawn->init(PlayerController->PlayerPlanes[0]);

The AFlyingPawn would than act/fly based on the properties given through the UPlane-Object.

Questions:

  1. In example codes properties like health, speed, ammo are placed directly into the Pawn. Is it not encouraged to wrap these flat data?
  2. Especially for regeneration I am going to need a tick-function. Has an UObject a tick function? Or do I have to design such behaviour inside an AActor?
  3. In my constructor of AFlyingPawn I had some struggles with calling the constructor of UPlane. When should I use NewObject, NewNamedObject, ConstructObject or CreateDefaultSubobject?

Im still very new to this topic in general and right now I am pretty much confused with all these extra decorators for properties, several constructor, separate init-functions and so on…
So thanks for helping, it’s much appreciated :slight_smile:

And another question: When should I use this forum and when the Answerhub?

First, I’m going to point out this hierarchy is a little weird looking.

Why do you have “UPlane” anyway? It should probably be “APlane” and derived from AActor instead of UObject.
I think you’re trying to go too far down the tree.

The idea was to encapsulate all my current data of my plane inclusive all the necessary limits for these stats.
My thought process was like this:

  1. My player probably has several planes. He can fly only one.

  2. I want to generally describe how a plane works. (In my AFlyingPawn).

  3. To load my configuration for my plane(which I am currently flying), I simply init my AFlyingPawn with my UPlane-object.

  4. Since I only want UPlane to hold my stats, I thought UObject would be enough. I will probably switch to AActor to simply have a tick-function.

Generally speaking: how would you initialize your Pawn with several variables?

With this class-hierarchy I wanted to replicate the structure of my plane. Since my plane has body(UHealth), an engine (UPropulsor) and some
additional Upgrades(UpgradeManager).

I simply want to avoid that my Pawn becomes to big (like 2000+ lines of code), since I have like 20-30 variables later on, which can influence the
behavior of my plane.

I meant you’re going too far down the tree since your “UPlane” class should really be the “AFlyingPawn” class. Your “Character” is really your PlayerController which can possess several different types of pawn.

That is kind of the summarizing idea of the Player->PlayerController->Pawn/Character. If you plan on having a non-plane character or something, you simply disable it, hide it and attach it to the center of the plane or something when your PlayerController possesses the plane. After the PlayerController takes back over the human character, you simply re-enable him, teleport him in a direction away from the plane and un-hide him.

As for the event listener part, I’d go with delegates/function pointers (same thing in C++ terms).