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完全连接(加入会话也是一样))
相关文章:
UE4/5多人游戏详解(七、自定义委托,实现寻找会话和加入会话的函数,通过Steam进行两台电脑的联机)
目录 可能出现问题(在六部分的测试可能无法连接的问题【在末尾加上了,怕有人没看见在这里写一下】) 自定义委托 调整位置 创建更多的委托和回调函数给菜单: 多播和动态多播 代码: 委托变量 代码: 回…...
【数据库多表操作】sql语句基础及进阶
常用数据库: 数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,它是长期存储在计算机内、有组织、有结构的数据集合。数据库是信息系统的核心部分,现代软件系统中大量采用了数据库管理系统(DBM…...
DPDK和RDMA的区别
网络的发展好像在各方面都是滞后于计算和存储,时延方面也不例外,网络传输时延高,逐渐成为了数据中心高性能的瓶颈。因为传统两个节点间传输数据的网络路径上有大量的内存拷贝,导致网络传输效率低下,网络数据包的收发处…...
体验 Google Bard
环境 windows 10 64bitGoogle Bardpython 3.8 简介 本篇介绍一个开源的 Google 聊天机器人Bard 的 API 逆向工程,使用它,可以免费的使用 Bard 服务,项目地址:https://github.com/acheong08/Bard 安装及使用 通过 pip 来安装 pip &…...
MITA触摸屏维修WP4053米塔工控机控制屏维修
MITA-TEKNIK米塔触摸屏维修工控机工控屏控制器维修DISPLAY 2COM全系列型号 Mita-Teknik触摸屏维修常见故障:上电无显示,运行报故障,无法与电脑通讯,触摸无反应,触控板破裂,触摸玻璃,上电黑屏&a…...
Nacos简介 安装 配置
简介 什么是注册中心 注册中心在微服务项目中扮演着非常重要的角色,是微服务架构中的纽带,类似于通讯录,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,…...
五、MyBatis各种查询功能
MyBatis的各种查询功能 如果查询出的数据只有一条,可以通过 实体类对象接收List集合接收Map集合接收 如果查询出的数据有多条,一定不能用实体对象接收,会抛TooManyResultsException,可以通过 实体类类型的List集合接收Map类型…...
uni-app——picker组件的用法、时间、日期、地区选择器等
1、uniapp–picker组件 <template><view class"signUp"><view class"signUp_dv1"><u-form :model"form" ref"uForm" label-width"95px"><u-form-item label"日期" :required"tr…...
什么情况需要考虑 mysql 分表
最近看到公司的其中一个数据库用户表每个月都要几百万的新用户数据增加,目前单表已经是两千多万了。所以找了 DBA 讨论,发现以前学的知识,以及网上的一些资料其实说的并不是很正确,比如 mysql 单表不建议超过一千万,我…...
系统架构师02-架构设计 20分
1.架构基本概念 *质量属性效用树:是对系统质量属性进行识别和优先级排序的重要工具 。 包括: 性能:效率指标,处理任务所需时间或单位时间内的处理量。 可用性: 可靠性: 容错:出现错误后人能保…...
【python视图3】networkx图操作示例
一、说明 根据定义,图是节点(顶点)以及已识别的节点对(称为边、链接等)的集合。在 NetworkX 中,节点可以是任何可哈希对象,例如文本字符串、图像、XML 对象、另一个图形、自定义节点对象等。 如…...
网络地址转换应用
如图所示,企业使用一台AR 路由器作为出口设备,路由器配置NAT Outbound为私网用户提供访问Internet服务,同时配置NAT Server将私网WEB服务器发布到公网上,对外网用户提供服务。运营商仅为该单位分配了一个公网IP,此地址既作为AR出接口的IP地址,也作为NAT Outbound和NAT Se…...
强化学习-Double DQN、竞争网络结构和Rainbow(第4章)
来源书籍: TENSORFLOW REINFORCEMENT LEARNING QUICK START GUIDE 《TensorFlow强化学习快速入门指南-使用Python动手搭建自学习的智能体》 著者:[美]考希克巴拉克里希南(Kaushik Balakrishnan) 译者:赵卫东 出版…...
Unity 性能优化锦集
Unity作为一款主流的游戏开发引擎,不仅提供了强大的编辑器和开发工具,还可以让开发者轻松地实现高质量的3D游戏。但是,随着游戏规模的不断扩大和玩家需求的增加,游戏的性能问题也变得越来越重要。因此,在使用Unity进行…...
JS之Map的基本使用
一、Map的基本API 创建: const map new Map()插入:map.set("name", "郑建")读取:map.get("name")判断:map.has("name")删除:map.delete大小:map.size遍历&#…...
音视频八股文(6)-- ffmpeg大体介绍和内存模型
播放器框架 常用音视频术语 • 容器/文件(Conainer/File):即特定格式的多媒体文件, 比如mp4、flv、mkv等。 • 媒体流(Stream):表示时间轴上的一段连续数据,如一 段声音…...
4.25~~~~~
接着之前PE文件结构的预习 DOS 定位到NT 怎么操作的? 用的是e_lfanew,然后是相对于文件头的偏移量(也就是raw表示方法) 现在有个问题,为什么e_lfanew 这个变量不直接存储PE头 的绝对地址呢? 比如说&…...
Android 9.0 系统设置显示主菜单添加屏幕旋转菜单实现旋转屏幕功能
1.前言 在android9.0的系统rom定制化开发中,在对系统设置进行定制开发中,有产品需求要求增加旋转屏幕功能的菜单,就是在点击旋转屏幕菜单后弹窗显示旋转0度,旋转 90度,旋转180度,旋转270度针对不同分辨率的无重力感应的大屏设备的屏幕旋转功能的实现,接下来就来分析实现…...
Python数据结构与算法-欧几里算法(p95)
一、欧几里算法原理 欧几里得公式 欧几里得算法:gcd(a,b) gcd(b, a mod b) ; mod是指模,即a/b取余数。 运算示例: gcd(60,21) gcd(21,18) gcd(18,3)gcd(3,0) 证明略 最大公约数-欧几里得求解 (…...
【故障诊断】用于轴承故障诊断的性能增强时变形态滤波方法及用于轴承断层特征提取的增强数学形态算子研究(Matlab代码实现)
💥 💥 💞 💞 欢迎来到本博客 ❤️ ❤️ 💥 💥 🏆 博主优势: 🌞 🌞 🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 …...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
