首先基於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 的宏定義一致
除此之外還有OnTakePointDamage 和 OnTakeRadialDamage 也是一樣的操作
#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); }
最後測試,結果無誤