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

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

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);//因为失败了,所以传入的是一个空的数组和falseMultiPlayerOnFindSessionComplete.Broadcast(TArray<FOnlineSessionSearchResult>(), false);}
}

实现寻找的回调函数

 代码:

void UMultiPlayerSessionGISubsystem::onFindSessionComplete(bool bWasSuccessful)
{if (mySessionInterface){//清除mySessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegateHandle);}//查找的会话数量if (LastSessionSearch->SearchResults.Num()<=0){//因为失败了,所以传入的是一个空的数组和falseMultiPlayerOnFindSessionComplete.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赋予SettingsValueResult.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()){//无效情况下//广播到所有绑定对象:UnknownErrorMultiPlayerOnJoinSessionComplete.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);}//广播到菜单的onjoinsessionMultiPlayerOnJoinSessionComplete.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完全连接(加入会话也是一样))