5

UEC 利用代理/委托写一个生命组件 - 黎沐不吃香菜

 1 year ago
source link: https://www.cnblogs.com/limu-zy/p/16838857.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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 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); }

最后测试,结果无误

2238312-20221029153150038-1288638443.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK