Implement a basic health UActorComponent in C++ for Unreal Engine.
Implement a reusable health component that any Actor can attach. It tracks max and current health, applies damage and healing with bounds, and notifies its owner when the Actor dies — without the component itself destroying the owner.
Requirements:
the component must not call into or destroy the owner directly.
- Derive from
UActorComponent(a reusable component), not fromAActor. ApplyDamage/Healmust clampCurrentHealthbetween0andMaxHealth.- On reaching zero, broadcast a multicast delegate (
OnDeath) so the owner reacts —
Complete the two member functions:
void UHealthComponent::ApplyDamage(float Amount)
{
// your code here: clamp the health down and broadcast OnDeath at zero
}
void UHealthComponent::Heal(float Amount)
{
// your code here: clamp the health up to MaxHealth
}
Write the implementation.
Derive from UActorComponent, store MaxHealth and CurrentHealth, expose ApplyDamage/Heal that clamp the value, and broadcast a death event when health hits zero. A multicast delegate lets the owning Actor react without tight coupling.
- ✗Not clamping health, letting it go negative or above
MaxHealth - ✗Deriving the health holder from
AActorinstead ofUActorComponent - ✗Destroying the owner directly inside the component instead of broadcasting an event
- →How would you replicate
CurrentHealthso all clients see the health bar? - →Why broadcast a delegate instead of calling the owner's death function directly?
Basic health component
// HealthComponent.h
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeath);
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class UHealthComponent : public UActorComponent
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, Category="Health")
float MaxHealth = 100.f;
UPROPERTY(BlueprintAssignable, Category="Health")
FOnDeath OnDeath;
UFUNCTION(BlueprintCallable, Category="Health")
void ApplyDamage(float Amount);
UFUNCTION(BlueprintCallable, Category="Health")
void Heal(float Amount);
bool IsDead() const { return CurrentHealth <= 0.f; }
protected:
virtual void BeginPlay() override;
private:
float CurrentHealth = 0.f;
};
// HealthComponent.cpp
void UHealthComponent::BeginPlay()
{
Super::BeginPlay();
CurrentHealth = MaxHealth; // initialize to full health
}
void UHealthComponent::ApplyDamage(float Amount)
{
if (Amount <= 0.f || IsDead()) // negative damage and double-death rejected
{
return;
}
CurrentHealth = FMath::Clamp(CurrentHealth - Amount, 0.f, MaxHealth);
if (IsDead())
{
OnDeath.Broadcast(); // the owner decides what to do
}
}
void UHealthComponent::Heal(float Amount)
{
if (Amount <= 0.f || IsDead())
{
return;
}
CurrentHealth = FMath::Clamp(CurrentHealth + Amount, 0.f, MaxHealth);
}
The component encapsulates one responsibility and is reusable on any Actor. It does not destroy the owner itself — it only broadcasts OnDeath, and the Actor subscribes and reacts. This breaks tight coupling.