UE4代理委托(代理,动态代理,单播,多播)
官方例子https://docs.unrealengine.com/en-us/Programming/UnrealArchitecture/Delegates
创建基于官方第三人称模版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"
//声明无参代理
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)
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);//打印日志到屏幕函数
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()
{
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()
{
DelegateNoParam.ExecuteIfBound();
Delegate1Param.ExecuteIfBound("Delegate1Param!");
Delegate5Param.ExecuteIfBound("1Param!", "2Param!", "3Param!", "4Param!", "5Param!");
FString CurRV = DelegateRV.Execute();
PrintScreenStr(CurRV);
}
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"
//声明无参代理
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);
}
}
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;
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!");
}
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;
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;
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);
}
}
//声明多播委托,与单播委托一样支持多参数传入,动态多播代理的名称开头须为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;
//多播
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!");
//执行多播
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);
}
}
//声明多播委托,与单播委托一样支持多参数传入,动态多播代理的名称开头须为F,否则会编译报错
DECLARE_MULTICAST_DELEGATE(FMulticastD_NoParam);//声明无参数多播
DECLARE_MULTICAST_DELEGATE_OneParam(FMulticastD_1Param, FString);//声明一个参数多播
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;
//多播
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!");
//执行多播
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);
}
}
UPROPERTY(BlueprintAssignable)//可在蓝图上绑定分配
FDynamicMulticastD_1Param DelegateDynamicMulticastD_1Param;//动态多播
定义多播DelegateDynamicMulticastD_1Param在蓝图TestA、TestA1、ThirdPersonCharacter蓝图上绑定,如下图
运行游戏,摁下Q键,运行效果如下:
5、代理委托解绑
UnBind():解除绑定该代理。
Remove():
将函数从这个多播代理的调用列表中移除(性能为O(N))。请注意代理的顺序可能不会被保留!
RemoveAll():
将所有函数从与特定UserObject绑定的多播代理的调用列表中移除。请注意代理的顺序可能不会被保留!
RemoveAll()将会移除所有与提供的指针绑定的注册代理! 不与对象指针绑定的原始代理将不会被此函数移除!
注意:代理委托绑定时候,绑定函数返回一个代理委托,可以在绑定注册代理委托对象类的EndPlay、Destroyed等结束函数解绑该代理委托。
完毕!解释都在代码里看到,代理委托有点像软件设计模式中的“观察者模式(Observer)”的具体运用,类与类可以降低低耦合!