Implemented hitboxes and health system for ennemies

This commit is contained in:
TjgL 2025-01-24 13:55:58 +01:00
parent 8a385a3f86
commit 879fb83f0e
5 changed files with 263 additions and 0 deletions

View File

@ -0,0 +1,62 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Player/LMHealthComponent.h"
#include "Player/LMHitBox.h"
ULMHealthComponent::ULMHealthComponent()
{
PrimaryComponentTick.bCanEverTick = true;
}
void ULMHealthComponent::BeginPlay()
{
Super::BeginPlay();
if (bMaxHealthOnStart)
Health = MaxHealth;
RegisterHitBoxes();
}
void ULMHealthComponent::RegisterHitBoxes()
{
GetOwner()->GetComponents<ULMHitBox>(HitBoxes);
for (const auto HitBox : HitBoxes)
{
HitBox->OnHitBoxHit.AddUniqueDynamic(this, &ULMHealthComponent::HitBoxHit);
}
}
void ULMHealthComponent::TakeDamage_Implementation(const float Damage)
{
if (Damage <= 0.0f)
return;
OnHit(Damage);
OnHealthChanged.Broadcast(Health, Damage);
if (Health <= 0.0f)
Kill();
}
void ULMHealthComponent::OnHit_Implementation(const float Damage)
{
Health = FMath::Clamp(Health - Damage, 0.0f, MaxHealth);
}
void ULMHealthComponent::Kill()
{
OnKill();
OnDeath.Broadcast();
}
void ULMHealthComponent::HitBoxHit(ULMHitBox* HitBox, float Damage)
{
TakeDamage(Damage);
}

View File

@ -0,0 +1,38 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Player/LMHitBox.h"
#include "Player/LMBulletInfo.h"
ULMHitBox::ULMHitBox()
{
PrimaryComponentTick.bCanEverTick = true;
}
void ULMHitBox::OnHit(const FLMBulletInfo& BulletInfo)
{
const float Damage = BulletInfo.Damage;
const double Distance = FVector::Distance(BulletInfo.Origin, GetComponentLocation());
const float TotalDamage = CalculateDamage(Damage, Distance, BulletInfo.Falloff, BulletInfo.MaxDistance);
OnHitBoxHit.Broadcast(this, TotalDamage);
}
float ULMHitBox::CalculateDamage_Implementation(const float Damage, const float Distance, UCurveFloat* Falloff, const float MaxDistance)
{
const float Absorption = Damage - FlatDamageAbsorption;
const float FalloffModifier = Falloff->GetFloatValue(Distance / MaxDistance);
UE_LOG(LogTemp, Display, TEXT("Falloff : %f"), FalloffModifier);
const float FalloffDamage = Absorption * FalloffModifier;
UE_LOG(LogTemp, Display, TEXT("Damage With Fallof: %f"), FalloffDamage)
const float FinalDamage = FalloffDamage * DamageModifier;
UE_LOG(LogTemp, Display, TEXT("Final Damages: %f"), FinalDamage)
return FinalDamage;
}

View File

@ -7,6 +7,7 @@
#include "Camera/CameraComponent.h" #include "Camera/CameraComponent.h"
#include "Ammo/LMAmmo.h" #include "Ammo/LMAmmo.h"
#include "Player/LMBulletInfo.h" #include "Player/LMBulletInfo.h"
#include "Player/LMHitBox.h"
#include "Weapon/LMWeaponManager.h" #include "Weapon/LMWeaponManager.h"
ALMPlayer::ALMPlayer() ALMPlayer::ALMPlayer()
@ -84,6 +85,19 @@ void ALMPlayer::FireBullets(const FLMBulletInfo Settings)
const bool HasHit = GetWorld()->LineTraceSingleByChannel(OutHit, Settings.Origin, EndLocation, ECC_Camera); const bool HasHit = GetWorld()->LineTraceSingleByChannel(OutHit, Settings.Origin, EndLocation, ECC_Camera);
DrawDebugLineTraceSingle(GetWorld(), Settings.Origin, EndLocation, EDrawDebugTrace::ForDuration, HasHit, OutHit, FColor::Green, FColor::Red, 2.f); DrawDebugLineTraceSingle(GetWorld(), Settings.Origin, EndLocation, EDrawDebugTrace::ForDuration, HasHit, OutHit, FColor::Green, FColor::Red, 2.f);
if (!HasHit)
continue;
FString Hit = FString::Printf(TEXT("Hit %s"), *OutHit.Component->GetName());
// GEngine->AddOnScreenDebugMessage(INDEX_NONE, 2.f, FColor::Red, Hit);
UE_LOG(LogTemp, Display, TEXT("%s"), *Hit);
if (ULMHitBox* HitBox = Cast<ULMHitBox>(OutHit.Component))
{
UE_LOG(LogTemp, Warning, TEXT("Hit Hitbox"));
HitBox->OnHit(Settings);
}
} }
} }

View File

@ -0,0 +1,103 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "LMHealthComponent.generated.h"
class ULMHitBox;
UCLASS(Blueprintable, ClassGroup=(Legumix), meta=(BlueprintSpawnableComponent))
class LEGUMEMIX_API ULMHealthComponent : public UActorComponent
{
GENERATED_BODY()
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnHealthChangedSignature, float, Health, float, Damage);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeathSignature);
public:
ULMHealthComponent();
virtual void BeginPlay() override;
void Kill();
public:
/**
* Applies an amount of damage to the component.
* @param Damage The damage to apply the Health to.
*/
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category=Legumix)
void TakeDamage(float Damage);
/**
* Called automatically when the component hits zero health.
*/
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category=Legumix)
void OnKill();
/**
* Register all hitboxes associated with this component owner.
*/
UFUNCTION(BlueprintCallable, Category=Legumix, CallInEditor)
void RegisterHitBoxes();
/**
* Called automatically when a hitbox is hit.
* @param HitBox The hitbox that was hit.
* @param Damage The damage received by the hitbox.
*/
UFUNCTION()
void HitBoxHit(ULMHitBox* HitBox, float Damage);
/**
* Applies the amount of damage received to the component.
*
* Called automatically when one of this component's LMHitBox is hit.
* @param Damage The damage received.
*/
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category=Legumix)
void OnHit(float Damage);
/**
* Adds an amount of health to the character.
* @param AddedHealth The amount of health to add.
* @param NoLimits If sets, ignore the component's MaxHealth value.
*/
UFUNCTION(BlueprintCallable, Category=Legumix, meta=(Keywords = "Refill, Health"))
void AddHealth(const float AddedHealth, const bool NoLimits = false) { Health = FMath::Clamp(Health + AddedHealth, 0.0f, NoLimits ? INT_MAX : MaxHealth); }
/**
* Completely refill the health of the component.
*/
UFUNCTION(BlueprintCallable, Category=Legumix, meta=(Keywords = "Refill, Health"))
void FillHealth() { Health = MaxHealth; }
public:
/** Delegate called when this component receives damages. */
UPROPERTY(BlueprintAssignable, Category=Legumix)
FOnHealthChangedSignature OnHealthChanged;
/** Delegate called when this component is killed, aka. Health reaches 0.*/
UPROPERTY(BlueprintAssignable, Category=Legumix)
FOnDeathSignature OnDeath;
private:
/**
* If set, the Health will be initialized to MaxHealth.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Legumix, meta=(AllowPrivateAccess=true))
bool bMaxHealthOnStart = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Legumix, meta=(AllowPrivateAccess=true, ClampMin=0, UIMin=0))
float Health = 100.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Legumix, meta=(AllowPrivateAccess=true, ClampMin=0, UIMin=0, UIMax=1000))
float MaxHealth = 100.f;
/**
* A list of all associated LMHitboxes.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Legumix, meta=(AllowPrivateAccess=true))
TArray<TObjectPtr<ULMHitBox>> HitBoxes;
};

View File

@ -0,0 +1,46 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "LMBulletInfo.h"
#include "Components/CapsuleComponent.h"
#include "LMHitBox.generated.h"
UCLASS(Blueprintable, ClassGroup=(Legumix), meta=(BlueprintSpawnableComponent))
class LEGUMEMIX_API ULMHitBox : public UCapsuleComponent
{
GENERATED_BODY()
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FHitBoxHitSignature, ULMHitBox*, HitBox, float, Damage);
public:
ULMHitBox();
void OnHit(const FLMBulletInfo& BulletInfo);
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category=Legumix)
float CalculateDamage(float Damage, float Distance, UCurveFloat* Falloff, float MaxDistance);
public:
/** Delegate called when the hitbox is hit.
* @param HitBox: The Hitbox that was hit.
* @param Damage: The amount of damage done to the hitbox.
*/
UPROPERTY(BlueprintAssignable, BlueprintCallable)
FHitBoxHitSignature OnHitBoxHit;
private:
/**
* A value to multiply the damage received by.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Legumix, meta=(AllowPrivateAccess=true))
float DamageModifier = 1.0f;
/**
* The Amount of damage removed when hit.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Legumix, meta=(AllowPrivateAccess=true, ClampMin=0.0f, UIMin=0.0f))
float FlatDamageAbsorption = 0.f;
};