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"http:// 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"http:// 自定義六參數代理事件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);}

推薦閱讀