UE4 定时器和事件分发机制(C++和蓝图)

Posted Li~阿蒙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UE4 定时器和事件分发机制(C++和蓝图)相关的知识,希望对你有一定的参考价值。

1. 自己写的机制

2. 用C++调用Unreal的事件分发机制

3. 用蓝图调用Unreal的事件分发机制

4.蓝图调用C++写的Pawn


记得绑定好,Pawn

先是自己写的一个简单的事件分发机制(自己写的肯定有考虑不到的地方(垃圾回收什么什么。。。))
先看自己写的机制:
一个蓝图宏库
一个发起事件者Pawn
一个接口(调用实现了这个接口的接口方法)
一个基类,然后继承这个基类做事件绑定(基类只是给了一个外观)
一个存数据的类
蓝图宏库:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "EventManage.generated.h"

/**
 * 
 */
UCLASS()
class EVEVT_FENFA_API UEventManage : public UBlueprintFunctionLibrary

	GENERATED_BODY()
private:
//要分发的类的集合
	static TMap<FString, TArray<UObject*>> AllListeners; 

public:
//添加到集合中
	UFUNCTION(BlueprintCallable, Category = "Event Dispather Utility")
		static void AddEventListener(FString EventName, UObject* Listener);
//从集合中移除
	UFUNCTION(BlueprintCallable, Category = "Event Dispather Utility")
		static void RemoveEventListener(FString EventName, UObject* Listener);
//分发给集合中的每一个
	UFUNCTION(BlueprintCallable, Category = "Event Dispather Utility")
		static FString DispatchEvent(FString EventName, UObject* Data);
//用来创建一个实例,返回这个实例的Obj 再转换
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Event Dispather Utility")//BlueprintPure声明为一个纯的函数
		static UObject* NewAsset(UClass* ClassType);
;

// Fill out your copyright notice in the Description page of Project Settings.


#include "EventDispather/EventManage.h"
#include "EventDispather/EventInterface.h"

TMap<FString, TArray<UObject*>> UEventManage::AllListeners;

void UEventManage::AddEventListener(FString EventName, UObject* Listener)

	if (EventName == "" 
		|| Listener == nullptr
		|| !Listener->IsValidLowLevel()//!Listener->IsValidLowLevel()这个是判断有没有被垃圾回收收了
		|| !Listener->GetClass()->ImplementsInterface(UEventInterface::StaticClass()))//这个是判断是否实现接口
	return;

	TArray<UObject*>* Arr = UEventManage::AllListeners.Find(EventName);
	if (Arr == nullptr || Arr->Num() == 0) 
		TArray<UObject*> NewArr =  Listener ;
		UEventManage::AllListeners.Add(EventName, NewArr);
	
	else 
		Arr->Add(Listener);
	



void UEventManage::RemoveEventListener(FString EventName, UObject* Listener)

	TArray<UObject*>* Arr = UEventManage::AllListeners.Find(EventName);
	if (Arr != nullptr && Arr->Num() != 0)
	
		Arr->Remove(Listener);
	


FString UEventManage::DispatchEvent(FString EventName, UObject* Data)

	TArray<UObject*>* Arr = UEventManage::AllListeners.Find(EventName);
	if (Arr == nullptr || Arr->Num() == 0) 
		return "'" + EventName + "' NO Listener.";
	
	FString ErrorInfo = "\\n";
	for (int i=0;i<Arr->Num();i++)
	
		UObject* Obj = (*Arr)[i];
		if (Obj == nullptr
			|| !Obj->IsValidLowLevel()
			|| !Obj->GetClass()->ImplementsInterface(UEventInterface::StaticClass()))
		
			Arr->RemoveAt(i--);//RemoveAt会帮我们补上删除的那个空的
		
		else 
			UFunction* FUn=Obj->FindFunction("OnReceiveEvent");
			if (FUn==nullptr||!FUn->IsValidLowLevel())
			
				ErrorInfo += "'" + Obj->GetName() + "'No 'OnReceiveEvent' Function.\\n";
			
			else
			
				Obj->ProcessEvent(FUn,&Data);
			
		

	
	return ErrorInfo;


UObject* UEventManage::NewAsset(UClass* ClassType)

	UObject* Obj = NewObject<UObject>(GetTransientPackage(), ClassType);
	return Obj;


Pawn:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "MyBpAndCpp_Sender.generated.h"

UCLASS()
class EVEVT_FENFA_API AMyBpAndCpp_Sender : public APawn

	GENERATED_BODY()

public:
	// Sets default values for this pawn's properties
	AMyBpAndCpp_Sender();

	UPROPERTY(VisibleAnywhere)
		class UStaticMeshComponent* Sphere;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

;

// Fill out your copyright notice in the Description page of Project Settings.


#include "EventDispather/MyBpAndCpp_Sender.h"
#include "Components/StaticMeshComponent.h"
#include "UObject/ConstructorHelpers.h"
#include "TimerManager.h"
#include "EventDispather/EventManage.h"
#include "EventDispather/MyData.h"


// Sets default values
AMyBpAndCpp_Sender::AMyBpAndCpp_Sender()

 	
	PrimaryActorTick.bCanEverTick = true;
	Sphere = CreateAbstractDefaultSubobject<UStaticMeshComponent>(TEXT("Sphere"));
	RootComponent = Sphere;
	static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticMeshAsset(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'"));
	static ConstructorHelpers::FObjectFinder<UMaterialInterface> MaterialAsset(TEXT("Material'/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial'"));
	if (StaticMeshAsset.Succeeded() && MaterialAsset.Succeeded()) 
		Sphere->SetStaticMesh(StaticMeshAsset.Object);
		Sphere->SetMaterial(0, MaterialAsset.Object);
	



// Called when the game starts or when spawned
void AMyBpAndCpp_Sender::BeginPlay()

	Super::BeginPlay();
	FTimerHandle TimerHandle;
	auto qwr=[]() 
		UMyData* Data = Cast<UMyData>(UEventManage::NewAsset(UMyData::StaticClass()));
		Data->Param = FMath::RandRange(0, 100);
		UEventManage::DispatchEvent("MyBpAndCpp_DispatchEvent", Data);
	;
	GetWorld()->GetTimerManager().SetTimer(TimerHandle, FTimerDelegate::CreateLambda(qwr), 3.0f, true);



// Called every frame
void AMyBpAndCpp_Sender::Tick(float DeltaTime)

	Super::Tick(DeltaTime);



// Called to bind functionality to input
void AMyBpAndCpp_Sender::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)

	Super::SetupPlayerInputComponent(PlayerInputComponent);




一个接口(调用实现了这个接口的接口方法):

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "EventInterface.generated.h"

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UEventInterface : public UInterface

	GENERATED_BODY()
;

/**
 * 
 */
class EVEVT_FENFA_API IEventInterface

	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
//   蓝图可以找到
	UFUNCTION(BlueprintNativeEvent,Category="Event Dispather Utilty")
	void OnReceiveEvent(UObject* Data);
;

一个基类,然后继承这个基类做事件绑定
子Actor;

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "EventDispather/MyBpAndCpp_Receiver_Parent.h"
#include "EventDispather/EventInterface.h"
#include "MyMyBpAndCpp_Receiver_Parent_G.generated.h"

/**
 * 
 */
UCLASS()
class EVEVT_FENFA_API AMyMyBpAndCpp_Receiver_Parent_G : public AMyBpAndCpp_Receiver_Parent,public IEventInterface

	GENERATED_BODY()
protected:
	virtual void BeginPlay() override;

public:

	virtual void BeginDestroy() override;
	virtual void OnReceiveEvent_Implementation(UObject* Data);
;

// Fill out your copyright notice in the Description page of Project Settings.


#include "EventDispather/MyMyBpAndCpp_Receiver_Parent_G.h"
#include "..\\..\\Public\\EventDispather\\MyMyBpAndCpp_Receiver_Parent_G.h"
#include "Engine/Engine.h"
#include "EventDispather/MyData.h"
#include "EventDispather/EventManage.h"
#include "TimerManager.h"

void AMyMyBpAndCpp_Receiver_Parent_G::BeginPlay() 
	Super::BeginPlay();
	UEventManage::AddEventListener("MyBpAndCpp_DispatchEvent",this);
	FTimerHandle TimerHandle;
	auto qwe=[this]() 
		UEventManage::RemoveEventListener("MyBpAndCpp_DispatchEvent", this);
	;
	//GetWorld()->GetTimerManager().SetTimer(TimerHandle, FTimerDelegate::CreateLambda(qwe), 5.0f, false);


void AMyMyBpAndCpp_Receiver_Parent_G::BeginDestroy()

	UEventManage::RemoveEventListener("MyBpAndCpp_DispatchEvent", this);
	Super::BeginDestroy();



void AMyMyBpAndCpp_Receiver_Parent_G::OnReceiveEvent_Implementation(UObject* Data)

	GEngine->AddOnScreenDebugMessage(INDEX_NONE, 3.0f, FColor::Green, FString::Printf(TEXT("%i"), Cast<UMyData>(Data)->Param));


存数据的类:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyData.generated.h"

/**
 * 
 */
UCLASS(Blueprintable)//可以被蓝图继承
class EVEVT_FENFA_API UMyData : public UObject

	GENERATED_BODY()
	
public:
	UMyData();

	UPROPERTY(BlueprintReadWrite)
	int Param;
;

就一个变量。。

结果:分发一个随机数

下面是使用Unreal的分发机制(C++):
一个发事件的Pawn
一个Actor

Pawn:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "MyPawn_Sender.generated.h"

UCLASS()
class EVEVT_FENFA_API AMyPawn_Sender : public APawn

	GENERATED_BODY()

public:
	// Sets default values for this pawn's properties
	AMyPawn_Sender();
	
	DECLARE_MULTICAST_DELEGATE_OneParam(FUECpp_Broadcast, int);//单参数多播
	//DECLARE_MULTICAST_DELEGATE_OneParam(FUECpp_Broadcast, int,value);//这个是暴露给蓝图的蓝图调用后返回一个value的参数
	//UPROPERTY(BlueprintAssignable)
	FUECpp_Broadcast UECpp_Broadcast;

	UPROPERTY(VisibleAnywhere)
	class UStaticMeshComponent* Sphere;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

;

 // Fill out your copyright notice in the Description page of Project Settings.


#include "UE_CPP/MyPawn_Sender.h"
#include "Components/StaticMeshComponent.h"
#include "UObject/ConstructorHelpers.h"
#include "TimerManager.h"

// Sets default values
AMyPawn_Sender::AMyPawn_Sender()

 	// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	Sphere = CreateAbstractDefaultSubobject<UStaticMeshComponent>(TEXT("Sphere"));
	RootComponent = Sphere;
	static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticMeshAsset(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'"));
	static ConstructorHelpers::FObjectFinder<UMaterialInterface> MaterialAsset(TEXT("Material'/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial'"));
	if (StaticMeshAsset.Succeeded() && MaterialAsset.Succeeded()) 
		Sphere->SetStaticMesh(StaticMeshAsset.Object);
		Sphere->SetMaterial(0,MaterialAsset.Object);
	



// Called when the game starts or when spawned
void AMyPawn_Sender::BeginPlay()

	Super::BeginPlay();

	FTimerHandle TimerHandle;//定义一个句柄,用来控制时间定时器的
	/*auto Lambda = [=]() 
		UECpp_Broadcast.Broadcast(FMath::RandRange(0, 100));
	;*/
	auto qwe = [](FUECpp_Broadcast* ucb) //拉姆达表达式
		ucb->Broadcast(FMath::RandRange(0, 100));//调用所有绑定了Int 类的事件
	;
	GetWorld()->GetTimerManager().SetTimer(TimerHandle, FTimerDelegate::CreateLambda(qwe,&UECpp_Broadcast),3.0f,true);//以事件设置定时器


// Called every frame
void AMyPawn_Sender::Tick(float DeltaTime)

	Super::Tick(DeltaTime);



// Called to bind functionality to input
void AMyPawn_Sender::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)

	Super::SetupPlayerInputComponent(PlayerInputComponent);




结果一样是输出一个随机数,

蓝图的:

Pawn:

Aotor:

结果:输出一个随机数

蓝图调用C++写的Pawn:

UE4蓝图传递

参考技术A 蓝图通信理解
蓝图通信的四种方法(1,4类似,2,3类似)
1、 直接蓝图通信
直接通过查询到actor 直接调用方法
2、 事件分配器
类似绑定事件触发器,定义好事件分发器后,其他所有的注册该事件分发器的蓝图都会在该触发器出发后调用各自定义好的事件(类似我写了博客后,所有订阅过我的人接收到消息,有的给我点赞,有的人看一遍,有的人阴阳怪气我)
3、 蓝图接口
往蓝图定义好的消息事件里添加接口函数,类设置中加载接口,只要在触发蓝图中触发该事件,目标蓝图就会触发并传值(类似目标蓝图暴露接口,触发蓝图调用该接口触发事件并传值)
4、 蓝图投射
通过查询actor查到多种类,或者提前不知道蓝图的类型,通过CastTo 蓝图类获取对象蓝图的方法(类似强转换)
获取到的是父蓝图,执行子蓝图的特有功能

模仿flyto方法

以上是关于UE4 定时器和事件分发机制(C++和蓝图)的主要内容,如果未能解决你的问题,请参考以下文章

UE4 C++入门之路1-C++和蓝图的关系和介绍

UE4 C++:事件绑定(输入碰撞检测定时器)

UE4蓝图传递

UE4 C++入门之路3-C++和蓝图的互相调用

UE4蓝图工程转换为c++工程

如何将ue4的蓝图类转换为c++类