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代码实现)
💥 💥 💞 💞 欢迎来到本博客 ❤️ ❤️ 💥 💥 🏆 博主优势: 🌞 🌞 🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 …...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
Sklearn 机器学习 缺失值处理 获取填充失值的统计值
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...
