您现在的位置是:首页 >技术教程 >UE4/5多人游戏详解(七、自定义委托,实现寻找会话和加入会话的函数,通过Steam进行两台电脑的联机)网站首页技术教程

UE4/5多人游戏详解(七、自定义委托,实现寻找会话和加入会话的函数,通过Steam进行两台电脑的联机)

多方通行8 2023-06-04 20:00:02
简介UE4/5多人游戏详解(七、自定义委托,实现寻找会话和加入会话的函数,通过Steam进行两台电脑的联机)

目录

可能出现问题(在六部分的测试可能无法连接的问题【在末尾加上了,怕有人没看见在这里写一下】)

自定义委托

调整位置

创建更多的委托和回调函数给菜单:

多播和动态多播

 代码:

委托变量

 代码:

回调函数

 代码:

绑定委托和动态函数:

 代码:

头文件添加

 代码:

实现加入按钮:

实现寻找函数:

 代码:

实现寻找的回调函数

 代码:

菜单类的onfind回调函数

 代码:

加入会话函数:

 代码:

加入会话的回调函数:

 代码:

菜单内的加入回调函数

 代码:

防止可能出现的意外进行的添加:

 代码1:

 代码2:


可能出现问题(在六部分的测试可能无法连接的问题【在末尾加上了,怕有人没看见在这里写一下】)

打包测试发现在连接steam的情况下无法创建会话

对于这种情况,我们打开:

 将这里改掉:

 

 

自定义委托

相关内容可以查看:

UE4/5C++:Delegate(委托or代理?)的使用_多方通行8的博客-CSDN博客

在子系统创建自定义委托

 然后在下面:

 

(这里的红线是说找不到,并不需要在意,如果这个时候启动或者热更新仍然是可以使用的,说明这是对的,如果看这个不爽,老办法:删除binary等文件)

接下来我们来到菜单的头文件:

 

然后声明。

因为我们要绑定到委托,所以这个委托应该是在MenuSet中就进行绑定:

 之后,我们到子系统里面:

 这样可以进行测试:

 

不过我们如果在这里测试的话,会发现直接就跳转到了另一个世界,并没有打印出这些字。

因为在执行这里之前,我们就已经跳转到另一个世界里了。

调整位置

这里我们需要将调整世界的函数换一个地方,我是希望在所有回调函数执行后在执行这个,所以要把这个红圈里面的ctrl+x换一个地方:

 

所以我们放在打印完毕之后(狗头笑容)

并且底下加一个如果创建失败的情况:

 测试:

 

创建更多的委托和回调函数给菜单:

因为子系统无法将相应的东西返回菜单类

所以我们在子系统里面,做一些自定义委托

多播和动态多播

 代码:

//自定义委托,用于回调到菜单类
//动态多播
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiOnCreateSessionComplete, bool, bWasSuccessful);
//这里使用多播,而不是动态多播
DECLARE_MULTICAST_DELEGATE_TwoParams(FMultiOnFindSessionComplete, const TArray<FOnlineSessionSearchResult>& SessionResult, bool bWasSuccessful);
DECLARE_MULTICAST_DELEGATE_OneParam(FMultiOnJoinSessionComplete, EOnJoinSessionCompleteResult::Type Result);
//动态多播
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiOnDestroySessionComplete, bool, bWasSuccessful);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiOnStartSessionComplete, bool, bWasSuccessful);

委托变量

然后在之前创建委托变量的地方:

 代码:

FMultiOnCreateSessionComplete MultiPlayerOnCreateSessionComplete;
FMultiOnFindSessionComplete  MultiPlayerOnFindSessionComplete;
FMultiOnJoinSessionComplete  MultiPlayerOnJoinSessionComplete;
FMultiOnDestroySessionComplete  MultiPlayerOnDestroySessionComplete;
FMultiOnStartSessionComplete  MultiPlayerOnStartSessionComplete;

回调函数

然后是回调函数:

 代码:

UFUNCTION()
	void onCreateSession(bool bWasSuccessful);
	void onFindSession(const TArray<FOnlineSessionSearchResult>& SessionResult, bool bWasSuccessful);
	void onJoinSession(EOnJoinSessionCompleteResult::Type Result);
	UFUNCTION()
	void onDestroySession(bool bWasSuccessful);
	UFUNCTION()
	void onStartSession(bool bWasSuccessful);

绑定委托和动态函数:

 代码:

MultiPlayerSessionSubsystem->MultiPlayerOnCreateSessionComplete.AddDynamic(this, &ThisClass::onCreateSession);
		MultiPlayerSessionSubsystem->MultiPlayerOnFindSessionComplete.AddUObject(this, &ThisClass::onFindSession);
		MultiPlayerSessionSubsystem->MultiPlayerOnJoinSessionComplete.AddUObject(this, &ThisClass::onJoinSession);
		MultiPlayerSessionSubsystem->MultiPlayerOnDestroySessionComplete.AddDynamic(this, &ThisClass::onDestroySession);
		MultiPlayerSessionSubsystem->MultiPlayerOnStartSessionComplete.AddDynamic(this, &ThisClass::onStartSession);

头文件添加

在菜单的cpp文件中添加头文件:

 代码:

#include "OnlineSessionSettings.h"
#include "Interfaces/OnlineSessionInterface.h"

实现加入按钮:

 

实现寻找函数:

首先在子系统的头文件中,做一个智能指针

 制作完毕之后,在cpp中开始实现这个find函数(这个不是回调函数):

 代码:

void UMultiPlayerSessionGISubsystem::FindSession(int32 findSessionMaxNum)
{
	if (!mySessionInterface.IsValid())
	{
		return;
	}
	//会话接口委托列表添加委托,然后句柄获取
	FindSessionsCompleteDelegateHandle = mySessionInterface->AddOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegate);

	LastSessionSearch = MakeShareable(new FOnlineSessionSearch);
	//配对服务返回的查询的最大数目
	LastSessionSearch->MaxSearchResults = findSessionMaxNum;
	//查询是否用于局域网匹配
	LastSessionSearch->bIsLanQuery = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false;
	//SEARCH_PRESENCE :仅搜索存在会话(值为true/false)
	//QuerySettings :用于查找匹配服务器的查询
	LastSessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
	//获取本地的玩家控制器
	const ULocalPlayer* playerControler = GetWorld()->GetFirstLocalPlayerFromController();
	//搜索与指定匹配的对话
	//GetPreferredUniqueNetId :检索首选的唯一网id。这是为了向后兼容不使用缓存唯一网络id逻辑的游戏
	if (!mySessionInterface->FindSessions(*playerControler->GetPreferredUniqueNetId(), LastSessionSearch.ToSharedRef());)
	{
		//寻找失败
		//清除
		mySessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegateHandle);
		//因为失败了,所以传入的是一个空的数组和false
		MultiPlayerOnFindSessionComplete.Broadcast(TArray<FOnlineSessionSearchResult>(), false);
	}
}

实现寻找的回调函数

 代码:

void UMultiPlayerSessionGISubsystem::onFindSessionComplete(bool bWasSuccessful)
{
	if (mySessionInterface)
	{
		//清除
		mySessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegateHandle);
	}
	//查找的会话数量
	if (LastSessionSearch->SearchResults.Num()<=0)
	{
		//因为失败了,所以传入的是一个空的数组和false
		MultiPlayerOnFindSessionComplete.Broadcast(TArray<FOnlineSessionSearchResult>(), false);
		return;
	}
	//传入菜单
	MultiPlayerOnFindSessionComplete.Broadcast(LastSessionSearch->SearchResults, bWasSuccessful);
}

菜单类的onfind回调函数

 代码:

void UInPluginsMenu::onFindSession(const TArray<FOnlineSessionSearchResult>& SessionResult, bool bWasSuccessful)
{
	if (MultiPlayerSessionSubsystem == nullptr)
	{
		return;
	}
	//遍历找到的会话数组
	for (auto Result : SessionResult)
	{
		FString SettingsValue;
		//获取定义会话设置的key对应的value赋予SettingsValue
		Result.Session.SessionSettings.Get(FName(TEXT("MatchType")), SettingsValue);
		if (SettingsValue ==MatchType)//判断SettingsValue和MatchType是否一致,即找到了会话
		{
			//加入会话(在这里)
			MultiPlayerSessionSubsystem->JoinSession(Result);
			return;
		}
	}
}

加入会话函数:

 代码:

void UMultiPlayerSessionGISubsystem::JoinSession(const FOnlineSessionSearchResult& SessionResult)
{
	if (!mySessionInterface.IsValid())
	{
		//无效情况下
		//广播到所有绑定对象:UnknownError
		MultiPlayerOnJoinSessionComplete.Broadcast(EOnJoinSessionCompleteResult::UnknownError);
		return;
	}
	//获取句柄和委托列表添加
	JoinSessionCompleteDelegateHandle =mySessionInterface->AddOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegate);
	//加入会话
	const ULocalPlayer* localPlayer = GetWorld()->GetFirstLocalPlayerFromController();
	if (!mySessionInterface->JoinSession(*localPlayer->GetPreferredUniqueNetId(), NAME_GameSession, SessionResult))
	{
		//加入失败
		//清除委托
		mySessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegateHandle);
		//广播回调函数为未知错误
		MultiPlayerOnJoinSessionComplete.Broadcast(EOnJoinSessionCompleteResult::UnknownError);
	}
}

加入会话的回调函数:

 代码:

void UMultiPlayerSessionGISubsystem::onJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
	if (mySessionInterface)
	{
		//执行完成,所以清除一下
		mySessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegateHandle);
	}
	//广播到菜单的onjoinsession
	MultiPlayerOnJoinSessionComplete.Broadcast(Result);
}

菜单内的加入回调函数

菜单cpp中添加头文件:

#include "OnlineSubsystem.h"	

然后

 代码:

void UInPluginsMenu::onJoinSession(EOnJoinSessionCompleteResult::Type Result)
{
	IOnlineSubsystem* onlineSubsystem = IOnlineSubsystem::Get();
	if (onlineSubsystem)
	{
		//临时接口
		IOnlineSessionPtr TempMySessionInterface = onlineSubsystem->GetSessionInterface();
		if (TempMySessionInterface.IsValid())
		{
			//给予tempAddress地址
			FString tempAddress;
			TempMySessionInterface->GetResolvedConnectString(NAME_GameSession,tempAddress);
			//从游戏实例里面获取本地的第一个玩家控制器
			APlayerController* playerControler = GetGameInstance()->GetFirstLocalPlayerController();
			if (playerControler)
			{
				//世界跳跃
				//旅行到不同的地图或IP地址。
				//在执行任何操作之前调用PreClientTravel事件。
				playerControler->ClientTravel(tempAddress, ETravelType::TRAVEL_Absolute);
			}
		}
	}
}

防止可能出现的意外进行的添加:

在菜单头文件添加这个头文件:

 代码1:

#include "Interfaces/OnlineSessionInterface.h"

在子系统创建会话这里加这两个:

 代码2:

//用于防止不同的构建在搜索期间看到彼此
	LastSessionSettings->BuildUniqueId = 1;
	//支持api则使用
	LastSessionSettings->bUseLobbiesIfAvailable = true;

测试结果是可以连接的(不过创建会话的时候需要等待一会儿,让steam完全连接(加入会话也是一样))

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。