自定义事件分发

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

相关推荐
Zhichao_975 小时前
【UE5.3 C++】ARPG游戏 05-准备角色攻击的武器和动画
游戏·ue5
zhangzhangkeji9 小时前
UE5 C++(15-3):UFUNCTION 的 meta 元数据, DisplayName ,同 UPROPERTY 的 meta
ue5
夜色。12 小时前
UE5 Error LNK2019 编译异常修复备忘
ue5
陈友松1 天前
UE5 表格文件动态导入导出插件
ue5
zhangzhangkeji1 天前
UE5 C++(17):结构体,USTRUCT(BlueprintType) struct FmyStruct ,必须用 F 开头
ue5
碎梦人1 天前
基于UE5开发的pico大空间项目开发之虚拟坐标系与真实坐标系的对应关系
ue5·lbe·大空间坐标系对应关系
zhangzhangkeji1 天前
UE5 C++(16):枚举 UENUM,UENUM(BlueprintType) 使其成为蓝图里的类型。模板类 TEnumAsByte<enum 类型>
ue5
zhangzhangkeji1 天前
UE5 C++(UObject 的实例化 19-2):UObject 类的继承关系
ue5
妙为2 天前
Unreal Engine5报错:Expected WarriorDebugHelper.h to be first header included.
ue5·虚幻·编译错误
Zhichao_973 天前
【UE5.3 C++】ARPG游戏 04-角色脚部贴合地形
游戏·ue5