【UC++】委托

委托

委托是一种用于事件处理的机制。使用委托,可以将一个或多个函数绑定到一个事件上,在事件触发时,自动调用这些函数。其作用就是提供一种消息机制,代理调用方法,而不用持有原对象指针。

种类

  • 单播委托 : 只能绑定一个函数
  • 多播委托 : 可以绑定多个函数,事件触发时,按照绑定顺序执行
  • 动态委托 :可以在运行时动态绑定或解绑函数

例子

1. 单播委托带参数

定义单播单参数委托(DECLARE_DELEGATE_OneParam) FCharacterHealthChangedDelegate

Test3Character.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Logging/LogMacros.h"
#include "test3Character.generated.h"

class USpringArmComponent;
class UCameraComponent;
class UInputMappingContext;
class UInputAction;
struct FInputActionValue;

DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);

DECLARE_DELEGATE_OneParam(FCharacterHealthChangedDelegate, float);


UCLASS(config=Game)
class ATest3Character : public ACharacter
{
GENERATED_BODY()

public:

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
float Health;

FCharacterHealthChangedDelegate CharacterHealthChangedDelegate;

public:
ATest3Character();

UFUNCTION(BlueprintCallable)
void UpdateHealth(const float _Health);

};


UpdateHealth 方法更新Health值,并触发委托通知

Test3Character.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
DEFINE_LOG_CATEGORY(LogTemplateCharacter);



void ATest3Character::UpdateHealth(const float _Health)
{
this->Health = _Health;
if (CharacterHealthChangedDelegate.ExecuteIfBound(_Health))
{
UE_LOG(LogTemp, Display, TEXT("Character health changed"));
}
}

新建类继承UTextRenderComponent组件

UUHealthTextObserver.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UCLASS(BlueprintType, Blueprintable, meta=(BlueprintSpawnableComponent))
class TEST3_API UUHealthTextObserver : public UTextRenderComponent
{
GENERATED_BODY()
public:
FCharacterHealthChangedDelegate CharacterHealthChangedDelegate;

UUHealthTextObserver();

void CharacterHealthChanged(float Health);

UFUNCTION(BlueprintCallable)
bool BindHealthChangedDelegate(ATest3Character* ATest3Character);
};

提供绑定委托方法及处理委托事件方法供调用。

角色health值修改时,触发文本组件更新数字

UUHealthTextObserver.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "UHealthTextObserver.h"


UUHealthTextObserver::UUHealthTextObserver()
{
}

void UUHealthTextObserver::CharacterHealthChanged(const float Health)
{
this->SetText(FText::AsNumber(Health));
}

bool UUHealthTextObserver::BindHealthChangedDelegate(ATest3Character* ATest3Character)
{
if (ATest3Character == nullptr) return false;
if (ATest3Character->CharacterHealthChangedDelegate.IsBound()) return false;
ATest3Character->CharacterHealthChangedDelegate.BindUObject(this, &UUHealthTextObserver::CharacterHealthChanged);
return true;
}

在蓝图中新增蓝图类继承C++ Test3Character 角色

新增两个HealthTextRender组件用于调式。

构造函数如下,执行两个组件的绑定方法。

开始事件 以及按下数字1的事件,触发health值变化

运行游戏及按下数字1,仅有一个组件起作用,说明单播委托仅支持单个绑定。


2. 多播委托

ATest3Character.h
1
DECLARE_MULTICAST_DELEGATE_OneParam(FCharacterHealthChangedDelegate, float);
ATest3Character.cpp
1
2
3
4
5
void ATest3Character::UpdateHealth(const float _Health)
{
this->Health = _Health;
CharacterHealthChangedDelegate.Broadcast(_Health);
}
UUHealthTextObserver.cpp
1
2
3
4
5
6
7
bool UUHealthTextObserver::BindHealthChangedDelegate(ATest3Character* ATest3Character)
{
if (ATest3Character == nullptr) return false;
ATest3Character->CharacterHealthChangedDelegate.AddUObject(this, &UUHealthTextObserver::CharacterHealthChanged);
return true;
}

单播改多播委托后,可以看到两个Text组件都能生效了。

3. 动态委托

修改为动态多播委托,注意需要写形参,回调方法需要UFUNCTION修饰,确保蓝图系统能正确运行。

ATest3Character.h
1
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCharacterHealthChangedDelegate, float, Health);

增加移除绑定方法

UUHealthTextObserver.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
bool UUHealthTextObserver::BindHealthChangedDelegate(ATest3Character* ATest3Character)
{
if (ATest3Character == nullptr) return false;
ATest3Character->CharacterHealthChangedDelegate.AddDynamic(this, &UUHealthTextObserver::CharacterHealthChanged);
return true;
}

bool UUHealthTextObserver::UnbindHealthChangedDelegate(ATest3Character* ATest3Character)
{
if (ATest3Character == nullptr) return false;
ATest3Character->CharacterHealthChangedDelegate.RemoveDynamic(this, &UUHealthTextObserver::CharacterHealthChanged);
return true;
}

蓝图实现:

  • 数字1 更新health
  • 数字2 绑定委托
  • 数字3 解绑委托