您现在的位置是:首页 >技术教程 >UE4/5多人游戏详解(七、自定义委托,实现寻找会话和加入会话的函数,通过Steam进行两台电脑的联机)网站首页技术教程
UE4/5多人游戏详解(七、自定义委托,实现寻找会话和加入会话的函数,通过Steam进行两台电脑的联机)
简介UE4/5多人游戏详解(七、自定义委托,实现寻找会话和加入会话的函数,通过Steam进行两台电脑的联机)
目录
可能出现问题(在六部分的测试可能无法连接的问题【在末尾加上了,怕有人没看见在这里写一下】)
可能出现问题(在六部分的测试可能无法连接的问题【在末尾加上了,怕有人没看见在这里写一下】)
打包测试发现在连接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完全连接(加入会话也是一样))
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。