自定义事件分发

一、在C++中创建可接收事件的接口类EventInterface,继承自UInterface

1、EventInterface.h

cpp 复制代码
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "EventInterface.generated.h"
UINTERFACE(MinimalAPI)
class UEventInterface : public UInterface
{
	GENERATED_BODY()
};

class EVENTDISPATHER_API IEventInterface
{
	GENERATED_BODY()
public:
	UFUNCTION(BlueprintNativeEvent, Category = "Event Dispather Utility")
	void OnReceiveEvent(UObject* Data);
};

注意:

声明一个接收事件的函数OnReceiveEvent,这里没有用Virtual关键字,是因为Virtual关键字不能在蓝图中使用。

BlueprintNativeEvent标记的函数,在C++中需要通过OnReceiveEvent_Implement来实现,在蓝图中也可以调出OnReceiveEvent事件节点。

蓝图中如果调用父类的OnReceiveEvent函数,就是先调用C++版本的OnReceiveEvent_Implement函数。

2、EventInterface.cpp

cpp 复制代码
#include "EventInterface.h"

二、在C++中创建事件管理器类CustomEventManager,继承自BlueprintFunctionLibrary

1、CustomEventManager.h

cpp 复制代码
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CustomEventManager.generated.h"
UCLASS()
class EVENTDISPATHER_API UCustomEventManager : 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);
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Event Dispather Utility")
	static UObject* NewAsset(UClass* ClassType);
};

注意:事件管理器包含了下面函数

添加事件监听AddEventListener

移除事件监听RemoveEventListener

发送事件DispatchEvent

初始化事件参数NewAsset

2、CustomEventManager.cpp

cpp 复制代码
#include "CustomEventManager.h"
#include "EventInterface.h"
#include "Engine.h"

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

void UCustomEventManager::AddEventListener(FString EventName, UObject* Listener)
{
	//表示指针为nullptr但是可能还没有被垃圾回收
	//所以需要IsValidLowLevel判断底层是否有效
	//ImplementsInterface用于判断是否实现了指定的接口
	if (EventName == "" || Listener == nullptr || !Listener->IsValidLowLevel() || !Listener->GetClass()->ImplementsInterface(UEventInterface::StaticClass()))
	{
		return;
	}

	TArray<UObject*>* Arr = UCustomEventManager::AllListeners.Find(EventName);

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

void UCustomEventManager::RemoveEventListener(FString EventName, UObject* Listener)
{
	TArray<UObject*>* Arr = UCustomEventManager::AllListeners.Find(EventName);

	if (Arr != nullptr && Arr->Num() != 0)
	{
		Arr->Remove(Listener);
	}
}

FString UCustomEventManager::DispatchEvent(FString EventName, UObject* Data)
{
	TArray<UObject*>* Arr = UCustomEventManager::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--);
		}
		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* UCustomEventManager::NewAsset(UClass* ClassType)
{
	//GetTransientPackage获取临时的包,将Obj生成进去
	UObject* Obj = NewObject<UObject>(GetTransientPackage(), ClassType);
	return Obj;
}

三、在C++中创建事件参数类MyData,继承自UObject

1、MyData.h

cpp 复制代码
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyData.generated.h"
UCLASS(Blueprintable)
class EVENTDISPATHER_API UMyData : public UObject
{
	GENERATED_BODY()
public:
	UMyData();
	UPROPERTY(BlueprintReadWrite)
	int Param;
};

2、MyData.cpp

cpp 复制代码
#include "MyData.h"
UMyData::UMyData()
{
}

四、在C++中发送、接收和移除事件

1、发送事件

cpp 复制代码
#include "../EventDispatherUtility/CustomEventManager.h"
#include "MyData.h"
void AMyBpAndCpp_Sender::BeginPlay()
{
	UMyData* Data = Cast<UMyData>(UCustomEventManager::NewAsset(UMyData::StaticClass()));
	Data->Param = FMath::RandRange(0, 100);
	UCustomEventManager::DispatchEvent("MyBpAndCpp_DispatchEvent", Data);
}

2、接收和移除事件

MyBpAndCpp_Receive_G.h

cpp 复制代码
#pragma once
#include "CoreMinimal.h"
#include "MyBpAndCpp_Receive_Parent.h"
#include "../EventDispatherUtility/EventInterface.h"
#include "MyBpAndCpp_Receive_G.generated.h"
UCLASS()
class EVENTDISPATHER_API AMyBpAndCpp_Receive_G : public AMyBpAndCpp_Receive_Parent,public IEventInterface
{
	GENERATED_BODY()
protected:
	virtual void BeginPlay() override;
	virtual void BeginDestroy() override;
public:
	void OnReceiveEvent_Implementation(UObject* Data) override;
};

MyBpAndCpp_Receive_G.cpp

cpp 复制代码
#include "MyBpAndCpp_Receive_G.h"
#include "Engine/Engine.h"
#include "MyData.h"
#include "../EventDispatherUtility/CustomEventManager.h"
#include "TimerManager.h"

void AMyBpAndCpp_Receive_G::BeginPlay()
{
	Super::BeginPlay();
	UCustomEventManager::AddEventListener("MyBpAndCpp_DispatchEvent", this);

	FTimerHandle TimerHandle;
	auto Lambda = [this]()
		{
			UCustomEventManager::RemoveEventListener("MyBpAndCpp_DispatchEvent", this);
		};
	//GetWorld()->GetTimerManager().SetTimer(TimerHandle, FTimerDelegate::CreateLambda(Lambda), 5.0f, false);

}
void AMyBpAndCpp_Receive_G::BeginDestroy()
{
	UCustomEventManager::RemoveEventListener("MyBpAndCpp_DispatchEvent", this);

	Super::BeginDestroy();
}
void AMyBpAndCpp_Receive_G::OnReceiveEvent_Implementation(UObject* Data)
{
	GEngine->AddOnScreenDebugMessage(INDEX_NONE, 10.0f, FColor::Green, FString::Printf(TEXT("%i"), Cast<UMyData>(Data)->Param));
}

五、在C++中发送事件,在蓝图中监听、接收、移除事件

1、C++发送事件同上

2、在蓝图中监听、接收、移除事件

六、在蓝图中发送、监听、接收、移除事件,

1、在蓝图中发送事件

2、在蓝图中监听、接收、移除事件

相关推荐
zhangzhangkeji16 分钟前
UE5 蓝图-游老师-14-Actor事件接口-上:共 21个左右的 Actor事件。玩家控制器的函数:显示鼠标光标,启用鼠标悬停事件,启用点击事件。
ue5
Zhichao_972 小时前
【VRoid Studio】创建3D角色导入UE5
ue5
联系QQ19226383 小时前
完整的PID和LQR四旋翼无人机simulink,matlab仿真,两个slx文件一个m文件...
ue5
吴梓穆1 天前
UE5 插件没有自动打包所需的dll
ue5
zhangzhangkeji1 天前
UE5 蓝图-游老师-10-蓝图中的选择分支与循环:br , for loop, while loop
ue5
曼巴UE51 天前
UE5 C++ 动态单播放
c++·ue5
Vince丶3 天前
UE DirectExcel使用笔记
笔记·ue5
AA陈超3 天前
Lyra学习004:GameFeatureData分析
c++·笔记·学习·ue5·虚幻引擎
平行云PVT3 天前
实时云渲染解决UE5 像素流插件迁移及传输数据受限问题
unity·ue5·xr·实时云渲染·云桌面·像素流·云推流
AA陈超3 天前
LyraRPG:001.创建RPGCore插件
c++·笔记·学习·ue5·虚幻引擎·lyra