1. 程式人生 > 實用技巧 >基於C++程式碼的UE4學習(五)—— 帶一個引數的FParamDelegateSignature動態代理與函式指標

基於C++程式碼的UE4學習(五)—— 帶一個引數的FParamDelegateSignature動態代理與函式指標

之前我們使用FStandardDeltegateSignature類進行了無引數的函式繫結,也有人告訴我說叫做觀察者模式。

今天我們先使用函式指標完成FStandardDeltegateSignature類的功能。

以下是繼承自Actor類的PointLightListner類的標頭檔案程式碼:

 1 #pragma once
 2 
 3 #include "CoreMinimal.h"
 4 #include "GameFramework/Actor.h"
 5 #include "Components\PointLightComponent.h"
 6 #include "PointLightListner.generated.h
" 7 8 UCLASS() 9 class MYPROJECT6_API APointLightListner : public AActor 10 { 11 GENERATED_BODY() 12 13 public: 14 // Sets default values for this actor's properties 15 APointLightListner(); 16 17 protected: 18 // Called when the game starts or when spawned 19 virtual void
BeginPlay() override; 20 21 virtual void EndPlay(EEndPlayReason::Type EndReason) override; 22 23 public: 24 // Called every frame 25 virtual void Tick(float DeltaTime) override; 26 27 28 29 public: 30 31 UPROPERTY() 32 class UPointLightComponent* pointLight; 33 34 UFUNCTION()
35 void enableLight(); 36 37 UFUNCTION() 38 void closeLight(); 39 40 UFUNCTION() 41 void SelfLight(FLinearColor color); 42 };

以下是繼承自Actor類的PointLightListner類的原始檔程式碼:

其中註釋起來的程式碼是之前用作FStandardDelegateSignature類內容設計的,這裡可以暫時忽略。

 1 // Fill out your copyright notice in the Description page of Project Settings.
 2 
 3 
 4 #include "PointLightListner.h"
 5 #include "Kismet/GameplayStatics.h"
 6 #include "MyProject6GameModeBase.h"
 7 
 8 // Sets default values
 9 APointLightListner::APointLightListner()
10 {
11      // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
12     PrimaryActorTick.bCanEverTick = true;
13     pointLight = CreateDefaultSubobject<UPointLightComponent>(TEXT("pointLight"));
14     pointLight->SetVisibility(true);
15     pointLight->SetLightColor(FLinearColor::Blue);
16 }
17 
18 // Called when the game starts or when spawned
19 void APointLightListner::BeginPlay()
20 {
21     Super::BeginPlay();
22     /*
23     auto ParentGameMode = UGameplayStatics::GetGameMode(GetWorld());
24 
25     AMyProject6GameModeBase* myGameModeBase = Cast<AMyProject6GameModeBase>(ParentGameMode);
26 
27     if (myGameModeBase) {
28         //myGameModeBase->myDelegate1.BindUObject(this,&APointLightListner::enableLight);
29         myGameModeBase->myDelegate2.BindUObject(this, &APointLightListner::closeLight);
30         myGameModeBase->myOneParamDelegate.BindUObject(this, &APointLightListner::SelfLight);
31     }
32     */
33     
34 }
35 
36 void APointLightListner::EndPlay(EEndPlayReason::Type EndReason)
37 {
38 
39     Super::EndPlay(EndReason);
40     /*
41     auto ParentGameMode = UGameplayStatics::GetGameMode(GetWorld());
42 
43     AMyProject6GameModeBase* myGameModeBase = Cast<AMyProject6GameModeBase>(ParentGameMode);
44 
45     if (myGameModeBase) {
46         //myGameModeBase->myDelegate1.Unbind();
47         myGameModeBase->myDelegate2.Unbind();
48         myGameModeBase->myOneParamDelegate.Unbind();
49     }
50     */
51 
52 }
53 
54 // Called every frame
55 void APointLightListner::Tick(float DeltaTime)
56 {
57     Super::Tick(DeltaTime);
58 
59 }
60 
61 
62 void APointLightListner::enableLight() {
63     pointLight->SetLightColor(FLinearColor::Red);
64 }
65 
66 
67 void APointLightListner::closeLight() {
68     pointLight->SetLightColor(FLinearColor::Blue);
69 }
70 
71 void APointLightListner::SelfLight(FLinearColor color)
72 {
73     pointLight->SetLightColor(color);
74 }

實現方是一個燈元件及其開關的方法及顏色。現在要實現觸發方。

以下是繼承自Actor類的TriggerS類的標頭檔案程式碼:

它匯入了PointLightListner類的標頭檔案,因為要訪問它類中的方法。

再建立一個Box的碰撞體。

 1 // Fill out your copyright notice in the Description page of Project Settings.
 2 
 3 #pragma once
 4 
 5 #include "CoreMinimal.h"
 6 #include "GameFramework/Actor.h"
 7 #include "PointLightListner.h"
 8 #include "Components\BoxComponent.h"
 9 #include "TriggerS.generated.h"
10 
11 
12 UCLASS()
13 class MYPROJECT6_API ATriggerS : public AActor
14 {
15     GENERATED_BODY()
16     
17 public:    
18     // Sets default values for this actor's properties
19     ATriggerS();
20 
21 protected:
22     // Called when the game starts or when spawned
23     virtual void BeginPlay() override;
24 
25 public:    
26     // Called every frame
27     virtual void Tick(float DeltaTime) override;
28 
29 public:
30     UFUNCTION(BlueprintCallable)
31         virtual void NotifyActorBeginOverlap(AActor* actor) override;
32 
33     UFUNCTION(BlueprintCallable)
34         virtual void NotifyActorEndOverlap(AActor* actor) override;
35     UPROPERTY()
36     class UBoxComponent* box;
37 
38 };

 1 // Fill out your copyright notice in the Description page of Project Settings.
 2 
 3 
 4 #include "TriggerS.h"
 5 #include "Kismet/GameplayStatics.h"
 6 
 7 // Sets default values
 8 ATriggerS::ATriggerS()
 9 {
10      // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
11     PrimaryActorTick.bCanEverTick = true;
12     box = CreateDefaultSubobject<UBoxComponent>(TEXT("box"));
13     RootComponent = box;
14     box->InitBoxExtent(FVector(80.0f));
15 }
16 
17 // Called when the game starts or when spawned
18 void ATriggerS::BeginPlay()
19 {
20     Super::BeginPlay();
21     
22 }
23 
24 // Called every frame
25 void ATriggerS::Tick(float DeltaTime)
26 {
27     Super::Tick(DeltaTime);
28 
29 }
30 
31 void ATriggerS::NotifyActorBeginOverlap(AActor* actor)
32 {
33     void (APointLightListner:: * PL)();
34     PL = &APointLightListner::enableLight;
35 
36 
37     TArray<AActor*> alist;
38 
39     UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass(), alist);
40 
41     for (int i = 0; i < alist.Num(); i++) {
42         APointLightListner * pll= Cast<APointLightListner>(alist[i]);
43         if (pll) {
44             (pll->*PL)();
45         }
46     }
47 }
48 
49 void ATriggerS::NotifyActorEndOverlap(AActor* actor)
50 {
51 
52     void (APointLightListner:: * PL)();
53     PL = &APointLightListner::closeLight;
54 
55 
56     TArray<AActor*> alist;
57 
58     UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass(), alist);
59 
60     for (int i = 0; i < alist.Num(); i++) {
61         APointLightListner* pll = Cast<APointLightListner>(alist[i]);
62         if (pll) {
63             (pll->*PL)();
64         }
65     }
66 }

在NotifyActorBeginOverlap和NotifyActorEndOverlap方法中建立陣列和PointLightListner類的函式指標,指向類中的方法。

效果如下,可以達到同樣效果。

接下來看帶有一個引數的FParamDelegateSignature類代理,其使用方法與FStandardDelegateSignature類幾乎完全一樣,除了兩個地方,在宣告階段是這樣的。

1 DECLARE_DELEGATE_OneParam(FParamDelegateSignature,FLinearColor)

宣告帶引數的代理,用DECLARE_DELEGATE_OneParam()巨集定義,我試了下,可以一直寫到DECLARE_DELEGATE_NineParam(),意思就是可以繫結帶有9個引數的方法。

巨集定義的第一個引數中傳入代理型別,這裡就是FParamDelegateSignature類,第二個引數是要繫結的方法需要傳入的資料型別,這裡需要傳入的是一個FLinearColor型別。

第二個地方不同的是,在FStandardDelegateSignature類物件將類中的繫結好的方法進行執行的時候,用到的是ExcuteIfBound方法,而FParamDelegateSignature類物件執行繫結好的方法的時候,使用的是Excute方法。

FParamDelegateSignature類其他例如繫結方法,解綁方法,與FStandardDelegateSignature類都是相同的。

BindUObject()

Unbind();