Hi all,
I came back to my project today, and I came back very confused because I suddenly started having some weird bugs after not having changed much at all. For some reason, my Health component’s values are persisting between editor play sessions which completely breaks some things. I haven’t gotten a clue why this started happening today, but I’ve been banging my head at this issue for hours with no luck. On a related note, I have a line within “TakeDamage” that uses SetTimer which also causes an unexplainable crash if anyone can figure that out as well.
Anyone know why this might happen?
HealthComponent.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "DamageableInterface.h"
#include "Components/ActorComponent.h"
#include "HealthComponent.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FOnHealthDepleted, float, Damage, FVector, Force, FVector, HitLocation, FName, HitBoneName);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthUpdate, UHealthComponent*, HealthComp);
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class HALOFLOODFANGAME01_API UHealthComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UHealthComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
float TakeDamage(float Damage, FVector Force = FVector(0,0,0), FVector HitLocation = FVector(0,0,0), FName HitBoneName = "", AController* EventInstigator = nullptr, AActor* DamageCauser = nullptr, bool bIgnoreShields = false, bool bIgnoreHealthArmor = false, bool bIgnoreShieldArmor = false);
void HealthDepleted(float Damage, FVector Force, FVector HitLocation, FName HitBoneName);
public:
UPROPERTY(EditAnywhere, meta = (Category="Health"))
float Health = 100;
UPROPERTY(EditAnywhere, meta = (Category="Health"))
float MaxHealth = 100;
UPROPERTY(EditAnywhere, meta = (Category="Health"))
float MaxHealthRegenAmount = 0;
UPROPERTY(EditAnywhere, meta = (Category="Health"))
float HealthRegenDelay = 0;
UPROPERTY(EditAnywhere, meta = (Category="Health"))
float HealthRegenPerSec = 1;
UPROPERTY(EditAnywhere, meta = (Category="Health"))
float HealthArmor = 0;
UPROPERTY(EditAnywhere, meta = (Category="Health"))
float MaxHealthArmor = 100;
UPROPERTY(EditAnywhere, meta = (Category="Shields"))
float Shields = 100;
UPROPERTY(EditAnywhere, meta = (Category="Shields"))
float MaxShields = 100;
UPROPERTY(EditAnywhere, meta = (Category="Shields"))
float ShieldRegenDelay = 3;
UPROPERTY(EditAnywhere, meta = (Category="Shields"))
float ShieldRegenRatePerSecond = 30;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
USoundBase* ShieldBreakSFX;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
USoundBase* ShieldStartRegenSFX;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
USoundBase* ShieldRegenSFX;
UPROPERTY(VisibleAnywhere, BlueprintAssignable)
FOnHealthDepleted OnHealthDepleted;
UPROPERTY(VisibleAnywhere, BlueprintAssignable)
FOnHealthUpdate OnHealthUpdate;
float HealthRegenTickRate = 0.01;
float ShieldRegenTickRate = 0.01;
private:
UPROPERTY()
FTimerHandle ShieldDelayTimerHandle;
public:
UFUNCTION(BlueprintCallable)
float GetHealth() const;
UFUNCTION(BlueprintCallable)
void SetHealth(float NewHealth);
UFUNCTION(BlueprintCallable)
float GetMaxHealth() const;
UFUNCTION(BlueprintCallable)
void SetMaxHealth(float NewMaxHealth);
UFUNCTION(BlueprintCallable)
float GetHealthArmor() const;
UFUNCTION(BlueprintCallable)
void SetHealthArmor(float NewHealthArmor);
UFUNCTION(BlueprintCallable)
float GetMaxHealthArmor() const;
UFUNCTION(BlueprintCallable)
void SetMaxHealthArmor(float NewMaxHealthArmor);
UFUNCTION(BlueprintCallable)
float GetShields() const;
UFUNCTION(BlueprintCallable)
float SetShields(float NewShields);
UFUNCTION(BlueprintCallable)
float GetMaxShields() const;
UFUNCTION(BlueprintCallable)
void SetMaxShields(float NewMaxShields);
UFUNCTION(BlueprintCallable)
float GetShieldRegenDelay() const;
UFUNCTION(BlueprintCallable)
void SetShieldRegenDelay(float NewShieldRegenDelay);
float GetShieldRegenRatePerSecond() const;
UFUNCTION(BlueprintCallable)
void SetShieldRegenRatePerSecond(float NewShieldRegenRatePerSecond);
UFUNCTION()
void BreakShields();
UFUNCTION()
void RegenShields();
};
HealthComponent.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "HealthComponent.h"
#include "TimerManager.h"
#include "GameFramework/Actor.h"
#include "Kismet/GameplayStatics.h"
// Sets default values for this component's properties
UHealthComponent::UHealthComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
// Called when the game starts
void UHealthComponent::BeginPlay()
{
Super::BeginPlay();
// ...
}
// Called every frame
void UHealthComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
float UHealthComponent::TakeDamage(float Damage, FVector Force, FVector HitLocation, FName HitBoneName, AController* EventInstigator, AActor* DamageCauser, bool bIgnoreShields, bool bIgnoreHealthArmor, bool bIgnoreShieldArmor)
{
float DamageLeft = Damage;
UE_LOG(LogTemp, Warning, TEXT("%f"), Health);
//if (MaxShields > 0 && GetWorld()) GetOwner()->GetWorldTimerManager();
if (Shields > 0)
{
DamageLeft = Damage - Shields;
// if (ShieldMat)
// {
// for (auto Mesh : MeshArray)
// {
// Mesh->OverlayMaterial = ShieldMat;
// UMaterialInstanceDynamic* ShieldMatDynamic = UMaterialInstanceDynamic::Create(ShieldMat, this->GetOwner());
// ShieldMatDynamic->SetScalarParameterValue(FName("Intensity"), 50 * (Shields/MaxShields));
// }
// }
if (SetShields(Shields - Damage) <= 0)
{
BreakShields();
}
}
if (Shields <= 0 && Health > 0)
{
//Health -= DamageLeft;
SetHealth(Health - DamageLeft);
if (Health <= 0)
{
this->HealthDepleted(Damage, Force, HitLocation, HitBoneName);
}
}
OnHealthUpdate.Broadcast(this);
return Damage;
}
void UHealthComponent::HealthDepleted(float Damage, FVector Force, FVector HitLocation, FName HitBoneName)
{
OnHealthDepleted.Broadcast(Damage, Force, HitLocation, HitBoneName);
}
float UHealthComponent::GetHealth() const
{
return Health;
}
void UHealthComponent::SetHealth(float NewHealth)
{
this->Health = FMath::Clamp(NewHealth, 0, MaxHealth);
}
float UHealthComponent::GetMaxHealth() const
{
return MaxHealth;
}
void UHealthComponent::SetMaxHealth(float NewMaxHealth)
{
this->MaxHealth = NewMaxHealth;
}
float UHealthComponent::GetHealthArmor() const
{
return HealthArmor;
}
void UHealthComponent::SetHealthArmor(float NewHealthArmor)
{
this->HealthArmor = NewHealthArmor;
}
float UHealthComponent::GetMaxHealthArmor() const
{
return MaxHealthArmor;
}
void UHealthComponent::SetMaxHealthArmor(float NewMaxHealthArmor)
{
this->MaxHealthArmor = NewMaxHealthArmor;
}
float UHealthComponent::GetShields() const
{
return Shields;
}
float UHealthComponent::SetShields(float NewShields)
{
return this->Shields = FMath::Clamp(NewShields, 0, MaxShields);
}
float UHealthComponent::GetMaxShields() const
{
return MaxShields;
}
void UHealthComponent::SetMaxShields(float NewMaxShields)
{
this->MaxShields = NewMaxShields;
}
float UHealthComponent::GetShieldRegenDelay() const
{
return ShieldRegenDelay;
}
void UHealthComponent::SetShieldRegenDelay(float NewShieldRegenDelay)
{
this->ShieldRegenDelay = NewShieldRegenDelay;
}
float UHealthComponent::GetShieldRegenRatePerSecond() const
{
return ShieldRegenRatePerSecond;
}
void UHealthComponent::SetShieldRegenRatePerSecond(float NewShieldRegenRatePerSecond)
{
this->ShieldRegenRatePerSecond = NewShieldRegenRatePerSecond;
}
void UHealthComponent::BreakShields()
{
if (ShieldBreakSFX) UGameplayStatics::SpawnSoundAttached(ShieldBreakSFX, GetOwner()->GetRootComponent());
}
void UHealthComponent::RegenShields()
{
OnHealthUpdate.Broadcast(this);
if (ShieldStartRegenSFX) UGameplayStatics::SpawnSoundAttached(ShieldStartRegenSFX, GetOwner()->GetRootComponent());
float ShieldRegenAmount = ShieldRegenRatePerSecond*ShieldRegenTickRate;
SetShields(FMath::Min(MaxShields, Shields + ShieldRegenAmount));
if (Shields >= MaxShields) GetOwner()->GetWorldTimerManager().ClearTimer(ShieldDelayTimerHandle);
}