Getter/Setter not used in the Details Panel of a Component

Hey everyone,

I want to encapsulate my private fields with accessor and mutator functions. Within the actual Blueprint, the field *maxHealth *is hidden as I expected and only accessible through the getter and setter. But I also want to be able to set this value through the Actor Component’s Details Panel. “EditAnywhere” makes the field accessible, but a value change is not processed by the setter function. I guess I don’t fully understand the underlying concept. Could you please point out where I am wrong?



#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "HealthComponent.generated.h"

UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class ACT_API UHealthComponent : public UActorComponent {
   GENERATED_BODY()

private:
   UPROPERTY(EditAnywhere, BlueprintGetter = GetMaxHealth, BlueprintSetter = SetMaxHealth)
   float maxHealth = 25.0f;

public:
   UHealthComponent();

   UFUNCTION(BlueprintGetter)
   float GetMaxHealth() const { return maxHealth; }

   UFUNCTION(BlueprintSetter)
   void SetMaxHealth(float value) {
      maxHealth = 10.0f; // this hardcoded 10 is for debugging only, to see if there is actually something happening
   }


I might be wrong here, but BlueprintGetter/Setter will only be called when you are in blueprints, trying to Get/Set a variable. For example, when you are dragging the variable into the blueprint canvas, and typing Get. Here’s their description, copied from ObjectMacros.h



/// This function is used as the get accessor for a blueprint exposed property. Implies BlueprintPure and BlueprintCallable.
BlueprintGetter,

/// This function is used as the set accessor for a blueprint exposed property. Implies BlueprintCallable.
BlueprintSetter,


You could be true. But in that case, why do I need those Macros then anyway? Couldn’t I just make a private field (without the UPROPERTY macro) and only UFUNCTION a getter and a setter? Where is the difference?



#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "HealthComponent.generated.h"

UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class ACT_API UHealthComponent : public UActorComponent {
   GENERATED_BODY()

private:
   float maxHealth = 25.0f;

public:
   UHealthComponent();

   UFUNCTION(BlueprintCallable)
   float GetMaxHealth() const { return maxHealth; }

   UFUNCTION(BlueprintCallable)
   void SetMaxHealth(float value) {
      maxHealth = 10.0f; // this hardcoded 10 is for debugging only, to see if there is actually something happening
   }


The engine uses reflection to show those properties in the details panel, and indeed most of the editor - so it won’t call those functions, it sets the properties directly. Encapsulation doesn’t matter.

How do I limit the Leveldesigner than not to put in numbers into the detail panel I don’t want them to use?

Calling the setter in the constructer so that the values got checked at least once?



UHealthComponent::UHealthComponent() {
   PrimaryComponentTick.bCanEverTick = true;
   SetMaxHealth(GetMaxHealth());
}


I feel like not understanding the conceptual design decision behind the whole thing. When I create a Variable with the Blueprint editor, I do have all these nice options like Slider- and ValueRange which are accessible via UPROPERTY Meta, but what if it is getting a little more complicated than a clamp?

You are looking for PostEditChangeProperty: How do I use PostEditChangeProperty? - C++ - Epic Developer Community Forums

@DasMaeh UE4 AnswerHub post: UProperty min & max values ?

Yeah… that is what I feared. My C++ knowledge is only limited but I advanced a lot within the last months by reading about Programming Patterns and stuff.
In my last Job Interview for a AAA company, they told me to use encapsulation more often which was a comment towards a 2D rendering engine I wrote myself in c++. For me, it is confusing to see that the Unreal Engine often goes a slightly different way. I know that Unity also uses reflection, but they provide an event (OnValidate) which is called every time someone changed a value within the editor. This allows me to add regular encapsulation like in the following example:



private float maxHealth;
public float GetMaxHealth() {return maxHealth;}
public void SetMaxHealth(value) { maxHealth = value; }

private void OnValidate() {
    SetMaxHeath(maxHealth);
}


Similar to PostEditChangeProperty as **_brunocoimbra **mentioned. But I guess… this isn’t just something you do with Unreal, in general, as a “best practice”.

Thank you for clarifying this for me.

I actually use PostEditChangeProperty on Unreal at the same rate that I use OnValidate on Unity, and seems to be for the same reason as you.

I would say that this is up to you (or your company) to define which are the best practice here. Mine is to always ensure that the data is in a valid range while in edit time to avoid issues at play time, so if PostEditChangeProperty helps me to achieve that then I will use it extensively.