自定义事件分发

一、在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、在蓝图中监听、接收、移除事件

相关推荐
清流君9 分钟前
【MySQL】数据库 Navicat 可视化工具与 MySQL 命令行基本操作
数据库·人工智能·笔记·mysql·ue5·数字孪生
Involuter11 小时前
UE5 Assimp 自用
ue5
电子云与长程纠缠13 小时前
Unreal Niagara制作SubUV贴图翻页动画
学习·ue5·编辑器·贴图·niagara
子燕若水17 小时前
“Daz to Unreal”将 G8 角色(包括表情)从 daz3d 导入到 UE5。在 UE5 中,我发现使用某个表情并与闭眼混合后,上眼睑出现了问题
3d·ue5
半天法师1 天前
UE5.2+VarjoXR3,Lumen、GI、Nanite无效的两种解决方案
ue5·xr·vr
ue星空1 天前
UE5摄像机画面没有填充满屏幕有黑边
ue5
李詹2 天前
游戏开发核心技术解析——从引擎架构到攻防体系的完整技能树
架构·ue5·游戏引擎·游戏程序·3dsmax·虚幻
子燕若水2 天前
UE5的 Modify Curve 蓝图节点
ue5
人宅4 天前
UE5有些场景的导航生成失败解决方法
ue5
子燕若水4 天前
在 UE5 编辑器中,由于游戏设置 -> EV100 设置,点击播放前后的光照不同。如何保持点击播放前后的光照一致?
游戏·ue5·编辑器