《UE5_C++多人TPS完整教程》学习笔记22 ——《P23 记录加入的玩家(Couting Incoming Players)》


本文为B站系列教学视频 《UE5_C++多人TPS完整教程》 —— 《P23 记录加入的玩家(Couting Incoming Players)》 的学习笔记,该系列教学视频为 Udemy 课程 《Unreal Engine 5 C++ Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者)为 游戏引擎能吃么。


文章目录

  • P23 记录加入的玩家
  • 23.1 游戏模式和游戏状态
  • 23.2 追踪加入或离开游戏的玩家
  • 23.3 使用 Lobby 游戏模式
  • 23.4 进行测试
  • 23.5 Summary


P23 记录加入的玩家

本节课将创建一个游戏模式,以便追踪(Track)加入游戏会话的玩家、打印游戏人数,之后我们就可以利用统计的人数来决定是否要从关卡 “Lobby” 过渡(Transition)到实际的匹配中。
在这里插入图片描述


23.1 游戏模式和游戏状态

  1. 在多人游戏中,两个重要的类分别是游戏模式类和游戏状态类:
  • 游戏模式规定了游戏规则,这涉及了很多方面,如何时将玩家移动至关卡中、选择出生位等。游戏模式有几个继承的虚拟函数,可以在玩家加入或离开会话时进行追踪,例如每当玩家加入游戏时都会调用 “PostLogin()” 访问玩家的控制器;当玩家离开时调用 “Logout()” 函数。
  • 客户端可以监控游戏状态,游戏状态将保存游戏的状态信息,而非特定的单个玩家的信息。游戏状态包含玩家状态数组,玩家状态类中保存了特定的单个玩家的信息,比如得分和胜利次数等。
  • 游戏模式类可以访问游戏状态类的玩家状态数组,通过查看该数组的大小可以得出玩家的个数。
    在这里插入图片描述

    两个主要类负责处理进行中游戏的相关信息:Game Mode 和 Game State。
    即使最开放的游戏也拥有基础规则,而这些规则构成了 Game Mode。在最基础的层面上,这些规则包括:

    • 出现的玩家和观众数量,以及允许的玩家和观众最大数量。
    • 玩家进入游戏的方式,可包含选择生成地点和其他生成/重生成行为的规则。
    • 游戏是否可以暂停,以及如何处理游戏暂停。
    • 关卡之间的过渡,包括游戏是否以动画模式开场。

    基于规则的事件在游戏中发生,需要进行追踪并和所有玩家共享时,信息将通过 Game State 进行存储和同步。这些信息包括:

    • 游戏已运行的时间(包括本地玩家加入前的运行时间)。
    • 每个个体玩家加入游戏的时间和玩家的当前状态。
    • 当前 Game Mode 的基类。
    • 游戏是否已开始。

    Game Modes
    特定的基础(如进行游戏所需要的玩家数量,或玩家加入游戏的方法)在多种类型的游戏中具有共通性。可根据开发的特定游戏进行无穷无尽的规则变化。无论规则如何,Game Modes 的任务都是定义和实现规则。Game Modes 当前常用的基类有两个。
    4.14 版本中加入了 AGameModeBase,这是所有 Game Mode 的基类,是经典的 AGameMode 简化版本。AGameMode 是 4.14 版本之前的基类,仍然保留,功能 如旧,但现在是 AGameModeBase 的子类。由于其比赛状态概念的实现,AGameMode 更适用于标准游戏类型(如多人射击游戏)。AGameModeBase 简洁高效,是新代码项目中包含的全新默认游戏模式。


    Game State
    Game State 负责启用客户端监控游戏状态。从概念上而言,Game State 应该管理所有已连接客户端已知的信息(特定于 Game Mode 但不特定于任何个体玩家)。它能够追踪游戏层面的属性,如已连接玩家的列表、夺旗游戏中的团队得分、开放世界游戏中已完成的任务,等等。
    Game State 并非追踪玩家特有内容(如夺旗比赛中特定玩家为团队获得的分数)的最佳之处,因为它们由 Player State 更清晰地处理。整体而言,Game State 应该追踪游戏进程中变化的属性。这些属性与所有人皆相关,且所有人可见。Game Mode 只存在于服务器上,而 Game State 存在于服务器上且会被复制到所有客户端,保持所有已连接机器的游戏进程更新。


    —— 虚幻引擎官方文档《Game Mode 和 Game State》

  1. 打开虚幻引擎,在内容浏览器中展开 “C++类/MenuSystem”,添加游戏模式基础(Game mode base)C++ 类,命名为 “LobbyGameMode”,选择模块为我们上节课新建的插件 “MenuSystem (Runtime)”。
    在这里插入图片描述
    在这里插入图片描述
  2. 点击 “创建类” 按钮,VS 中出现弹窗,选择 “全部重新加载(A)”。
    在这里插入图片描述

23.2 追踪加入或离开游戏的玩家

  1. 在 “LobbyGameMode.h” 中添加头文件 “GameFramework/GameStateBase.h” 和 “GameFramework/PlayerState.h”,避免出现错误 “使用了未定义类型 AGameStateBase” 和 “使用了未定义类型 APlayerState。接着,声明重写 “virtual void PostLogin()” 和 “virtual void Logout()”。

    // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
    #include "GameFramework/GameModeBase.h"/* P23 记录加入的玩家(Couting Incoming Players)*/
    #include "GameFramework/GameStateBase.h"
    #include "GameFramework/PlayerState.h"
    /* P23 记录加入的玩家(Couting Incoming Players)*/#include "LobbyGameMode.generated.h"/*** */
    UCLASS()
    class MENUSYSTEM_API ALobbyGameMode : public AGameModeBase
    {GENERATED_BODY()/* P23 记录加入的玩家(Couting Incoming Players)*/
    public:// 成功登录后调用。这是首个在 PlayerController 上安全调用复制函数之处。// OnPostLogin 可在蓝图中实现,以添加额外的逻辑。virtual void PostLogin(APlayerController* NewPlayer) override;	// 重写 virtual void PostLogin()// 玩家离开游戏或被摧毁时调用。可实现 OnLogout 执行蓝图逻辑。virtual void Logout(AController* Exiting) override;				// virtual void Logout()
    /* P23 记录加入的玩家(Couting Incoming Players)*/
    };
    
  2. 在 “LobbyGameMode.cpp” 中实现“virtual void PostLogin()” 和 “virtual void Logout()” 的覆写。如果 “GameState” 和 “PlayerState” 标红且错误提示为 “不允许指针指向不完整的类类型 AGameStateBase” 和 “不允许指针指向不完整的类类型 APlayerState”,则添加头文件 “GameFramework/GameStateBase.h” 和 “GameFramework/PlayerState.h

    // Fill out your copyright notice in the Description page of Project Settings.#include "LobbyGameMode.h"/* P23 记录加入的玩家(Couting Incoming Players)*/
    void ALobbyGameMode::PostLogin(APlayerController* NewPlayer)
    {Super::PostLogin(NewPlayer);if (GameState) {int32 NumberOfPlayers = GameState.Get()->PlayerArray.Num();if (GEngine) {GEngine->AddOnScreenDebugMessage(	// 添加调试信息到屏幕上1,				// Key 不是 -1 时,则更新现有消息60.f,			// 调试信息的显示时间FColor::Red,	// 字体颜色:黄色FString::Printf(TEXT("Players in game: %d!"), NumberOfPlayers)	// 打印玩家人数);APlayerState* PlayerState = NewPlayer->GetPlayerState<APlayerState>();if (PlayerState) {FString PlayerName = PlayerState->GetPlayerName();GEngine->AddOnScreenDebugMessage(	// 添加调试信息到屏幕上2,				// Key 不是 -1 时,则更新现有消息60.f,			// 调试信息的显示时间FColor::Cyan,	// 字体颜色:蓝绿色FString::Printf(TEXT("%s has joined the game!"), *PlayerName)	// 打印进入游戏的玩家昵称);}	}}
    }void ALobbyGameMode::Logout(AController* Exiting)
    {Super::Logout(Exiting);APlayerState* PlayerState = Exiting->GetPlayerState<APlayerState>();if (PlayerState) {int32 NumberOfPlayers = GameState.Get()->PlayerArray.Num();GEngine->AddOnScreenDebugMessage(	// 添加调试信息到屏幕上1,				// Key 不是 -1 时,则更新现有消息60.f,			// 调试信息的显示时间FColor::Red,	// 字体颜色:红色FString::Printf(TEXT("Players in game: %d!"), NumberOfPlayers - 1)	// 打印玩家人数,// 此时 PlayerArray.Num() 还未更新,这里进行减 1 操作只是为了方便测试时显示正确的人数,在实际项目中不会如此操作);FString PlayerName = PlayerState->GetPlayerName();GEngine->AddOnScreenDebugMessage(	// 添加调试信息到屏幕上2,				// Key 不是 -1 时,则更新现有消息60.f,			// 调试信息的显示时间FColor::Cyan,	// 字体颜色:蓝绿色FString::Printf(TEXT("%s has exited the game!"), *PlayerName)	// 打印进入游戏的玩家昵称);}}
    /* P23 记录加入的玩家(Couting Incoming Players)*/
    

    这里可以复习一下函数 “AddOnScreenDebugMessage()” 第一个入参 “int32 Key” 取不同值的含义。这里 打印玩家人数打印进入或退出游戏的玩家昵称 设置了不同的 “Key” 是为了都能在屏幕上显示,如果设置为 -1 则 玩家人数消息 会被 进入或退出游戏的玩家昵称消息 覆盖。

    调用全局变量 GEngine 指针调用函数 AddOnScreenDebugMessage节点,进行屏幕输出。

    void AddOnScreenDebugMessage {int32 Key,float TimeToDisplay,FColor Di splayColor,const FString & DebugMessage,bool bNewerOnTop,const FVector2D & TextScale
    }
    
    • Key = -1 时,则添加新的消息,不会覆盖旧有消息(当 Key = -1 时,bNewerOnTop 有效,直接添加到队列最上层)
    • Key != -1 时,则更新现有消息,效率更高。

    —— 《虚幻引擎基础入门(C++) — 【日志输出篇 03】》

  3. 在 “MultiplayerSessionsSubsystem.cpp” 的 “CreateSession()” 函数中加入一条会话设置的代码行,用来设置唯一构建标识,防止不同的构建在搜索过程中可以互相搜索到。

    void UMultiplayerSessionsSubsystem::CreateSession(int32 NumpublicConnections, FString MatchType)
    {...// FOnlineSessionSettings 在头文件 "OnlineSessionSettings.h" 中LastSessionSettings = MakeShareable(new FOnlineSessionSettings());	// 创建会话设置,利用函数 MakeShareable 初始化// 会话设置成员变量参阅及含义:https://docs.unrealengine.com/5.0/en-US/API/Plugins/OnlineSubsystem/FOnlineSessionSettings/LastSessionSettings->bIsLANMatch = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false;	// 会话设置:如果找到的子系统名称为 “NULL”,则使用 LAN 连接,否则不使用LastSessionSettings->NumPublicConnections = NumpublicConnections;	// 会话设置:设置最大公共连接数为函数输入变量 NumpublicConnections.../* P23 记录加入的玩家(Couting Incoming Players)*/LastSessionSettings->BuildUniqueId = 1;								// 会话设置:设置唯一构建标识,防止不同的构建在搜索过程中可以互相搜索到。/* P23 记录加入的玩家(Couting Incoming Players)*/...}
    
  4. 在 “MenuSystem\Config” 目录下打开 “DefaultGame.ini”,设置最大玩家数为 100。创建会话中设置最大公共连接数 “NumPublicConnections”区别开,设置最大公共连接数是指定加入会话的最大连接数,而设置最大玩家数是指定连接到游戏项目最大人数。

    [/Script/EngineSettings.GeneralProjectSettings]
    ProjectID=6A5F83AB4DEB75FB9BB586AC8DE40CDA
    ProjectName=Third Person Game Template[StartupActions]
    bAddPacks=True
    InsertPack=(PackSource="StarterContent.upack",PackName="StarterContent")[/Script/Engine.GameSession]
    MaxPlayers=100	
    
  5. (选做)注释掉 “MenuSystemCharacter.cpp” 获取在线子系统并打印在线子系统名称到屏幕上的代码,并删除 “BP_ThirdPersonCharacter” 角色蓝图中按下数字键 “1” 和 “2” 对应的事件。

    // AMenuSystemCharacterAMenuSystemCharacter::AMenuSystemCharacter() :	// 为委托绑定回调函数CreateSessionCompleteDelegate(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateSessionComplete)),FindSessionsCompleteDelegate(FOnFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnFindSessionsComplete)),JoinSessionCompleteDelegate(FOnJoinSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnJoinSessionComplete))
    {.../* P23 记录加入的玩家(Couting Incoming Players)*///IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get();			// 获取当前的在线子系统指针//if (OnlineSubsystem) {													// 如果当前在线子系统有效//	OnlineSessionInterface = OnlineSubsystem->GetSessionInterface();	// 获取会话接口智能指针//	if (GEngine) {//		GEngine->AddOnScreenDebugMessage(	// 添加调试信息到屏幕上//			-1,				// 使用 -1 不会覆盖前面的调试信息//			15.f,			// 调试信息的显示时间//			FColor::Blue,	// 字体颜色//			FString::Printf(TEXT("Found subsystem %s!"),//				*OnlineSubsystem->GetSubsystemName().ToString())	// 打印在线子系统的名称//		);//	}//}/* P23 记录加入的玩家(Couting Incoming Players)*/}
    

    在这里插入图片描述


23.3 使用 Lobby 游戏模式

  1. 打开虚幻引擎,在内容浏览器中目录 “/内容/ThirdPerson/Blueprints/” 下新建 “LobbyGameMode” 蓝图类,命名为 “BP_LobbyGameMode”。
    在这里插入图片描述
    在这里插入图片描述
  2. 双击 “BP_LobbyGameMode”,进入蓝图编辑器,在右侧 “细节” 面板 “类” 选项卡下设置 “默认 pawn 类” 为 “BP_ThirdPersonCharacter”,编译、保存。
    在这里插入图片描述
  3. 在目录 “/内容/ThirdPerson/Maps/” 下打开地图 “Lobby”,在右下方 “世界场景设置” 面板设置 “游戏模式” 为“BP_LobbyGameMode”,保存。
    在这里插入图片描述

    如果没有看到右下方的 “世界场景设置” 面板,可以点击工具栏右侧 “项目和编辑器设置” 按钮(虚幻引擎窗口右上方),在下拉菜单栏中选中 “世界场景设置”。
    在这里插入图片描述


23.4 进行测试

  1. 将项目打包之后发送到另一台设备上。在设备 1 上运行游戏(保证 Steam 已经运行),点击 “Host” 按钮,当前关卡跳转至 “Lobby”,且左上角显示会话创建成功消息。
    在这里插入图片描述
    在这里插入图片描述

  2. 在设备 2 上运行游戏(保证 Steam 已经运行且登录的账户与设备1 上登录的账号不同),点击 “Join” 按钮,当前关卡跳转至 “Lobby”,并且可以看到有两个玩家存在,说明设备 2 成功找到并加入到了设备 1 创建的会话中,但是设备 2 屏幕左上角没有显示玩家人数和加入游戏信息,而创建会话的设备 1 上有显示。
    在这里插入图片描述
    在这里插入图片描述

  3. 在设备 2 上退出游戏,可以看到设备 1 屏幕左上角显示了设备 2 上的玩家离开游戏的消息,玩家人数也发生了变化。
    在这里插入图片描述


23.5 Summary

本节课了解了游戏模式和游戏状态的基本概念,并在虚幻引擎创建了游戏模式基础(Game mode base)C++ 类 “LobbyGameMode”。接着我们在 “LobbyGameMode.cpp” 中实现“virtual void PostLogin()” 和 “virtual void Logout()” 的覆写,以便能够打印在线玩家人数以及玩家加入或退出游戏的消息。为了能够使用这个游戏模式,我们以 “LobbyGameMode” 为父类新建了蓝图类 “BP_LobbyGameMode”,设置蓝图类的 “默认 pawn 类” 为 “BP_ThirdPersonCharacter”,并将地图 “Lobby” 的 “游戏模式” 设置为“BP_LobbyGameMode”。最后,我们在两台设备上以玩家加入和退出游戏的方式进行来了测试,玩家人数以及玩家加入和退出游戏的消息都能正常显示。
在这里插入图片描述
23.2 追踪加入或离开游戏的玩家步骤 2 中使用函数 AddOnScreenDebugMessage() 进行屏幕消息输出时,若函数第一个入参 “int32 Key” 为 -1 ,则添加新的消息,不会覆盖旧有消息(当 Key 为 -1 时,bNewerOnTop 有效,直接添加到队列最上层),若 Key 不为 -1 ,则更新现有消息。这里打印 玩家人数 和 打印进入或退出游戏的玩家昵称 设置了不同的 “Key” 是为了都能在屏幕上显示,如果设置为 -1 则 玩家人数消息 会被 进入或退出游戏的玩家昵称消息 覆盖。


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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SaaS智慧校园管理平台全套源码,支持二次开发,项目使用

什么是电子班牌系统&#xff1f; 电子班牌用来显示班级信息&#xff0c;班级活动信息以及学校的通知信息。信息内容为文字、图片、视频、FLASH等&#xff0c;为学生和老师提供新颖的师生交流及校园服务平台。融合了多媒体信息发布、家校互通、物联控制、教务管理、日常办公等一…

c++类和对象新手保姆级上手教学(下)

目录 前言&#xff1a; 初始化列表&#xff1a; explicit关键字&#xff1a; static成员&#xff1a; 友元函数&#xff1a; 友元类&#xff1a; 内部类&#xff1a; 匿名对象&#xff1a; 前言&#xff1a; 类和对象下篇中剩余的部分较为简单易理解&#xff0c;认真记住…

从大厂裸辞后成为自由职业者,一年后我怎么样了?

深耕技术领域7年&#xff0c;前前后后也做过不少副业&#xff0c;最近我一直在思考什么副业才是对自己有价值的&#xff0c;可持续的&#xff0c;甚至是可增长的。 22年我所在团队的一个项目解散了&#xff0c;领导问我想拿钱走还是转岗&#xff0c;想想自己也在这个公司干了5…

猫头虎分享已解决Bug || TypeError: unhashable type: ‘slice‘

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

Java毕业设计-基于ssm的校园二手交易管理系统-第67期

获取源码资料&#xff0c;请移步从戎源码网&#xff1a;从戎源码网_专业的计算机毕业设计网站 项目介绍 基于ssm的校园二手交易管理系统&#xff1a;前端jsp、jquery&#xff0c;后端 springmvc、spring、mybatis&#xff0c;集成商品管理、订单管理、销售管理、采购管理、购…

【ACW 导入数据库脚本】

版本: 1.2.2-JDK17-SNAPSHOT 项目地址&#xff1a;wu-smart-acw 演示地址&#xff1a;演示地址 admin/admin docker启动 docker run -d -it -p 18080:18080 -e MAIN_DB_HOSTcloud-mysql:3306 -e MAIN_DB_USERNAMEroot -e MAIN_PASSWORDmysql --name wu-smart-acw-server …

猫头虎分享已解决Bug || 容器编排问题:OrchestrationFailure, ContainerManagementError

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

【CANoe示例分析】EthernetTC8Test

1、工程路径 C:\Users\Public\Documents\Vector\CANoe\Sample Configurations 15.3.89\Ethernet\Test\EthernetTC8Test 在CANoe软件上也可以打开此工程:File|Help|Sample Configurations|Ethernet Testing|TC8Test(Ethernet) 2、示例目的 TC8示例是作者本人使用最多的CANo…

Web服务器集群: kylin 部署 Halo博客系统

目录 一、实验 1.环境 2. kylin 部署mysql数据库 3. kylin 构建Java运行环境 4. 创建博客使用的数据库 5. kylin 部署 halo博客系统 6. kylin 部署nginx 7. kylin 使用 SSL证书基于https访问部署的博客系统 二、问题 1.mysql安装报错 2.查看mysql数据库报错 3.syste…

【大数据】Flink 之部署篇

Flink 之部署篇 1.概述和参考架构2.可重复的资源清理3.部署模式3.1 Application 模式3.2 Per-Job 模式&#xff08;已废弃&#xff09;3.3 Session 模式 Flink 是一个多用途框架&#xff0c;支持多种不同的混合部署方案。下面&#xff0c;我们将简要介绍 Flink 集群的构建模块、…

选择VR全景行业,需要了解哪些内容?

近年来&#xff0c;随着虚拟现实、增强现实等技术的持续发展&#xff0c;VR全景消费市场得以稳步扩张。其次&#xff0c;元宇宙行业的高速发展&#xff0c;也在进一步拉动VR全景技术的持续进步&#xff0c;带动VR产业的高质量发展。作为一种战略性的新兴产业&#xff0c;国家和…

Qt程序设计-中英文输入法软键盘实现

本文讲解Qt中英文输入法软键盘实现。 实现目标 中英文切换、大小写切换、特殊字符切换、 使用谷歌中文字库txt文档。 在QWidget窗体上实现,可视化编写软键盘。 实现过程 准备工作:下载谷歌中文字库,按键的图片 创建QWidget项目,在主窗体上添加一个按钮,用于弹出软键…