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

首先基于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 propertiesUHealthComponent();// 初始化健康值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 startsvirtual void BeginPlay() override;public:// Called every framevirtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;};这里的 HanldTakeAnyDamaged 函数是通过代理绑定到拥有者身上
HanldTakeAnyDamaged 需要的形参需要与 OnTakeAnyDamage 的宏定义一致
除此之外还有OnTakePointDamage 和 OnTakeRadialDamage 也是一样的操作
#include "Components/HealthComponent.h"#include "Engine.h"#include "Kismet/KismetSystemLibrary.h"// Sets default values for this component's propertiesUHealthComponent::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 startsvoid 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 framevoid 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);}

推荐阅读