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