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代码实现)
💥 💥 💞 💞 欢迎来到本博客 ❤️ ❤️ 💥 💥 🏆 博主优势: 🌞 🌞 🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 …...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
