多人枪战游戏

分析多人游戏案例中的蓝图以及它们是如何构建的。

Choose your operating system:

Windows

macOS

Linux

MultiplayerExample_Header.png

简单蓝图多人游戏(Simple Blueprint Multiplayer) 是一个完全由 蓝图 UMG用户界面(UMG UI) 编写的游戏,可以作为如何使用蓝图的 会话节点(Session Node) 打造游戏中的多人部分的使用示例。这里有一个主菜单、一个服务器列表以及一个简单的地图,地图中有一个带有记分牌的HUD显示。在主菜单中点击 运行(Play) 便能创建会话并加载进入游戏地图。其他玩家在自己的菜单界面中点击 查找游戏(Find games) 来查看所有存在的游戏主机列表,点击列表中的一个已查找到的游戏时则会尝试加入。如果出现任何错误,则会回到主菜单并显示"错误"对话框。

这个游戏也可以作为一个示例,说明如何使用 游戏实例(GameInstance) 蓝图来管理游戏状态。游戏实例(GameInstance)是一种便于管理游戏状态的方法,在地图加载过程中它始终存在,同时它还是用于接收错误事件的对象。大部分会话相关的调用以及菜单的切换都由游戏实例来处理。

目前,本文档涵盖了 在线会话节点 和它如何实现多人游戏中的主机创建、发现、加入和退出游戏。本文档会在之后更新,进一步说明射击游戏的其他方面,比如攻击命中其他玩家、死亡和重生以及得分的计算。

开始/进行游戏

这个部分介绍了如何开始和进行游戏,并拆解了游戏中的各个组件。

如果你刚开始了解虚幻引擎4中的多人游戏测试,请先参阅 测试多人游戏 文档。

加载游戏:

  1. 游戏/地图(Game/Maps) 文件夹中打开 主菜单(MainMenu) 地图。

  2. 主菜单地图打开后,点击 运行(Play) 按钮右边的向下箭头,将 玩家数量(Number of players) 设置为 2。

    SettingNumberOfPlayers.png

    运行专用服务器(Run Dedicated Server) 选项在本示例中可能会造成服务器列表显示不正确,此问题正在修复中。

  3. 点击 运行(Play) 按钮启动游戏。

  4. 当游戏启动后,会显示以下窗口。

    twoWindows.png

    上图是我们在 新编辑器窗口(New Editor Windows) 中,其中每个窗口设置为 640x480 的分辨率,你可以在 高级设置...(Advanced Settings...) 选项内进行此项设置。

    如果是在网络上测试这个范例,不像上述过程直接使用本地多人游戏的话,应采用 独立运行模式(Standalone Play Mode) 加入其他人的游戏,或者自己作为主机创建并让其他人来加入。通过在编辑器中启动游戏(Play In Editor,即PIE)运行已联网的游戏目前并不稳定,这个问题我们还在处理。

主菜单(Main Menu)的详细说明如下。

MainMenu.png

选项

描述

1

以当前游戏窗口作为主机开始游戏。

2

显示当前有效的 服务器列表 ,可用于选择并加入。

3

退出当前游戏。

4

局域网(LAN) 因特网(Internet) 两个连接模式间切换选择。

一旦选择作为主机开始游戏,或者加入其他主机的游戏,则会看到下图所示的游戏窗口。

InGame1.png

在屏幕的左上角(黄色框中),可以看到一些文字,这是当前角色的名字。在名字右边的框中,是当前该角色的得分。当有玩家加入时,角色名字和得分的部分都会更新,来显示当前游戏中的所有玩家和对应的的得分。

TwoInGame.png

上图使用的玩家名来自局域网(LAN)连接模式。但当你使用 Steam 等服务时,会显示玩家的Steam用户名。

游戏主机已经创建后,在第二个玩家的窗口中点击 查找游戏(Find Games) 按钮来显示 服务器列表(Server List)

ServerListWindow.png

经过一小段时间的搜索后,当前可用的游戏就会显示在列表中。在这个窗口中,从左到右依次显示的是服务器名称、玩家数量以及该游戏主机的ping值。你可以点击屏幕左下角的 刷新(Refresh) 按钮来刷新列表,或者点击 返回(Back) 按钮返回主菜单。直接点击列表中的名称就会尝试连接服务器,并且在游戏中生成角色。

进入游戏后,你必须点击 鼠标左键 表示你已经准备好开始游戏。

1Ready.png

当点击完成准备后(主机端或客户端),左上角会显示一段文本,表示该玩家已经准备完毕。

完成准备后,可以用以下几个功能按钮进行游戏操作。

按钮

描述

鼠标右键(Right Mouse Button)

拔枪(持续按住保持举枪动作)。

鼠标移动(Mouse)

进入 瞄准模式(Aim Mode) ,移动鼠标进行瞄准。

鼠标左键(Left Mouse Button)

开火。

鼠标中键(上下滚动)(Middle Mouse (Wheel Up/Down))

在非瞄准状态下换弹。

Q 键

打开游戏暂停菜单(能够继续游戏或者离开游戏)。

这个游戏的目标是射击其他玩家,命中得1分,被击中的玩家则会重生。每个玩家有六次射击机会,用完后需要用鼠标中间(上下滚轮)来换弹(一次一颗)。

项目设置/配置

这个部分涵盖了 内容浏览器(Content Browser) 中创建(或修改)的每个资产及其描述。有几个蓝图和 UMG UI资产相互通信(或有依赖关系),因此如果你想要模仿本项目示例,最好从头开始创建并再将它们关联起来。

内容浏览器 中,每种资产都能在它们对应的文件夹中找到:

游戏/蓝图/控件

名称

描述

错误对话框(ErrorDialog)

这是一个出现错误时用于显示错误的UI界面。

HUD

这个是游戏内的记分牌,显示 记分牌行(ScoreboardRow) 控件,包括所有玩家的名字和得分。

游戏内菜单(InGameMenu)

这个是游戏内的菜单,提供离开游戏、返回主菜单的功能。

加载界面(LoadingScreen)

这是一个简单的加载界面,它会在加入游戏的过程中显示。

主菜单(MainMenu)

游戏启动时的主菜单,玩家能够创建主机、加入或者退出游戏。

记分牌行(ScoreboardRow)

这是个添加到 HUD 的控件,包括了玩家的名字和得分。

服务器列表(ServerList)

这个菜单包括了一组 服务器行(ServerRow) 的控件,用于显示所有的可用游戏主机列表。

服务器行(ServerRow)

这个控件具有一个可用主机的信息,包括服务器名称、玩家数量和ping值。它被传递到 服务器列表(ServerList) 控件上。

游戏/蓝图

名称

描述

我的游戏实例(MyGameInstance)

(实例蓝图) - 处理所有从主菜单到其他界面过程中发生的状态变化,以及错误事件。

我的游戏模式(MyGameMode)

(游戏模式蓝图) - 记录了所有的默认类(Pawn、HUD、PlayerController 等)。并处理玩家重生的逻辑,以及玩家加入游戏时的逻辑。

我的玩家控制器(MyPlayerController)

(PlayerController 蓝图)设置游戏内界面,并允许玩家打开游戏中的暂停界面。

我的玩家状态(MyPlayerState)

(玩家状态蓝图)处理每个玩家的分数。

发射物蓝图(ProjectileBP)

(Actor蓝图)是每个用于玩家开火的子弹,会给对方造成伤害。

状态(State)

(枚举)这是游戏可能处于的状态的全集列表。

游戏/枪手

名称

描述

Gunslinger_BP

这个是游戏中可以使用的角色。

GunslingerTTP

和Gunslinger_BP一同使用的骨骼网格体(SkeletalMesh)。

Gunslinger_AnimBP

用来驱动Gunslinger_BP的动画。

游戏/角色

名称

描述

角色_父节点(Character_Parent)

(父节点)Gunslinger_BP使用的材质

角色_实例(Character_Instance)

(实例化的)和Gunslinger_BP使用的材质。

游戏/字体

名称

描述

得分字体(ScoreFont)

记分牌(Scoreboard) 控件使用的字体。

游戏/地图

名称

描述

主菜单(MainMenu)

项目和编辑器启动时的默认加载地图,包含了前端显示。

关卡_01(Level_01)

这是玩家创建或加入游戏时生成的游戏地图。

配置设置

为了成功创建主机或者连接到多人游戏中,你需要对 DefaultEngine.ini 文件进行一些设置,该文件在以下位置(示例如图) UnrealProjectDirectory/ProjectName/Config

ExampleLocation.png

打开后,需要先找到 在线子系统(OnlineSubsystem) 以及与子系统对应的 默认平台服务(DefaultPlatformService)

例如,要进行局域网(LAN)游戏,需要添加 DefaultPlatformService=Null

[OnlineSubsystem]
DefaultPlatformService=Null

或者如果要在 Steam 上玩,需要使用 OnlineSubsystemSteam

[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")

[OnlineSubsystem]
DefaultPlatformService=Steam

[OnlineSubsystemSteam]
bEnabled=true
SteamDevAppId=480
bVACEnabled=0

[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"

如果要用Steam的话,需要 额外的SDK和INI设置 ,点击该链接查看更多信息。

蓝图演示

在接下来的章节中,我们将介绍游戏的各个状态,以及驱动这些状态工作的蓝图。

我们先看看 启动(Startup) 的序列。

启动

主菜单(MainMenu) 地图中,打开 关卡蓝图(Level Blueprint)

Startup1.png

关卡蓝图(Level Blueprint) 打开后,可以看到一部分脚本标记了"游戏逻辑自此开始(Game Logic Starts Here)"。

Startup2.png

这个注释非常不错,因为游戏逻辑正是从这部分脚本开始。游戏开始时,获取游戏实例并 转换(Cast) 我的游戏实例(MyGameInstance) ,这样就可以访问它的变量、函数和脚本了。在本示例中,接下来调用了一个名为 Begin Play Show Main Menu 的函数(见下图)。

Startup3.png

Begin Play Show Main Menu 被调用时,首先运行了一个预先创建的宏,名为 IsCurrentState

IsCurrentState宏

Startup4.png

IsCurrentState 会检查 In State (在宏的节点上定义) 是否和一个叫做 当前状态(Current State) 的变量一致。 当前状态(Current State) 变量是一个由 状态(State) 创建的枚举类型,记录游戏可能处于的每一个状态。

既然 Current State In State 一致,便能调用 展示主菜单事件(ShowMainMenuEvent) 这个自定义事件。

Startup5a.png

Startup5.png

展示主菜单事件(ShowMainMenuEvent) 首先运行 是否为当前状态宏(IsCurrentState Macro) 以检查当前状态是否为 运行中(Playing) 。如果为 是(True) 的话,则打开 主菜单(MainMenu) 的关卡,如果游戏当前在 启动(Startup) 状态,则返回 否(False) 。这里调用了另一个预创建的宏,名为 过渡至状态(TransitionToState)

TransitionToState宏

下图中,在过渡至状态(TransitionToState)中,首先运行了 IsCurrentState宏 (1)对照 当前状态(Current State) 期望状态(Desired State) 。如果两者一致则在屏幕上显示错误并提示原因(2),如果它们不一致,则执行 Switch on State 节点(3),这里会获取 当前状态(Current State) 并调用一个叫做 隐藏控件(Hide Widget) 的函数(这里将会隐藏当前显示的UI控件),或者运行一个叫做 Destroy Session Caller 的自定义事件(将会销毁该玩家调用的会话)。

Startup6.png

当切换状态(Switch on State)完成后,当前状态(Current State)会根据期望状态(Desired State)被更新。

完成 TransitionToState 宏后,我们可以继续 ShowMainMenuEvent 自定义事件。

Startup7.png

上图中, IsValid 节点(1)执行,它会检查 主菜单(Main Menu) 变量是否有效,第一次运行该变量会无效(如果有效的话则跳过图中的(2)和(3))。步骤(2)调用了 创建控件(Create Widget) 节点来创建一个叫做 主菜单(Main Menu) 的 UMG 控件蓝图,并将结果作为一个变量(3)保存,这样便能在以后的脚本中直接访问而无需重新创建。在(4)中 Set Input Mode UIOnly 节点用来限制输入为仅UI输入,在(5)中则将 主菜单(Main Menu) 添加到视口并显示主菜单界面。

Startup8.png

接下来看看玩家点击运行(Play)来创建游戏会发生什么。

创建游戏

加载主菜单(Main Menu)并点击后,可以在 主菜单(MainMenu) 控件蓝图中点击 运行(Play) 按钮,会运行下图中的脚本。

Play1.png

主菜单(Main Menu) 控件蓝图中的 Designer 选项卡中,可以为 "Play" 按钮创建一个 按钮 事件关联到 OnClicked 事件上。当该事件触发时将获取游戏实例并 转换(Cast) 我的游戏实例(MyGameInstance) 蓝图,这样就能调用它内部的自定义事件 Host Game Event 了。

Play2.png

HostGameEvent 首先运行了一个 MyGameInstance 蓝图内的自定义事件: ShowLoadingScreen

Play3.png

ShowLoadingScreen 被调用时,先运行了 TransitionToState宏 (并将 期望状态(Desired State) 设置为 Loading Screen )。

然后继续 ShowLoadingScreen 事件,调用了 IsValid 的检查,如下图(1)部分。

Play4.png

是否有效(IsValid) 检查变量 加载控件(Loading Widget) 是否有效,第一次运行是无效的(如果有效则跳过步骤(2)和(3))。步骤(2)创建了 Loading Screen 的UMG控件蓝图,并将结果保存到变量中(3),这样以后便能直接访问而无需重新创建。在步骤(4)中, 加载控件(Loading Widget) 被添加到视口中,并调用 Set Input Mode UIOnly 节点,可以限制输入为仅UI输入(这时会在游戏中显示)。

Play5.png

当 Loading Screen 显示时,脚本就回到了 HostGameEvent 上继续执行 Create Session 节点。

Play6.png

创建会话(Create Session) 节点上, Public Connections 的数量(允许玩家加入该游戏会话)设置为 4 。还有一个 布尔值 变量叫做 Enable LAN ,它直接连接到了 Use LAN 的输入引脚上。 Enable LAN 变量是通过主菜单上的 Play Mode 来切换的,稍后在本文档中我们还将进行讨论。如果成功创建会话,则调用 打开关卡(Open Level) 节点来加载用于该游戏会话的地图。如果失败的话,则执行 OnFailure 引脚,并运行一个预先创建的宏,名为 DisplayErrorDialog

DisplayErrorDialog宏

调用DisplayErrorDialog宏时,先会执行(1) TransitionToState宏 ,以便切换到 错误对话(Error Dialog) 状态中。当切换到新状态后,执行自定义事件 Destroy Session Caller (2) 来销毁当前玩家会话(可以在事件图表中找到)。销毁会话后,将调用引擎的宏 IsValid 来检查错误对话(Error Dialog)变量(这是一个错误对话控件蓝图)是否有效。

Play7.png

以下图表第一次运行时, 错误对话(Error Dialog) 是无效的,如果有效的话,将会跳过(1)和(2)直接进入(3)。

Play8.png

第一次运行时,上图的(1)将使用 创建控件(Create Widget) 节点创建一个ErrorDialog控件蓝图。(2)中将该控件蓝图提升为名为Error Dialog的变量(以便下次执行宏时直接使用)。在(3)中,调用ErrorDialog控件蓝图中的函数 Set Message ,将得到并设置指定在宏节点上的信息,以便在对话框(下图黄色高亮框)中显示,设置到 ErrorDialog 上,并且 Add to Viewport 将错误对话控件蓝图(Error Dialog Widget Blueprint)显示在界面中(5),并使用 Set Input Mode UIOnly 将输入模式设为仅UI输入。

Play9.png

如果没有错误的话,则会加载在 Open Level 节点中定义的地图,玩家进入 游戏

加入游戏

在主菜单中,点击 搜索游戏(Find Games) 按钮会执行 主菜单(MainMenu) 控件蓝图中的相应脚本,如下图所示:

Findgames1.png

主菜单(MainMenu) 控件蓝图中的 Designer 选项卡中,可以为"搜索游戏(Find Games)"按钮创建一个 按钮 事件并关联到 OnClicked 事件上。当这个事件触发时,获取游戏实例并 转换为我的游戏实例(Cast to MyGameInstance) 蓝图,这样就能调用它内部的自定义事件 Show Server List Event 了。

Findgames2.png

ShowServerListEvent 首先运行 TransitionToState 宏 (1)将游戏状态设置为 Server List 。然后用 IsValid (2)检查 Server List 控件蓝图变量,如果有效,就使用 Add to Viewport (5)节点屏幕上显示服务器列表菜单。第一次运行时,需要先创建服务器列表界面,通过 Create Widget (3)节点,并将结果提升至变量(4)中,然后再显示在界面中。当显示在界面中后,再用 Set Input Mode UIOnly 节点限制输入为仅UI。

在服务器列表菜单显示的时候,在上述的步骤(5)时,步骤(6)前,服务器列表控件蓝图内还有一段脚本会被执行,可以将当前可用的游戏填充到服务器列表菜单中。可以在下图的服务器列表控件蓝图中看到, 事件构造(Event Construct) 节点会在该控件蓝图被构造(创建)时调用,并立刻执行一段预先设置的 RefreshListMacro 宏。

Findgames3.png

RefreshListMacro宏

RefreshListMacro执行许多任务,下面将介绍第一部分。

Findgames4.png

如上,当宏被调用时,首先它将一个名为 Refresh Button Enabled 的布尔值变量设置为 False (1),这会禁用 刷新(Refresh) 按钮,防止玩家点击。接下来,一个叫做 状态文本(Status Text) 的变量设置为 搜索...(Searching...) (2)并将它的 可视性(Visibility) 设置为 可见(Visible) (3),这样就能更新并显示带有状态文本的控件。

在(4)的位置,有一个叫做 Found Session Widgets 的数组变量,它是一组服务器行的集合,用于刷新服务器列表,可以使用 Remove Child (5)节点移除当前存在的服务器。当显示界面时,这将会删除所有将进入列表的服务器。在(6)的位置, Found Session Widgets 变量再次被清理以消除先前保存过的会话,这样就能在进一步查找前先把所有的东西清理干净。

然后,该宏获取游戏实例并使用 Cast To MyGameIntance 节点(下图 1 处)来访问 Enable LAN 的值(在查询会话时将会使用)。

Findgames5.png

Find Session 节点(2)用于查询当前存在的会话(会作为返回值输出)。在这个节点中,还可以设置 最大结果(Max Results) (返回查询结果的数量)。 Find Sessions 查询的返回结果指定保存在 Found Sessions 数组变量中(3)。这里会有一个 分支(Branch) (4)来检查结果,如果是 0 的话,继续执行 True 引脚(如果不是的话则执行 False )。如果执行 Find Sessions 的节点时出现错误,则会调用 OnFailure 事件,并修改 状态文本(Status Text) ,显示为 Search failed (5),提示用户查找会话并未完成。

分支 后(上图中的 4),如果是 True (也就是并未找到任何会话), 状态文本(Status Text) 会被设置为 No sessions found (下图中的 7),并且 Refresh Button Enabled 变量被设置为 True (8),这样玩家可以点击 Refresh 按钮以再次查找游戏。

Findgames6.png

如果找到会话, Status Text Visibility 被设置为 Collapsed (1)便能将该控件隐藏。然后使用 ForEachLoop 节点(2)来处理找到的每个 结果(result) ,并使用 Create ServerRow 节点(3)为每个结果创建一个服务器行控件(Server Row Widget)。这些就是服务器列表里每个独立的会话行。

在(4)的位置, 结果(Results) 也被添加到 Found Session Widgets 数组里,然后再调用服务器行(ServerRow)控件蓝图(用于生成服务器名称、玩家数量和ping值)中的设置搜索结果(Set Serach Result)函数。设置完服务器行控件蓝图的内容后, Add Child 节点(6)用于将每个服务器行控件蓝图添加至显示在服务器列表菜单中的 滚动服务器(Scrolling Servers) 滚动框。

当每个搜索到的会话都创建了服务器行控件后,把它们添加到 滚动服务器(Scrolling Servers) 控件上, ForEachLoop 完成,并将 Refresh Button Enabled 变量设置为 True (8)。

在运行了 RefreshListMacro 搜索会话后,任何找到的可用会话将显示在 服务器列表(Server List) 菜单中。

Findgames7.png

点击 刷新(Refresh) 按钮将会再次运行 刷新列表宏(RefreshListMacro) 来搜索会话。点击 返回(Back) 按钮将会执行自定义事件 ShowMainMenuEvent (已在 启动 章节中进行了说明)。

在服务器列表中点击其中一个服务器将会触发在 服务器行(ServerRow) 控件蓝图中的 OnClicked 事件(见下图)。

Findgames8.png

服务器行(ServerRow) 控件的 OnClicked 事件,先获取了游戏实例,并使用 Cast To MyGameInstance 节点来进一步调用它的 Join from Server List Event 事件。 ServerResult 变量(作为 RefreshListMacro 的一部分生成)被传入 MyGameInstance 的蓝图,并允许玩家在点击时加入服务器。

我的游戏实例(MyGameInstance) 蓝图中的 JoinFromServerListEvent (下图)被调用时,先运行自定义事件 ShowLoadingScreen ,这部分在 创建游戏 章节中作了描述。

Findgames9.png

当显示加载界面(LoadingScreen)的时候,加入会话(Join Session)节点会尝试加入玩家当前点击的并由服务器行控件提供的搜索结果(SearchResult)。如果成功的话,玩家就能加入该服务器,并在游戏中生成角色。如果出现错误的话,就会运行 DisplayErrorDialog 并显示 Failed To Join Session 错误信息(Error Message)

游戏性

在玩家控制角色前,角色和游戏状态都需要被设置好(或者,比如要加入一个游戏时,游戏自身需要更新,并被告知有新玩家要加入进来)。点击主菜单的 Play 按钮,或者从服务器列表中选择一个服务器加入时,首先要调用 游戏模式(GameMode) 蓝图中的 Post Login 事件,这里将介绍下图所示的设置过程。

Gameplay1.png

Event Post Login 触发后,先运行了一个引擎的宏,名为 Switch Has Authority (1),这里会检查脚本在一台具有网络权限(Network Authority)(在服务器上)的机器上运行,还是在一台远程机器(一个客户端)上运行。这个过程会在服务端为主机玩家和加入游戏的客户端运行, 远程(Remote) 引脚并不运行任何事件,而 Authority 引脚会继续执行登录后(Post Login)的脚本。

还有一个关于 Switch Has Authority 的示例,请查看 在蓝图中检测网络主机和远程客户端 页面。

在(2)处,新玩家的 玩家状态(PlayerState) 转换(Cast) MyPlayerState 蓝图,这里会设置 Player Number (3),保证玩家记分牌中的顺序和玩家顺序一致(每个新玩家加入都会添加到记分牌中)。设置好玩家数量后,新玩家被 转换(Cast) MyPlayerController 蓝图,以便触发 ClientPostLogin 这个自定义事件。

Gameplay2.png

如上图所示, ClientPostLogin 事件的 细节(Details) 面板中还有一些属性设置。

Gameplay3.png

图表(Graph) 分段中, 复制(Replicates) 选项设置为 Run in owning Client 并且勾选了 Reliable 复选框。这两个选项可以表示这段脚本在服务上被调用,但该节点则仅在当前用户的客户端上执行。 Reliable 的设置保证了这个脚本一定会被客户端执行,并且不会因为严重的网络负载而丢失(大部分处理视觉效果同步复制的调用都是 Unreliable ,以避免过多的消耗网络负载,但在这个例子中,我们需要该事件必须执行,因此使用 Reliable )。

确认了脚本应该在何处触发后,就可以进一步调用 Setup Ingame UI 函数。

Gameplay4.png

这个函数从 分支(Branch) (1)开始,检查当前的目标控制器是否是本地玩家控制器,如果是,则使用 Create Widget (2)节点创建 HUD 控件蓝图。然后将 HUD 控件提升至一个名为 HUD Widget 的变量(3)中,并使用 Add to Viewport (4)节点把它添加到玩家视口中。在(5)处,再用一个 Create Widget 节点创建 InGame Menu 控件蓝图,并提升到 InGameMenuWidget 变量(6)中,这会在玩家点击按钮调用游戏内菜单时显示。

到这里,"login" 的过程就完成了。角色的设置在 Character Animation Blueprint 中,定义了角色的每个动作并设置了动画。此处并不涵盖这部分内容,如需了解有关 动画蓝图(Animation Blueprints) 的内容,请参阅 动画蓝图 文档。

设置好 角色动画蓝图(Character Animation Blueprint) 后,就可以打开 Level_01 地图,在这个地图的 关卡蓝图(Level Blueprint) 中,当玩家实际在关卡中生成之前,调用了以下脚本。

level1LevelBlueprint.png

上方地图的 Event Begin Play 中,为当前游戏实例调用了 Cast To MyGameInstance 节点,并,从中调用 Start Playing State 函数。这是一个 MyGameInstance 蓝图内的函数,可将游戏状态设置为 游戏中(Playing) ,如下图所示。

playingState.png

将状态设置为 游戏中(Playing) 后,通过调用 Set Focus To Game Viewport 的节点(上图中的(3)),鼠标会被锁定在游戏视口内,并调用 Set Input Mode Game Only 节点(上图的(4))使UI不再截获用户输入。

从游戏中离开

离开游戏的过程非常简单直接,只需要打开或关闭一些UMG控件的显示,并执行第一次加载游戏进入主菜单同样的过程即可。

在游戏中按 Q 键后,则能看到游戏内的暂停菜单,点击 离开游戏(Leave Game) 选项,则执行在 InGameMenu 控件蓝图中的以下步骤:

disconnectingGraph.png

在上图中,点击了离开(Leave)按钮后触发了事件(1),获取到当前玩家并 转换(Cast) MyPlayerController 的蓝图(2)。在 MyPlayerController 蓝图内的函数 Hide in Game Menu (3)如图被调用。

hideingamemenu.png

这部分脚本得到 游戏内菜单控件(In Game Menu Widget) 变量(先前创建并保存为引用的变量),并使用 Remove From Parent 节点将它从屏幕上移除。然后,调用 Set Input Mode Game Only 来防止玩家操作其他 UI,直到我们将输入模式修改为允许UI输入为止。

然后脚本返回到 InGameMenu 控件蓝图上,并使用 Cast To MyGameInstance 节点(4)来得到当前游戏实例并调用它内部的函数 Show Main Menu Event 。该函数首先运行 IsCurrentState宏 ,接下来便是 开始 章节中首次加载游戏的部分。

disconnectingGraph.png

欢迎帮助改进虚幻引擎文档!请告诉我们该如何更好地为您服务。
填写问卷调查
取消