UEC 利用代理/委托写一个生命组件

2022-12-13,,,,

首先基于ActorComponent创建一个组件 HealthComponent,将需要的变量与函数创建

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "HealthComponent.generated.h" UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class PVETPC_API UHealthComponent : public UActorComponent
{
GENERATED_BODY() public:
// Sets default values for this component's properties
UHealthComponent();
// 初始化健康值
UFUNCTION(BlueprintCallable)
void Init(int taotalHealth,int currentHealth);
// 造成伤害
UFUNCTION(BlueprintCallable)
void HanldTakeAnyDamaged(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
// 恢复健康值
UFUNCTION(BlueprintCallable)
void RestoreHealth(int restoreValue);
UFUNCTION(BlueprintPure)
float GetHealth() { return CurrentHealth; }
protected:
// 总健康值
float TotalHealth;
// 当前健康值
float CurrentHealth; // Called when the game starts
virtual void BeginPlay() override; public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};

这里的 HanldTakeAnyDamaged 函数是通过代理绑定到拥有者身上

HanldTakeAnyDamaged 需要的形参需要与 OnTakeAnyDamage 的宏定义一致

除此之外还有OnTakePointDamageOnTakeRadialDamage 也是一样的操作

#include "Components/HealthComponent.h"
#include "Engine.h"
#include "Kismet/KismetSystemLibrary.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; // ...
} void UHealthComponent::Init(int taotalHealth, int currentHealth)
{
TotalHealth = taotalHealth;
CurrentHealth = currentHealth;
} void UHealthComponent::HanldTakeAnyDamaged(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if (Damage <= 0) { return; }
CurrentHealth = FMath::Clamp( CurrentHealth - Damage , 0.f, TotalHealth);
UE_LOG(LogTemp, Warning, TEXT("I am Demaged! CurrentHealth = %f"), CurrentHealth);
} void UHealthComponent::RestoreHealth(int restoreValue)
{
CurrentHealth = FMath::Clamp(CurrentHealth + restoreValue, 0.f, TotalHealth);
GEngine->AddOnScreenDebugMessage(-1, 20, FColor::Red, FString(TEXT("I am RestoreHealth!")));
} // Called when the game starts
void UHealthComponent::BeginPlay()
{
Super::BeginPlay();
// 获取拥有者
AActor* MyOwner = GetOwner();
// 如果存在就将伤害接收函数绑定
if (MyOwner)
{
UE_LOG(LogTemp, Warning, TEXT("I am bound!"));
MyOwner->OnTakeAnyDamage.AddDynamic(this, &UHealthComponent::HanldTakeAnyDamaged);
}
Init(100,100);
// ... } // Called every frame
void UHealthComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction); // ...
}

这时候我们将该组件挂载在角色身上,已经有了效果,但是角色不知道组件生命值是否改变

接着我们在组件头文件的头文件申明下添加代理的宏定义,并创建一个代理对象

并在需要响应的函数中添加广播

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "HealthComponent.generated.h" // 自定义六参数代理事件
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, UHealthComponent*, HealthComp, float, Health, float, HealthDelta, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);
......
// 恢复健康值
UFUNCTION(BlueprintCallable)
void RestoreHealth(int restoreValue);
UFUNCTION(BlueprintPure)
float GetHealth() { return CurrentHealth; }
// 定义代理
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnHealthChangedSignature OnHealthChanged; ......
void UHealthComponent::HanldTakeAnyDamaged(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if (Damage <= 0) { return; }
CurrentHealth = FMath::Clamp( CurrentHealth - Damage , 0.f, TotalHealth);
UE_LOG(LogTemp, Warning, TEXT("I am Demaged! CurrentHealth = %f"), CurrentHealth);
// 每当该函数被调用时,就将调用一次代理函数
OnHealthChanged.Broadcast(this, CurrentHealth, Damage, DamageType, InstigatedBy, DamageCauser);
}

最后再到拥有者类中添加一个用于回调的操作函数,其中形参对应在生命组件中定义的那样(注意命名是否重复)

头文件

    // 代理事件
UFUNCTION()
void OnHealthChanged(UHealthComponent* OnwerHealthComp, float Health, float HealthDelta,
const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);

cpp文件

void APCharacter::OnHealthChanged(UHealthComponent* OnwerHealthComp, float Health, float HealthDelta, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if (IsDeath) return;
UE_LOG(LogTemp, Warning, TEXT("I know I was hurt! "));
if (Health <= 0 && !IsDeath)
{
UE_LOG(LogTemp, Warning, TEXT("I am Death! "));
IsDeath = true;
     Death();
GetMovementComponent()->StopMovementImmediately();
GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision); // 分离控制器
DetachFromControllerPendingDestroy();
// 3秒后执行
SetLifeSpan(3.0f);
}
}
void APCharacter::BeginPlay()
{
Super::BeginPlay();
HealthComp->OnHealthChanged.AddDynamic(this, &APCharacter::OnHealthChanged); }

最后测试,结果无误

UEC 利用代理/委托写一个生命组件的相关教程结束。

《UEC 利用代理/委托写一个生命组件.doc》

下载本文的Word格式文档,以方便收藏与打印。