[UE4]事件处理(Handling Events)和委托(Delegate)代码示例(一)

1. 通过重写虚函数来处理事件

MyTriggerVolume.h

自定义一个Actor类,添加一个 Box 组件作为触发区域,然后通过重写虚函数——NotifyActorBeginOverlap, NotifyActorEndOverlap来响应事件
#pragma once  
  
#include "GameFramework/Actor.h"  
#include "MyTriggerVolume.generated.h"  
  
UCLASS()  
class TEST_API AMyTriggerVolume : public AActor  
{  
    GENERATED_BODY()  
      
public:   
    // Sets default values for this actor's properties  
    AMyTriggerVolume();  
  
    // Called when the game starts or when spawned  
    virtual void BeginPlay() override;  
      
    // Called every frame  
    virtual void Tick( float DeltaSeconds ) override;  
  
    UPROPERTY()  
    UBoxComponent* TriggerZone;  
      
    UFUNCTION()  
    virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;  
    UFUNCTION()  
    virtual void NotifyActorEndOverlap(AActor* OtherActor) override;  
};  

MyTriggerVolume.cpp

#include "Test.h"  
#include "UE4TestGameMode.h"  
#include "MyTriggerVolume.h"  
  
  
// Sets default values  
AMyTriggerVolume::AMyTriggerVolume()  
{  
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
    PrimaryActorTick.bCanEverTick = true;  
    TriggerZone = CreateDefaultSubobject<UBoxComponent>("TriggerZone");  
    TriggerZone->SetBoxExtent(FVector(200, 200, 100));// 设置触发区域的范围  
}  
  
// Called when the game starts or when spawned  
void AMyTriggerVolume::BeginPlay()  
{  
    Super::BeginPlay();  
      
}  
  
// Called every frame  
void AMyTriggerVolume::Tick( float DeltaTime )  
{  
    Super::Tick( DeltaTime );  
  
}  
  
// 重写虚函数来响应事件  
void AMyTriggerVolume::NotifyActorBeginOverlap(AActor* OtherActor)  
{  
    GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, FString::Printf(TEXT("%s entered me"), *(OtherActor->GetName())));// 注意FString::Format需要解引用  
}  
// 重写虚函数来响应事件  
void AMyTriggerVolume::NotifyActorEndOverlap(AActor* OtherActor)  
{  
    GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Red, FString::Printf(TEXT("%s left me"), *(OtherActor->GetName())));  
}  

2. 绑定在 UFUNCTION 函数上的委托(不带参数)

委托的好处在于,我们不用知道当前指派的函数的细节就可以调用它,它是一种安全版本的函数指针。

 

以下代码将展示如何关联 UFUNCTION 到一个委托上,即委托执行时,UFUNCTION 将被调用。
(效果为 当玩家进入触发区域,点光源亮)

 

首先在 UE4TestGameMode.h 中添加一个委托声明(在 UCLASS 之前),如下:

DECLARE_DELEGATE(FStandardDelegateSignature)  

然后,为 UE4TestGameMode 类添加一个新成员

FStandardDelegateSignature MyStandardDelegate;  

接着,我们新建一个 Actor 类——DelegateListener,主要实现具体方法,以及负责委托的绑定和解绑 

DelegateListener.h

UCLASS()  
class TEST_API ADelegateListener : public AActor  
{  
    GENERATED_BODY()  
      
public:   
    // Sets default values for this actor's properties  
    ADelegateListener();  
  
    // Called when the game starts or when spawned  
    virtual void BeginPlay() override;  
      
    // Called every frame  
    virtual void Tick( float DeltaSeconds ) override;  
      
    UFUNCTION()  
    void EnableLight();  
  
    UFUNCTION()  
    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;  
  
    UPROPERTY()  
    UPointLightComponent* PointLight;  
      
};  

DelegateListener.cpp

#include "Test.h"  
#include "UE4TestGameMode.h" // 注意 include 的位置  
#include "DelegateListener.h"  
  
  
// Sets default values  
ADelegateListener::ADelegateListener()  
{  
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
    PrimaryActorTick.bCanEverTick = true;  
    PointLight = CreateDefaultSubobject<UPointLightComponent>("PointLight");  
    RootComponent = PointLight;  
      
    PointLight->SetVisibility(false);  
}  
  
// Called when the game starts or when spawned  
void ADelegateListener::BeginPlay()  
{  
    Super::BeginPlay();  
    UWorld* TheWorld = GetWorld();  
    if (TheWorld != nullptr)  
    {  
        AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));  
        AUE4TestGameMode * MyGameMode = Cast<AUE4TestkGameMode>(GameMode);  
        if (MyGameMode != nullptr)  
        {  
            // ❤ 绑定一个基于 UObject 的成员函数的委托。UObject 委托保留了一个弱引用在你的对象上,可以通过.ExecuteIfBound() 来调用委托的函数  
            MyGameMode->MyStandardDelegate.BindUObject(this, &ADelegateListener::EnableLight);// 其实就是将 EnableLight 函数绑定在了委托上。  
                 }  
    }  
  
}  
  
// Called every frame  
void ADelegateListener::Tick( float DeltaTime )  
{  
    Super::Tick( DeltaTime );  
  
}  
  
void ADelegateListener::EnableLight()  
{  
    PointLight->SetVisibility(true);  
}  
  
void ADelegateListener::EndPlay(const EEndPlayReason::Type EndPlayReason)  
{  
    Super::EndPlay(EndPlayReason);  
    UWorld* TheWorld = GetWorld();  
    if (TheWorld != nullptr)  
    {  
        AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));  
        AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);  
        if (MyGameMode != nullptr)  
        {  
            // 解绑委托  
            MyGameMode->MyStandardDelegate.Unbind();  
        }  
    }  
}  

值得注意的是,如果我们绑定的是普通的C++函数,那么就应该将 BindUObject 改为 BindRaw,如果是静态方法,那就改为 BindStatic。

 

最后,回到我们之前的 MyTriggerVolume.cpp, 利用 GameMode(我们之前声明委托和定义委托成员的地方) 执行委托,

在 NotifyActorBeginOverlap 方法中添加以下代码:

UWorld* TheWorld = GetWorld();  
if (TheWorld != nullptr)  
{  
    AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));  
    AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);  
    // ❤ 执行委托的函数  
    MyGameMode->MyStandardDelegate.ExecuteIfBound();  
}  

 

posted on 2018-03-31 16:18  一粒沙  阅读(3262)  评论(0编辑  收藏  举报