版权声明:本文为CSDN博主「落寞根号七」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
官方例子
创建基于官方第三人称模版 C++ 工程,项目名字:MyTP,以下例子基于该工程实现。
代理委托就是 函数指针(类成员函数指针),函数指针指向函数地址,然后调用该函数指针,实现所需效果。
1、代理声明、绑定、执行(单播)
新建 MyTPCharacter.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyTPCharacter.generated.h"
/// @note 声明无参代理
DECLARE_DELEGATE(D_NoParam)
/// @note 声明 1 个参数的代理
DECLARE_DELEGATE_OneParam(D_1Param, FString)
/// @note 声明 5 个参数的代理
DECLARE_DELEGATE_FiveParams(D_5Param, FString, FString, FString, FString, FString)
/// @note 返回值代理
DECLARE_DELEGATE_RetVal(FString, D_RV)
UCLASS(config=Game)
class AMyTPCharacter : public ACharacter
{
GENERATED_BODY()
/** Camera boom positioning the camera behind the character */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent* CameraBoom;
/** Follow camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FollowCamera;
public:
AMyTPCharacter();
/** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
float BaseTurnRate;
/** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
float BaseLookUpRate;
/// @note 定义代理
D_NoParam DelegateNoParam;
D_1Param Delegate1Param;
D_5Param Delegate5Param;
D_RV DelegateRV;
/// @note 绑定到代理的函数
void FuncNoParam();
void Func1Param(FString Str);
void Func5Param(FString Str1, FString Str2, FString Str3, FString Str4, FString Str5);
FString FuncRV();
void OnRunEvent();///< 绑定到 Q 键函数,执行所有代理
void PrintScreenStr(FString Str);// 打印日志到屏幕函数
protected:
/** Resets HMD orientation in VR. */
void OnResetVR();
/** Called for forwards/backward input */
void MoveForward(float Value);
/** Called for side to side input */
void MoveRight(float Value);
/**
* Called via input to turn at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void TurnAtRate(float Rate);
/**
* Called via input to turn look up/down at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void LookUpAtRate(float Rate);
/** Handler for when a touch input begins. */
void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);
/** Handler for when a touch input stops. */
void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);
protected:
// APawn interface
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// End of APawn interface
public:
/** Returns CameraBoom subobject **/
FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
/** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
};
新建 MyTPCharacter.cpp
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "MyTPCharacter.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
#include "Engine/GameEngine.h"
//
// AMyTPCharacter
AMyTPCharacter::AMyTPCharacter()
{
/// @note 绑定函数
DelegateNoParam.BindUObject(this, &AMyTPCharacter::FuncNoParam);
Delegate1Param.BindUObject(this, &AMyTPCharacter::Func1Param);
Delegate5Param.BindUObject(this, &AMyTPCharacter::Func5Param);
DelegateRV.BindUObject(this, &AMyTPCharacter::FuncRV);
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// set our turn rates for input
BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;
// Don't rotate when the controller rotates. Let that just affect the camera.
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Configure character movement
GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate
GetCharacterMovement()->JumpZVelocity = 600.f;
GetCharacterMovement()->AirControl = 0.2f;
// Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character
CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
// Create a follow camera
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
// are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}
//
// Input
void AMyTPCharacter::SetupPlayerInputComponent(class UInputComponent *PlayerInputComponent)
{
// Set up gameplay key bindings
check(PlayerInputComponent);
PlayerInputComponent->BindAction("RunEvent", IE_Pressed, this, &AMyTPCharacter::OnRunEvent);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
PlayerInputComponent->BindAxis("MoveForward", this, &AMyTPCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AMyTPCharacter::MoveRight);
// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("TurnRate", this, &AMyTPCharacter::TurnAtRate);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("LookUpRate", this, &AMyTPCharacter::LookUpAtRate);
// handle touch devices
PlayerInputComponent->BindTouch(IE_Pressed, this, &AMyTPCharacter::TouchStarted);
PlayerInputComponent->BindTouch(IE_Released, this, &AMyTPCharacter::TouchStopped);
// VR headset functionality
PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &AMyTPCharacter::OnResetVR);
}
void AMyTPCharacter::PrintScreenStr(FString Str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Red, Str);
}
}
void AMyTPCharacter::OnRunEvent()
{
/// @note 执行委托
DelegateNoParam.ExecuteIfBound();
Delegate1Param.ExecuteIfBound("Delegate1Param!");
Delegate5Param.ExecuteIfBound("1Param!", "2Param!", "3Param!", "4Param!", "5Param!");
FString CurRV = DelegateRV.Execute();
PrintScreenStr(CurRV);
}
/// @note 绑定到委托的函数定义
void AMyTPCharacter::FuncNoParam()
{
PrintScreenStr("NoParam");
}
void AMyTPCharacter::Func1Param(FString Str)
{
PrintScreenStr(Str);
}
void AMyTPCharacter::Func5Param(FString Str1, FString Str2, FString Str3, FString Str4, FString Str5)
{
PrintScreenStr(Str1 + " " + Str2 + " " + Str3 + " " + Str4 + " " + Str5);
}
FString AMyTPCharacter::FuncRV()
{
return FString("FuncRV");
}
/// --------------------------------
void AMyTPCharacter::OnResetVR()
{
UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}
void AMyTPCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
{
Jump();
}
void AMyTPCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
{
StopJumping();
}
void AMyTPCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
void AMyTPCharacter::LookUpAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
void AMyTPCharacter::MoveForward(float Value)
{
if ((Controller != NULL) && (Value != 0.0f))
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
}
void AMyTPCharacter::MoveRight(float Value)
{
if ((Controller != NULL) && (Value != 0.0f))
{
// find out which way is right
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get right vector
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement in that direction
AddMovementInput(Direction, Value);
}
}
绑定 Q 键如下图:
启动 游戏,摁下 Q 键盘,屏幕信息如下:
2、代理各种绑定(单播)
还是基于步骤 1,编辑器创建一个基于 None 的类,名字:MyTest
新建 MyTest.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
/**
*
*/
class MYTP_API MyTest
{
public:
MyTest();
~MyTest();
void TestFunc(FString Str);
};
新建 MyTest.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyTest.h"
#include "Engine/GameEngine.h"
MyTest::MyTest()
{
}
MyTest::~MyTest()
{
}
void MyTest::TestFunc(FString Str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Blue, Str);
}
}
更新 MyTPCharacter.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyTest.h"
#include "MyTPCharacter.generated.h"
/// @note 声明无参代理
DECLARE_DELEGATE(D_NoParam)
/// @note 声明 1 个参数的代理
DECLARE_DELEGATE_OneParam(D_1Param, FString)
/// @note 声明 5 个参数的代理
DECLARE_DELEGATE_FiveParams(D_5Param, FString, FString, FString, FString, FString)
/// @note 返回值代理
DECLARE_DELEGATE_RetVal(FString, D_RV)
static void Static_Func(FString Str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Blue, Str);
}
}
UCLASS(config = Game)
class AMyTPCharacter : public ACharacter
{
GENERATED_BODY()
/** Camera boom positioning the camera behind the character */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent *CameraBoom;
/** Follow camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent *FollowCamera;
public:
AMyTPCharacter();
/** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
float BaseTurnRate;
/** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
float BaseLookUpRate;
/// @note 定义代理
D_NoParam DelegateNoParam;
D_1Param Delegate1Param;
D_5Param Delegate5Param;
D_RV DelegateRV;
/// @note 绑定到代理的函数
void FuncNoParam();
void Func1Param(FString Str);
void Func5Param(FString Str1, FString Str2, FString Str3, FString Str4, FString Str5);
FString FuncRV();
void OnRunEvent(); ///< 绑定到 Q 键函数,执行所有代理
void PrintScreenStr(FString Str); ///< 打印日志到屏幕函数
//定义多种绑定
D_1Param DelegateLambda;
D_1Param DelegateRaw;
D_1Param DelegateSP;
D_1Param DelegateStatic;
D_1Param DelegateUFunction;
UFUNCTION() ///< 可以声明 BlueprintCallable 等其他标记
void TestUFunc(FString str);
MyTest MyT;
TSharedPtr<MyTest> MyT_SPtr;
protected:
/** Resets HMD orientation in VR. */
void OnResetVR();
/** Called for forwards/backward input */
void MoveForward(float Value);
/** Called for side to side input */
void MoveRight(float Value);
/**
* Called via input to turn at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void TurnAtRate(float Rate);
/**
* Called via input to turn look up/down at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void LookUpAtRate(float Rate);
/** Handler for when a touch input begins. */
void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);
/** Handler for when a touch input stops. */
void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);
protected:
// APawn interface
virtual void SetupPlayerInputComponent(class UInputComponent *PlayerInputComponent) override;
// End of APawn interface
public:
/** Returns CameraBoom subobject **/
FORCEINLINE class USpringArmComponent *GetCameraBoom() const { return CameraBoom; }
/** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent *GetFollowCamera() const { return FollowCamera; }
};
更新 MyTPCharacter.cpp:
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "MyTPCharacter.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
#include "Engine/GameEngine.h"
#include "MyTest.h"
//
// AMyTPCharacter
AMyTPCharacter::AMyTPCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// set our turn rates for input
BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;
// Don't rotate when the controller rotates. Let that just affect the camera.
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Configure character movement
GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate
GetCharacterMovement()->JumpZVelocity = 600.f;
GetCharacterMovement()->AirControl = 0.2f;
// Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character
CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
// Create a follow camera
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
// are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)
DelegateNoParam.BindUObject(this, &AMyTPCharacter::FuncNoParam); ///< this 只能是继承自 UObject 的类, 绑定继承 UObject 类的对象函数
Delegate1Param.BindUObject(this, &AMyTPCharacter::Func1Param);
Delegate5Param.BindUObject(this, &AMyTPCharacter::Func5Param);
DelegateRV.BindUObject(this, &AMyTPCharacter::FuncRV);
/// @note Lambda 表达式
auto LambdaFunc = [&](FString str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Blue, str);
}
};
DelegateLambda.BindLambda(LambdaFunc); ///< 绑定 lambda 表达式
DelegateRaw.BindRaw(&MyT, &MyTest::TestFunc); ///< 绑定到一个原始的 C++ 类函数绑定到一个原始的 C++ 指针全局函数代理上。原始指针不使用任何引用,所以如果从代理的底层删除了该对象,那么调用它可能是不安全的。因此,当调用 Execute() 时一定要小心!
MyT_SPtr = MakeShareable(new MyTest());
DelegateSP.BindSP(MyT_SPtr.ToSharedRef(), &MyTest::TestFunc); ///< 绑定一个基于共享指针的成员函数代理。共享指针代理保持到您的对象的弱引用。您可以使用 ExecuteIfBound() 来调用它们。
DelegateStatic.BindStatic(Static_Func); ///< 绑定全局静态函数
DelegateUFunction.BindUFunction(this, "TestUFunc"); ///< 绑定 this 指向对象类蓝图函数
}
//
// Input
void AMyTPCharacter::SetupPlayerInputComponent(class UInputComponent *PlayerInputComponent)
{
// Set up gameplay key bindings
check(PlayerInputComponent);
PlayerInputComponent->BindAction("RunEvent", IE_Pressed, this, &AMyTPCharacter::OnRunEvent);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
PlayerInputComponent->BindAxis("MoveForward", this, &AMyTPCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AMyTPCharacter::MoveRight);
// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("TurnRate", this, &AMyTPCharacter::TurnAtRate);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("LookUpRate", this, &AMyTPCharacter::LookUpAtRate);
// handle touch devices
PlayerInputComponent->BindTouch(IE_Pressed, this, &AMyTPCharacter::TouchStarted);
PlayerInputComponent->BindTouch(IE_Released, this, &AMyTPCharacter::TouchStopped);
// VR headset functionality
PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &AMyTPCharacter::OnResetVR);
}
void AMyTPCharacter::PrintScreenStr(FString Str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Red, Str);
}
}
void AMyTPCharacter::OnRunEvent()
{
DelegateNoParam.ExecuteIfBound();
Delegate1Param.ExecuteIfBound("Delegate1Param!");
Delegate5Param.ExecuteIfBound("1Param!", "2Param!", "3Param!", "4Param!", "5Param!");
FString CurRV = DelegateRV.Execute();
PrintScreenStr(CurRV);
DelegateLambda.ExecuteIfBound("DelegateLambda!");
DelegateRaw.ExecuteIfBound("DelegateRaw!");
DelegateSP.ExecuteIfBound("DelegateSP!");
DelegateStatic.ExecuteIfBound("DelegateStatic!");
DelegateUFunction.ExecuteIfBound("DelegateUFunction!");
}
void AMyTPCharacter::FuncNoParam()
{
PrintScreenStr("NoParam");
}
void AMyTPCharacter::Func1Param(FString Str)
{
PrintScreenStr(Str);
}
void AMyTPCharacter::Func5Param(FString Str1, FString Str2, FString Str3, FString Str4, FString Str5)
{
PrintScreenStr(Str1 + " " + Str2 + " " + Str3 + " " + Str4 + " " + Str5);
}
FString AMyTPCharacter::FuncRV()
{
return FString("FuncRV");
}
void AMyTPCharacter::TestUFunc(FString str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Blue, str);
}
}
/// ----------------------------------------------------------------------------
void AMyTPCharacter::OnResetVR()
{
UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}
void AMyTPCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
{
Jump();
}
void AMyTPCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
{
StopJumping();
}
void AMyTPCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
void AMyTPCharacter::LookUpAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
void AMyTPCharacter::MoveForward(float Value)
{
if ((Controller != NULL) && (Value != 0.0f))
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
}
void AMyTPCharacter::MoveRight(float Value)
{
if ((Controller != NULL) && (Value != 0.0f))
{
// find out which way is right
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get right vector
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement in that direction
AddMovementInput(Direction, Value);
}
}
按 Q 键,运行输出到屏幕如下图:
3、多播代理委托
多播代理的功能和单播代理几乎一样。区别是它们对目标为弱引用,可以和结构体一起使用,可以很方便地进行拷贝,等等。 和普通代理一样,多播代理可被载入/保存,并远程触发;但是,多播代理的函数无法使用返回值。最好将它们用于方便地传递代理集合。事件 是特定类型的多播代理,同时具有对 Broadcast()
, IsBound()
, 和 Clear()
函数的受限访问。
多播代理在代理触发时可能会调用多个函数绑定。因此,绑定函数在语句中看起来更为像是数组。
多播代理允许您附加多个函数代理,然后通过调用多播代理的 Broadcast()
函数一次性执行所有函数代理。多播代理的签名不能使用返回值。
任何时候在多播代理上调用 Broadcast()
函数都是安全的,即时它没有绑定任何函数也可以。唯一需要注意的时候是您使用代理来 初始化 输出变量时,这样做一般是非常不好的。
当调用 Broadcast()
函数时,绑定函数的执行顺序是不确定的。可能并不按照函数的添加顺序执行
- 步骤1 和 2 单播代理委托,指的是只能绑定 一个函数指针 的委托,实现一对一的通知。
- 多播代理委托,指的是能绑定 多个函数指针 的委托,实现一对多的通知。
- 多播代理委托的定义是有 “MULTICAST” 修饰的委托
继续基于步骤 1、2 实现,在编辑器创建基于 C++ 类继承于 Actor 两个类,名字分别叫做:TestActor、TestActor2
新建 TestActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TestActor.generated.h"
UCLASS()
class MYTP_API ATestActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATestActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
void FuncNoParam();
void Func1Param(FString str);
};
新建 TestActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "TestActor.h"
#include "Engine/GameEngine.h"
#include "MyTPCharacter.h"
// Sets default values
ATestActor::ATestActor()
{
// 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;
}
// Called when the game starts or when spawned
void ATestActor::BeginPlay()
{
Super::BeginPlay();
APlayerController *PC = GetWorld()->GetFirstPlayerController();
if (nullptr == PC)
return;
ACharacter *Cha = PC->GetCharacter();
if (nullptr == Cha)
return;
AMyTPCharacter *MyTPC = Cast<AMyTPCharacter>(Cha);
if (nullptr == MyTPC)
return;
/// @note 绑定多播
MyTPC->DelegateMulticastD_NoParam.AddUObject(this, &ATestActor::FuncNoParam);
MyTPC->DelegateMulticastD_1Param.AddUObject(this, &ATestActor::Func1Param);
}
// Called every frame
void ATestActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ATestActor::FuncNoParam()
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Yellow, "TestActor FuncNoParam");
}
}
void ATestActor::Func1Param(FString str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Yellow, "TestActor Func1Param" + str);
}
}
新建 TestActor2.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TestActor2.generated.h"
UCLASS()
class MYTP_API ATestActor2 : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATestActor2();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
void FuncNoParam();
void Func1Param(FString str);
};
新建 TestActor2.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "TestActor2.h"
#include "Engine/GameEngine.h"
#include "MyTPCharacter.h"
// Sets default values
ATestActor2::ATestActor2()
{
// 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;
}
// Called when the game starts or when spawned
void ATestActor2::BeginPlay()
{
Super::BeginPlay();
APlayerController *PC = GetWorld()->GetFirstPlayerController();
if (nullptr == PC)
return;
ACharacter *Cha = PC->GetCharacter();
if (nullptr == Cha)
return;
AMyTPCharacter *MyTPC = Cast<AMyTPCharacter>(Cha);
if (nullptr == MyTPC)
return;
/// @note 绑定多播
MyTPC->DelegateMulticastD_NoParam.AddUObject(this, &ATestActor2::FuncNoParam);
MyTPC->DelegateMulticastD_1Param.AddUObject(this, &ATestActor2::Func1Param);
}
// Called every frame
void ATestActor2::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ATestActor2::FuncNoParam()
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Yellow, "TestActor2 FuncNoParam");
}
}
void ATestActor2::Func1Param(FString str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Yellow, "TestActor2 Func1Param" + str);
}
}
更新 MyTPCharacter.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyTest.h"
#include "MyTPCharacter.generated.h"
// 声明无参代理
DECLARE_DELEGATE(D_NoParam)
// 声明 1 个参数的代理
DECLARE_DELEGATE_OneParam(D_1Param, FString)
// 声明 5 个参数的代理
DECLARE_DELEGATE_FiveParams(D_5Param, FString, FString, FString, FString, FString)
// 返回值代理
DECLARE_DELEGATE_RetVal(FString, D_RV)
static void Static_Func(FString Str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Blue, Str);
}
}
/// @note 声明多播委托,与单播委托一样支持多参数传入,动态多播代理的名称开头须为 F,否则会编译报错
DECLARE_MULTICAST_DELEGATE(FMulticastD_NoParam); ///< 声明无参数多播
DECLARE_MULTICAST_DELEGATE_OneParam(FMulticastD_1Param, FString); ///< 声明一个参数多播
UCLASS(config = Game)
class AMyTPCharacter : public ACharacter
{
GENERATED_BODY()
/** Camera boom positioning the camera behind the character */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent *CameraBoom;
/** Follow camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent *FollowCamera;
public:
AMyTPCharacter();
/** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
float BaseTurnRate;
/** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
float BaseLookUpRate;
// 定义代理
D_NoParam DelegateNoParam;
D_1Param Delegate1Param;
D_5Param Delegate5Param;
D_RV DelegateRV;
// 绑定到代理的函数
void FuncNoParam();
void Func1Param(FString Str);
void Func5Param(FString Str1, FString Str2, FString Str3, FString Str4, FString Str5);
FString FuncRV();
void OnRunEvent(); // 绑定到 Q 键函数,执行所有代理
void PrintScreenStr(FString Str); // 打印日志到屏幕函数
// 定义多种绑定
D_1Param DelegateLambda;
D_1Param DelegateRaw;
D_1Param DelegateSP;
D_1Param DelegateStatic;
D_1Param DelegateUFunction;
UFUNCTION() // 可以声明 BlueprintCallable 等其他标记
void TestUFunc(FString str);
MyTest MyT;
TSharedPtr<MyTest> MyT_SPtr;
/// @note 定义多播
FMulticastD_NoParam DelegateMulticastD_NoParam;
FMulticastD_1Param DelegateMulticastD_1Param;
protected:
/** Resets HMD orientation in VR. */
void OnResetVR();
/** Called for forwards/backward input */
void MoveForward(float Value);
/** Called for side to side input */
void MoveRight(float Value);
/**
* Called via input to turn at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void TurnAtRate(float Rate);
/**
* Called via input to turn look up/down at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void LookUpAtRate(float Rate);
/** Handler for when a touch input begins. */
void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);
/** Handler for when a touch input stops. */
void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);
protected:
// APawn interface
virtual void SetupPlayerInputComponent(class UInputComponent *PlayerInputComponent) override;
// End of APawn interface
public:
/** Returns CameraBoom subobject **/
FORCEINLINE class USpringArmComponent *GetCameraBoom() const { return CameraBoom; }
/** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent *GetFollowCamera() const { return FollowCamera; }
};
更新 MyTPCharacter.cpp
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "MyTPCharacter.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
#include "Engine/GameEngine.h"
#include "MyTest.h"
//
// AMyTPCharacter
AMyTPCharacter::AMyTPCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// set our turn rates for input
BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;
// Don't rotate when the controller rotates. Let that just affect the camera.
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Configure character movement
GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate
GetCharacterMovement()->JumpZVelocity = 600.f;
GetCharacterMovement()->AirControl = 0.2f;
// Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character
CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
// Create a follow camera
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
// are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)
DelegateNoParam.BindUObject(this, &AMyTPCharacter::FuncNoParam); // this 只能是继承自 UObject 的类,绑定继承 UObject 类的对象函数
Delegate1Param.BindUObject(this, &AMyTPCharacter::Func1Param);
Delegate5Param.BindUObject(this, &AMyTPCharacter::Func5Param);
DelegateRV.BindUObject(this, &AMyTPCharacter::FuncRV);
// Lambda 表达式
auto LambdaFunc = [&](FString str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Blue, str);
}
};
DelegateLambda.BindLambda(LambdaFunc); // 绑定 lambda 表达式
DelegateRaw.BindRaw(&MyT, &MyTest::TestFunc); // 绑定到一个原始的 C++ 类函数绑定到一个原始的 C++ 指针全局函数代理上。原始指针不使用任何引用,所以如果从代理的底层删除了该对象,那么调用它可能是不安全的。因此,当调用 Execute() 时一定要小心!
MyT_SPtr = MakeShareable(new MyTest());
DelegateSP.BindSP(MyT_SPtr.ToSharedRef(), &MyTest::TestFunc); // 绑定一个基于共享指针的成员函数代理。共享指针代理保持到您的对象的弱引用。您可以使用 ExecuteIfBound() 来调用它们。
DelegateStatic.BindStatic(Static_Func); // 绑定全局静态函数
DelegateUFunction.BindUFunction(this, "TestUFunc"); // 绑定 this 指向对象类蓝图函数
}
//
// Input
void AMyTPCharacter::SetupPlayerInputComponent(class UInputComponent *PlayerInputComponent)
{
// Set up gameplay key bindings
check(PlayerInputComponent);
PlayerInputComponent->BindAction("RunEvent", IE_Pressed, this, &AMyTPCharacter::OnRunEvent);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
PlayerInputComponent->BindAxis("MoveForward", this, &AMyTPCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AMyTPCharacter::MoveRight);
// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("TurnRate", this, &AMyTPCharacter::TurnAtRate);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("LookUpRate", this, &AMyTPCharacter::LookUpAtRate);
// handle touch devices
PlayerInputComponent->BindTouch(IE_Pressed, this, &AMyTPCharacter::TouchStarted);
PlayerInputComponent->BindTouch(IE_Released, this, &AMyTPCharacter::TouchStopped);
// VR headset functionality
PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &AMyTPCharacter::OnResetVR);
}
void AMyTPCharacter::PrintScreenStr(FString Str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Red, Str);
}
}
void AMyTPCharacter::OnRunEvent()
{
// 执行单播
DelegateNoParam.ExecuteIfBound();
Delegate1Param.ExecuteIfBound("Delegate1Param!");
Delegate5Param.ExecuteIfBound("1Param!", "2Param!", "3Param!", "4Param!", "5Param!");
FString CurRV = DelegateRV.Execute();
PrintScreenStr(CurRV);
DelegateLambda.ExecuteIfBound("DelegateLambda!");
DelegateRaw.ExecuteIfBound("DelegateRaw!");
DelegateSP.ExecuteIfBound("DelegateSP!");
DelegateStatic.ExecuteIfBound("DelegateStatic!");
DelegateUFunction.ExecuteIfBound("DelegateUFunction!");
/// @note 执行多播
DelegateMulticastD_NoParam.Broadcast();
DelegateMulticastD_1Param.Broadcast(FString("@1Param!!!"));
}
void AMyTPCharacter::FuncNoParam()
{
PrintScreenStr("NoParam");
}
void AMyTPCharacter::Func1Param(FString Str)
{
PrintScreenStr(Str);
}
void AMyTPCharacter::Func5Param(FString Str1, FString Str2, FString Str3, FString Str4, FString Str5)
{
PrintScreenStr(Str1 + " " + Str2 + " " + Str3 + " " + Str4 + " " + Str5);
}
FString AMyTPCharacter::FuncRV()
{
return FString("FuncRV");
}
void AMyTPCharacter::TestUFunc(FString str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Blue, str);
}
}
void AMyTPCharacter::OnResetVR()
{
UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}
void AMyTPCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
{
Jump();
}
void AMyTPCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
{
StopJumping();
}
void AMyTPCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
void AMyTPCharacter::LookUpAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
void AMyTPCharacter::MoveForward(float Value)
{
if ((Controller != NULL) && (Value != 0.0f))
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
}
void AMyTPCharacter::MoveRight(float Value)
{
if ((Controller != NULL) && (Value != 0.0f))
{
// find out which way is right
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get right vector
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement in that direction
AddMovementInput(Direction, Value);
}
}
编辑器分别创建继承于 TestActor、TestActor2 的蓝图类 TestA
、TestA2
,并且把它们摆放到测试场景关卡生成;
启动游戏后,摁 Q 键,运行效果如下:
4、动态多播代理委托(与蓝图混合使用)
动态委托可以序列化,它们的函数可以通过名称找到,而且它们比常规委托慢。动态代理的使用方法和普通代理相似,不过动态多播代理可以暴露给蓝图绑定,方便在蓝图做处理,特别是 C++ 和 UMG 数据通知交互。
还是基于步骤 1/2/3 来实现,代码过程如下:
更新 MyTPCharacter.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyTest.h"
#include "MyTPCharacter.generated.h"
//声明无参代理
DECLARE_DELEGATE(D_NoParam)
//声明1个参数的代理
DECLARE_DELEGATE_OneParam(D_1Param, FString)
//声明5个参数的代理
DECLARE_DELEGATE_FiveParams(D_5Param, FString, FString, FString, FString, FString)
//返回值代理
DECLARE_DELEGATE_RetVal(FString, D_RV)
static void Static_Func(FString Str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Blue, Str);
}
}
/// @note 声明多播委托,与单播委托一样支持多参数传入,动态多播代理的名称开头须为 F,否则会编译报错
DECLARE_MULTICAST_DELEGATE(FMulticastD_NoParam); ///< 声明无参数多播
DECLARE_MULTICAST_DELEGATE_OneParam(FMulticastD_1Param, FString); ///< 声明一个参数多播
/// @note 声明动态多播
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDynamicMulticastD_1Param, FString, Str); ///< 声明一个参数动态多播,动态多播代理的名称开头须为 F,否则会编译报错
UCLASS(config = Game)
class AMyTPCharacter : public ACharacter
{
GENERATED_BODY()
/** Camera boom positioning the camera behind the character */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent *CameraBoom;
/** Follow camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent *FollowCamera;
public:
AMyTPCharacter();
/** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
float BaseTurnRate;
/** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
float BaseLookUpRate;
// 定义代理
D_NoParam DelegateNoParam;
D_1Param Delegate1Param;
D_5Param Delegate5Param;
D_RV DelegateRV;
// 绑定到代理的函数
void FuncNoParam();
void Func1Param(FString Str);
void Func5Param(FString Str1, FString Str2, FString Str3, FString Str4, FString Str5);
FString FuncRV();
void OnRunEvent(); //绑定到 Q 键函数,执行所有代理
void PrintScreenStr(FString Str); //打印日志到屏幕函数
// 定义多种绑定
D_1Param DelegateLambda;
D_1Param DelegateRaw;
D_1Param DelegateSP;
D_1Param DelegateStatic;
D_1Param DelegateUFunction;
UFUNCTION() //可以声明 BlueprintCallable 等其他标记
void TestUFunc(FString str);
MyTest MyT;
TSharedPtr<MyTest> MyT_SPtr;
/// @note 多播
FMulticastD_NoParam DelegateMulticastD_NoParam;
FMulticastD_1Param DelegateMulticastD_1Param;
UPROPERTY(BlueprintAssignable) //可在蓝图上绑定分配
FDynamicMulticastD_1Param DelegateDynamicMulticastD_1Param; ///< 动态多播
protected:
/** Resets HMD orientation in VR. */
void OnResetVR();
/** Called for forwards/backward input */
void MoveForward(float Value);
/** Called for side to side input */
void MoveRight(float Value);
/**
* Called via input to turn at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void TurnAtRate(float Rate);
/**
* Called via input to turn look up/down at a given rate.
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void LookUpAtRate(float Rate);
/** Handler for when a touch input begins. */
void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);
/** Handler for when a touch input stops. */
void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);
protected:
// APawn interface
virtual void SetupPlayerInputComponent(class UInputComponent *PlayerInputComponent) override;
// End of APawn interface
public:
/** Returns CameraBoom subobject **/
FORCEINLINE class USpringArmComponent *GetCameraBoom() const { return CameraBoom; }
/** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent *GetFollowCamera() const { return FollowCamera; }
};
更新 MyTPCharacter.cpp
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "MyTPCharacter.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
#include "Engine/GameEngine.h"
#include "MyTest.h"
//
// AMyTPCharacter
AMyTPCharacter::AMyTPCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// set our turn rates for input
BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;
// Don't rotate when the controller rotates. Let that just affect the camera.
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Configure character movement
GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate
GetCharacterMovement()->JumpZVelocity = 600.f;
GetCharacterMovement()->AirControl = 0.2f;
// Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character
CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
// Create a follow camera
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
// are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)
DelegateNoParam.BindUObject(this, &AMyTPCharacter::FuncNoParam); // this 只能是继承自 UObject 的类,绑定继承 UObject 类的对象函数
Delegate1Param.BindUObject(this, &AMyTPCharacter::Func1Param);
Delegate5Param.BindUObject(this, &AMyTPCharacter::Func5Param);
DelegateRV.BindUObject(this, &AMyTPCharacter::FuncRV);
// Lambda 表达式
auto LambdaFunc = [&](FString str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Blue, str);
}
};
DelegateLambda.BindLambda(LambdaFunc); //绑定 lambda 表达式
DelegateRaw.BindRaw(&MyT, &MyTest::TestFunc); //绑定到一个原始的 C++ 类函数绑定到一个原始的 C++ 指针全局函数代理上。原始指针不使用任何引用,所以如果从代理的底层删除了该对象,那么调用它可能是不安全的。因此,当调用 Execute() 时一定要小心!
MyT_SPtr = MakeShareable(new MyTest());
DelegateSP.BindSP(MyT_SPtr.ToSharedRef(), &MyTest::TestFunc); // 绑定一个基于共享指针的成员函数代理。共享指针代理保持到您的对象的弱引用。您可以使用 ExecuteIfBound() 来调用它们。
DelegateStatic.BindStatic(Static_Func); // 绑定全局静态函数
DelegateUFunction.BindUFunction(this, "TestUFunc"); // 绑定 this 指向对象类蓝图函数
}
//
// Input
void AMyTPCharacter::SetupPlayerInputComponent(class UInputComponent *PlayerInputComponent)
{
// Set up gameplay key bindings
check(PlayerInputComponent);
PlayerInputComponent->BindAction("RunEvent", IE_Pressed, this, &AMyTPCharacter::OnRunEvent);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
PlayerInputComponent->BindAxis("MoveForward", this, &AMyTPCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AMyTPCharacter::MoveRight);
// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("TurnRate", this, &AMyTPCharacter::TurnAtRate);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("LookUpRate", this, &AMyTPCharacter::LookUpAtRate);
// handle touch devices
PlayerInputComponent->BindTouch(IE_Pressed, this, &AMyTPCharacter::TouchStarted);
PlayerInputComponent->BindTouch(IE_Released, this, &AMyTPCharacter::TouchStopped);
// VR headset functionality
PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &AMyTPCharacter::OnResetVR);
}
void AMyTPCharacter::PrintScreenStr(FString Str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Red, Str);
}
}
void AMyTPCharacter::OnRunEvent()
{
// 执行单播
DelegateNoParam.ExecuteIfBound();
Delegate1Param.ExecuteIfBound("Delegate1Param!");
Delegate5Param.ExecuteIfBound("1Param!", "2Param!", "3Param!", "4Param!", "5Param!");
FString CurRV = DelegateRV.Execute();
PrintScreenStr(CurRV);
DelegateLambda.ExecuteIfBound("DelegateLambda!");
DelegateRaw.ExecuteIfBound("DelegateRaw!");
DelegateSP.ExecuteIfBound("DelegateSP!");
DelegateStatic.ExecuteIfBound("DelegateStatic!");
DelegateUFunction.ExecuteIfBound("DelegateUFunction!");
/// @note 执行多播
DelegateMulticastD_NoParam.Broadcast();
DelegateMulticastD_1Param.Broadcast(FString("@1Param!!!"));
DelegateDynamicMulticastD_1Param.Broadcast(FString("@1DynamicMulticast!")); ///< 执行动态多播
}
void AMyTPCharacter::FuncNoParam()
{
PrintScreenStr("NoParam");
}
void AMyTPCharacter::Func1Param(FString Str)
{
PrintScreenStr(Str);
}
void AMyTPCharacter::Func5Param(FString Str1, FString Str2, FString Str3, FString Str4, FString Str5)
{
PrintScreenStr(Str1 + " " + Str2 + " " + Str3 + " " + Str4 + " " + Str5);
}
FString AMyTPCharacter::FuncRV()
{
return FString("FuncRV");
}
void AMyTPCharacter::TestUFunc(FString str)
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 30, FColor::Blue, str);
}
}
void AMyTPCharacter::OnResetVR()
{
UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}
void AMyTPCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
{
Jump();
}
void AMyTPCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
{
StopJumping();
}
void AMyTPCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
void AMyTPCharacter::LookUpAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
void AMyTPCharacter::MoveForward(float Value)
{
if ((Controller != NULL) && (Value != 0.0f))
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
}
void AMyTPCharacter::MoveRight(float Value)
{
if ((Controller != NULL) && (Value != 0.0f))
{
// find out which way is right
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get right vector
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement in that direction
AddMovementInput(Direction, Value);
}
}
定义多播 DelegateDynamicMulticastD_1Param
在蓝图 TestA
、TestA1
、ThirdPersonCharacter
蓝图上 绑定 ,如下图
运行游戏,摁下 Q 键,运行效果如下:
5、代理委托解绑
UnBind()
:解除绑定该代理。Remove()
:将函数从这个多播代理的调用列表中移除(性能为O(N)
)。请注意代理的顺序可能不会被保留!RemoveAll()
:将所有函数从与特定UserObject
绑定的多播代理的调用列表中移除。请注意代理的顺序可能不会被保留!RemoveAll()
将会移除所有与提供的指针绑定的注册代理! 不与对象指针绑定的原始代理将不会被此函数移除!
注意:代理委托绑定时候,绑定函数返回一个代理委托,可以在绑定注册代理委托对象类的 EndPlay
、Destroyed
等结束函数解绑该代理委托。
完毕!解释都在代码里看到,代理委托有点像软件设计模式中的 “观察者模式(Observer)” 的具体运用,类与类可以降低低耦合!