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

news2024/11/24 0:12:33

目录

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

自定义委托

调整位置

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

多播和动态多播

 代码:

委托变量

 代码:

回调函数

 代码:

绑定委托和动态函数:

 代码:

头文件添加

 代码:

实现加入按钮:

实现寻找函数:

 代码:

实现寻找的回调函数

 代码:

菜单类的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完全连接(加入会话也是一样))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/449829.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

( “树” 之 BST) 109. 有序链表转换二叉搜索树 ——【Leetcode每日一题】

二叉查找树&#xff08;BST&#xff09;&#xff1a;根节点大于等于左子树所有节点&#xff0c;小于等于右子树所有节点。 二叉查找树中序遍历有序。 109. 有序链表转换二叉搜索树 给定一个单链表的头节点 head &#xff0c;其中的元素 按升序排序 &#xff0c;将其转换为高度…

Linux: 进程间通信机制

文章目录 1. 前言2. 进程间通信机制2.1 管道2.1.1 匿名管道2.1.2 popen() 和 pclose()2.1.3 命名管道 FIFO 2.2 消息队列2.3 共享内存2.4 信号量2.5 网络套接字2.6 UNIX套接字2.7 信号 3. 参考资料 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给…

基于格密码的LWE问题

LWE LWE问题&#xff0c; Learning With Errors&#xff0c;带有安全性证明的第一个版本是由Oded Regev 在2005年提出&#xff0c;Kawachi等给出了效率的改进&#xff0c;接着一些效率方面非常重要的改进由Peikert等提出。 格理论知识 格密码学&#xff08;Lattice-based Cr…

PTA L1-093 猜帽子游戏 (15 分)

宝宝们在一起玩一个猜帽子游戏。每人头上被扣了一顶帽子&#xff0c;有的是黑色的&#xff0c;有的是黄色的。每个人可以看到别人头上的帽子&#xff0c;但是看不到自己的。游戏开始后&#xff0c;每个人可以猜自己头上的帽子是什么颜色&#xff0c;或者可以弃权不猜。如果没有…

机器学习算法 决策树

文章目录 一、决策树的原理二、决策树的构建2.1 ID3算法构建决策树2.2 C4.5 算法树的构建2.3 CART 树的创建 三、决策树的优缺点 一、决策树的原理 决策树&#xff08;Decision Tree&#xff09;是一种非参数的有监督学习方法&#xff0c;它能够从一系列有特征和标签的数据中总…

NDK OpenCV人脸定位

NDK系列之OpenCV人脸定位技术实战&#xff0c;本节主要是通过OpenCV C库&#xff0c;实现识别人脸定位&#xff0c;并对识别到的人脸画面增加红框显示。 实现效果&#xff1a; 实现逻辑&#xff1a; 1.初始化CameraX&#xff0c;绑定图片分析器ImageAnalysis&#xff0c;监听…

7.队列算法

算法&#xff1a;队列算法 队列是一种抽象的数据结构&#xff0c;有点类似于Stacks。与堆栈不同&#xff0c;队列的两端都是开放的。一端始终用于插入数据(入队)&#xff0c;另一端用于删除数据(出队)。队列遵循先进先出方法&#xff0c;即首先访问先存储的数据项。 一个真实的…

【C++初阶】类与对象(上)

一.什么是类&#xff0c;什么是对象 我们可以形象的把类比作是一个房子的设计图纸&#xff0c;而对象就是根据设计图纸设计出来的房子。 由设计图纸到房子的过程&#xff0c;我们称之为类的实例化。 C兼容C的&#xff0c;所以C中的结构体在C中也能用&#xff0c;但是C把结构体升…

rust教程 第一章 —— 初识rust

文章目录 前言一、Rust简介二、安装Rust编译器三、第一个Rust程序四、 IDE环境五、初识包管理六、总结 前言 本系列教程目录可查看这里&#xff1a;Rust教程目录 近些年来不断有新的语言崛起&#xff0c;比如当下非常火的go语言&#xff0c;不过相比于C&#xff0c;go语言确实…

C++类和对象 (3)

类和对象 1. 类的6个默认成员函数2. 构造函数2.1. 概念&#xff08;问题提出&#xff09;2.2. 特性 3.析构函数3.1. 概念3.2.特性 1. 类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在…

使用OpenFeign实现接口访问

1. 引言 在微服务横行的年代&#xff0c;后端根据业务的不一样分成了很多单独运行的服务&#xff0c;比如在物联网中&#xff0c;根据业务拆分为定时服务、设备控制等服务。当前端想控制设备时&#xff0c;其请求首先到其对应的后端服务&#xff0c;后端服务再调用设备控制服务…

Vue+Vant封装通用模态框单选框组件

前言 我们知道&#xff0c;在vant组件中提供的组件往往是比较基础的&#xff0c;能够满足基本需求。但是我们想实现ui设计的一些比较丰富效果的组件&#xff0c;需要自己去实现&#xff0c;且当项目中多次用到的时候&#xff0c;我们将以组件化的思想将其封装起来&#xff0c;…

Node.js -- 使用Express写接口

1.创建基本的服务器 //导入express const express require(express) //创建服务器实例 const app express() //调用app.listen方法&#xff0c;指定端口号并启动web服务器 app.listen(80,function(){console.log(Express server running at http://127.0.0.1) })2. 创建API路…

路由交换综合实验

拓扑结构&#xff1a; 要求 1、R6为网络运营商&#xff08;ISP&#xff09;&#xff0c;接口IP地址均为公有地址&#xff1b;该设备只能配置IP地址&#xff0c;之后不能在对其进行任何配置&#xff1b; 2、R1~R5为局域网&#xff0c;私有IP地址192.168.1.0/24&#xff0c;请合…

真题详解(UML图)-软件设计(五十五)

真题详解&#xff08;计算机知识&#xff09;-软件设计&#xff08;五十四)https://blog.csdn.net/ke1ying/article/details/130278265 组织域名&#xff1a; com商业组织 edu教育组织 gov政府组织 net主要网络支持中心 mil军事部门 Int国际组织 2、时间复杂度 O&#…

写一个自己的命令行解释器

写一个自己的命令行解释器 当我点开xshell运行服务器的时候bash就被加载到了内存中&#xff0c;此后我在bash上执行的所有程序都是作为bash的子进程。在bash这个进程内创建子进程&#xff0c;并让子进程去执行全新的代码&#xff0c;这不就是程序替换吗&#xff1f; 所以我们…

腾讯云4核8g服务器支持多少人在线使用?

腾讯云轻量4核8G12M轻量应用服务器支持多少人同时在线&#xff1f;通用型-4核8G-180G-2000G&#xff0c;2000GB月流量&#xff0c;系统盘为180GB SSD盘&#xff0c;12M公网带宽&#xff0c;下载速度峰值为1536KB/s&#xff0c;即1.5M/秒&#xff0c;假设网站内页平均大小为60KB…

【Unity入门】17.脚本访问父子结点

【Unity入门】脚本访问父子结点 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity入门系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;父级节点 &#xff08;1&#xff09;访问父级节点 父子关系我们并不陌生&#xff0c;在cocos中常用node:get…

单链表的实现

链表的概念与结构 链表与我们通讯录中的顺序表是不同的&#xff0c;顺序表的空间是连续的&#xff0c;像数组一样可以通过下标访问。而链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。即&#xff1a;链表…

数据结构笔记:二叉树的遍历与技巧

引言 本篇是最近有遇到过的一个题目&#xff0c;关于二叉树的遍历&#xff0c;正好有一些经验与技巧&#xff0c;所以开一篇帖子记录一下。 二叉树遍历介绍 遍历是数据结构中常见的操作&#xff0c;主要是将所有元素都访问一遍。对于线性结构来说&#xff0c;遍历分为两种&a…