# UE4架构

参考文献:

# 总览

UE4启动会加载Level,Level可以看成是一个World容器,其他类之间的关系如下:

FrameRelated

UE4ArchTech

下面是对它们的说明:

  1. 使用玩家输入或者AI逻辑控制Pawns

    • Controller是一个负责指导Pawn的Actor。它们通常有两种版本,AIController和PlayerController。控制器可以“拥有”一个Pawn来控制它。
    • PlayerController是Pawn与玩家控制他之间的接口。其代表了玩家的意志。
    • AIController是一个可以控制Pawn的模拟意志。
  2. 代表世界的玩家、朋友、敌人

    • Pawn是一个actor。Pawn可以被Controller持有,他们可以接收输入,可以做很多游戏逻辑。
    • Character是一个人形风格的Pawn,继承自Pawn。他默认自带一个胶囊体碰撞器和角色运动组件。他可以做到基本的人形移动,他可以平滑的复制移动并且有一些动画相关的功能。
  3. 向玩家展现信息

    • HUD是一个抬头显示器。可以显示健康、弹药等,每个PlayerController通常有一个。
    • Camera相当于玩家的眼球并且管理他的行为。每个PlayerController通常也有其中一个。
  4. 设置与追踪游戏的规则

    • 游戏模式(GameMode)游戏的概念分为两类。GameMode与GameState是游戏的定义,包括像游戏规则的事情,胜利的条件。他只存在于服务器上。他通常没有太多的数据变化,也没有客户端需要了解的瞬态数据。
    • 游戏状态(GameState)包括像关联球员名单得分,那里的作品是一盘棋,或者游戏中的任务完成列表。GameState存在于所有服务器与客户端上,可自由复制使所有计算机保持最新状态。
    • 玩家状态(PlayerState)是游戏参与者的状态,PlayerState包含玩家姓名,得分,类似MOBA的匹配等级。所有玩家的PlayerState存在于所有机器上,可以自由复制保持同步。

# GameInstance

能够横跨所有UE4关卡,UE4关闭时才会关闭。它不会因为切换关卡或者切换游戏模式而被销毁。然而,GameMode会在切换关卡或者游戏模式时销毁重置,这样它们里面的状态就不能被保存,GameMode的声明周期同Level是一样的。比如,你想在下一个关卡中知道上一个关卡游戏角色的位置,这时就得在GameInstance中保存游戏角色在上一个关卡的位置。

# GameState

该Game State是负责使客户能够监视游戏的状态。从概念上讲,Game State应该管理想让所有连接的客户端知道的信息,并且特定于Game Mode,但不是特定于任何单个玩家。它可以跟踪游戏范围内的属性。Game State不是追踪玩家特定事物的最佳位置,例如特定玩家在Capture The Flag比赛中为球队得分的数量,因为Player State可以更清晰地处理。通常,GameState应该跟踪在游戏过程中发生变化的属性,并且对每个人都是相关且可见的。虽然Game Mode仅存在于服务器上,但Game State存在于服务器上并被复制到所有客户端,随着游戏的进行使所有连接的计算机保持最新状态。

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

  • 游戏运行了多长时间(包括本地玩家加入前的运行时间)。
  • 当每个玩家加入游戏时,以及该玩家的当前状态。
  • 当前游戏模式的基类。
  • 游戏是否已经开始。

AGameStateBase是基本实现,它的一些默认功能包括:

功能或变量 使用
GetServerWorldTimeSeconds(获取服务器世界时间) 这是服务器的UWorld功能版本,GetTimeSeconds将在客户端和服务器上同步,因此可以用于复制。
PlayerArray(玩家数组) 这是所有APlayerState对象的数组,为游戏中的所有玩家做某事时非常有用。
HasBegunPlay(是否开始游玩) 如果BeginPlay已在游戏中的actor上调用该函数,则返回true 。

# GameMode

GameMode设置了游戏的规则,比如"最先经过终点的玩家获胜",他也处理players的生成。在联网状态下会是一个服务端,来控制连入它的客户端行为。在多人联网时,GameState可以Replicate而GameMode不能,为了数据安全考虑,GameMode只在服务器存在。所以,GameState可以允许客户端访问一些游戏数据如:得分、匹配时间,而访问不了GameMode里面的敏感数据。GameMode负责的主要规则包括:

  • 现有的球员和观众人数,以及允许的最大球员和观众人数。
  • 玩家怎样进入游戏,其中包括选择生成位置和其他生成/重生行为的规则。
  • 是否可以暂停游戏,以及如何开始游戏的暂停。
  • 关卡之间的切换,包括游戏是否应该以电影模式启动。

Game Modes都是AGameModeBase子类,其中包含可以覆盖的相当多的基本功能。一些常见功能包括:

函数/事件 目的
InitGame(初始化游戏) InitGame事件在任何其他脚本之前调用(包括PreInitializeComponents),并且通过AGameModeBase初始化参数和生产其辅助类。这是在任何Actor运行PreInitializeComponents之前调用的,包括Game Mode实例本身。
PreLogin(预登陆) 接受或拒绝尝试加入服务器的玩家。Login函数设置ErrorMessage为非空字符串,则导致该函数失败。PreLogin在Login之前调用,并且在调用Login之前可能会经过大量时间,特别是如果加入的玩家需要下载游戏内容。
PostLogin(处理登陆) 成功登录后调用。这是第一个可以安全地在PlayerController调用复制函数的地方。OnPostLogin可以在Blueprint中实现以添加额外的逻辑。
HandleStartingNewPlayer(处理启动的新玩家) 在PostLogin或者无缝旅行之后调用,可以在蓝图中覆盖,以改变新玩家的情况。默认情况下,它会为玩家创建一个pawn。
RestartPlayer(重新启动玩家) 被调用来开始产生一个玩家的Pawn。如果你想命令Pawn将在哪里生产同样有有RestartPlayerAtPlayerStart和RestartPlayerAtTransform可用。OnRestartPlayer可以在Blueprint中实现在函数结束后添加逻辑。
SpawnDefaultPawnAtTransform(在指定transform生产默认Pawn) 这实际上产生了玩家的Pawn,并且可以在蓝图中被覆盖。
Logout(登出) 当玩家离开游戏或被摧毁时调用。OnLogout可以实现蓝图逻辑。

AGameMode包含一个跟踪匹配状态或一般游戏流程的状态机。要查询目前的状态,您可以使用GetMatchState来获取,现在对它们的状态简要描述:

状态名 描述
EnteringMap 这是最初的状态。actor还没有每帧更新,世界还没有完全初始化。当物品完全加载时,它将转换到下一个状态。
WaitingToStart HandleMatchIsWaitingToStart在进入时被调用。玩家正在每帧更新,但Players还没有产生。如果ReadyToStartMatch返回true,或者如果StartMatch被调用,它将转换到下一个状态。
InProgress 是游戏的主要部分将发生的状态。HandleMatchHasStarted在输入时调用,然后在所有Actors调用BeginPlay。此时,正常的游戏玩法正在进行中。当ReadyToEndMatch返回true或被EndMatch调用时,匹配将转换到下一个状态。
WaitingPostMatch HandleMatchHasEnded在进入时被调用。actors仍然在每帧更新着,但新玩家无法加入。当地图传输开始时,它会转换到下一个状态。
LeavingMap 并在进入时调用HandleLeavingMap。该匹配在转移到新地图时保持此状态,并将转换回该地图EnteringMap。
Aborted 是故障状态,可以通过调用AbortMatch启动。当存在不可恢复的错误时设置此项。

# 设置GameMode

有几种方法可以为一个关卡设置游戏模式,从最低优先级到最高优先级排序:

  1. 设置/Script/EngineSettings.GameMapsSettingsDefaultEngine.ini文件部分中的GlobalDefaultGameMode条目将为项目中的所有地图设置默认游戏模式。

    [/Script/EngineSettings.GameMapsSettings]
    GlobalDefaultGameMode="/Script/MyGame.MyGameGameMode"
    GlobalDefaultServerGameMode="/Script/MyGame.MyGameGameMode"
    
    1
    2
    3
  2. 要覆盖单个地图的项目设置,请在编辑器的世界设置选项卡中设置GameMode覆盖。

  3. URL可以传递给可执行文件,以强制游戏加载特定选项。使用该game选项设置Game Mode。

    UE4Editor.exe /Game/Maps/MyMap?game=MyGameMode -game
    
    1
  4. 最后,可以/Script/Engine.WorldSettings/在DefaultEngine.ini文件的部分中设置映射前缀(以及URL方法的别名)。这些前缀为具有给定前缀的所有地图设置默认游戏模式。

    [/Script/EngineSettings.GameMapsSettings]
    +GameModeMapPrefixes=(Name="DM",GameMode="/Script/UnrealTournament.UTDMGameMode")
    +GameModeClassAliases=(Name="DM",GameMode="/Script/UnrealTournament.UTDMGameMode")
    
    1
    2
    3

# GameSession

可以把GameSession想象成是服务端的一个房间,所有玩家可以搜索房间并选择加入某个房间进行会话。它会处理是否同意登录请求,权威认证,是在线游戏的接口。

目前UE的机制里面,Session信息都是包含在OnlineSubsystem里面的。因为不同的平台有不同的验证机制,所以除了基本的IP地址端口等信息外,不同平台对Session的处理还可能有不同的内容,这样就出现了IOnlineSession接口以及对应平台的如FOnlineSubsystemNull这样的类,他把具体的Session信息封装,加入一些与当前OnlineSubsystem相关的操作与处理。

最后几个类(FOnineSession,FNamedOnlineSession, FOnlineSesssionInfo),其实就是具体的Session信息。里面都是常见的Session数据,比如用户唯一ID,服务器IP地址,玩家数量配置等。前面不管是GameSession或者是OnlineSubsystem,最终操作的都是这里的数据。

和Session有关的几个类图关系如下:

SessionRelatedClass

AGameSession顾名思义,其实其本身并不是Session信息的产生者与拥有者,他主要的目的就是负责Gameplay游戏逻辑与具体的底层Session机制的交互。比如游戏里面有一个在网络上寻找Session的功能,那么一般我们会通过游戏逻辑(比如UI按钮事件)调用GameSession的查找Session功能,GameSession会进一步从OnlineSubsystem里面查询Session。

# PlayerController

PlayerController(玩家控制器)是Pawn和控制它的人类玩家间的接口。PlayerController本质上代表了人类玩家的意愿。

当您设置PlayerController时,您需要考虑的一个事情就是您想在PlayerController中包含哪些功能及内容。您可以在Pawn中处理所有输入, 尤其是不太复杂的情况下。但是,如果您的需求非常复杂,比如在一个游戏客户端上的多玩家、或实时地动态修改角色的功能,那么最好 PlayerController中处理输入。在这种情况中,PlayerController决定要干什么,然后将命令(比如“开始蹲伏”、“跳跃”)发布给Pawn。

同时,某些情况下,则必须把输入处理或其他功能放到PlayerController中。PlayerController在整个游戏在过程中都是一直存在的,但是Pawn可能是临时存在的。 比如,在死亡竞技模式的游戏中,您可能死了又重生,所以您将获得一个新的Pawn,但是您的PlayerController都是一样的。在这个示例中,如果您将分数保存到您的Pawn上, 那么分数将会重置,但是如果您将分数保存到PlayerController上,它将不会重置。

# PlayerState

PlayerState包含玩家姓名,得分,类似MOBA的匹配等级。所有玩家的PlayerState存在于所有机器上,可以自由复制保持同步。

切换地图时,PlayerController默认是不会被销毁的,而PlayerState默认会被销毁

如果PlayerState中有变量需要跨地图传递,可以有如下两种实现方法:

  1. 可以在PlayerController中增加相应的变量,然后在PlayerController派生类中的InitPlayerState(重载)方法中,获取该变量,再给PlayerState中对应的变量赋值。

  2. Playerstate切地图后会调用CopyProperties,可以在你的PlayerState的派生类中重写改方法,把上一个地图中的PlayerState需要的属性赋值到新地图的PlayerState中,如下是ShooterGame的代码,跨地图传递TeamNumber。断线重连后也会调用CopyProperties方法,可以通过变量bFromPreviousLevel判定PlayerState是否来自于前一个关卡,如果不是,就可以认为是断线重连属性,这样可以处理玩家掉线后属性丢失的问题。

    CopyProperties

# Pawn

Pawn是玩家控制的实体,是玩家在游戏中的物理表示。E也是从Actor中再派生出了APawn,并定义了3块基本的模板方法接口:

  1. 可被Controller控制
  2. PhysicsCollision表示
  3. MovementInput的基本响应接口

# 子类

Pawn有三个重要子类:ADefaultPawn,ACharacter,ASpectatorPawn,其中前两者是直接继承自Pawn,最后是继承自ADefaultPawn。

# DefaultPawn

因为我们每次想自己搞Pawn都得从Pawn派生过来,然后再一个个添加组件。UE知道我们大家都很懒,所以提供了一个默认的Pawn:DefaultPawn,默认带了一个DefaultPawnMovementComponent、spherical CollisionComponent和StaticMeshComponent。也是上述Pawn阐述过的三件套,只不过都是默认套餐。

# SpectatorPawn

UE的FPS做的太好了,就会有一些观众想要观战。观战的玩家们虽然也在当前地图里,但是我们并不需要真正的去表示它们,只要给他们一些摄像机“漫游”的能力。所以派生于DefaultPawn的SpectatorPawn提供了一个基本的USpectatorPawnMovement(不带重力漫游),并关闭了StaticMesh的显示,碰撞也设置到了“Spectator”通道。

# Character

因为我们是人,所以在游戏中,代入的角色大部分也都是人。大部分游戏中都会有用到人形的角色,既然如此,UE就为我们直接提供了一个人形的Pawn来让我们操纵。像人一样行走的CharacterMovementComponent, 尽量贴合的CapsuleComponent,再加上骨骼上蒙皮的网格。同样的三件套,不一样的配方。 有些人一开始的时候会困惑应该选择Pawn还是Character,其实从继承体系中就可以了解到Character只不过是Pawn的加强特化版本。一般来说,如果你控制的角色是人形的带骨骼的,那就选择Character吧。而如果是VR中的一双手(假设只有一双手),因为移动模式和显示都算不太上人形,顶多只能算是个漂浮的“幽灵”,所以还是用Pawn方便些。

# 游戏界面

游戏界面一般由两个主要部分组成:平头显示信息(HUD)和 菜单或用户界面(UI)。

# HUD

(HUD)(平视显示信息) 是指在游戏过程中覆盖在屏幕上的状态及信息。HUD的作用是告知玩家 在游戏中的当前状态,也就是,分数、它们的生命值、剩余时间等。 HUD通常是不能交互的,这意味着玩家不能点击HUD的元素,尽管这会变成一个灰色区域, 但在某些类型的游戏中,HUD和用户界面是很难区分的。

UE4中的HUD中常用来管理UMG。

# UI

用户界面(UI) 是指菜单和其他的交互性元素。这些元素通常和 HUD 一样覆盖地 描画在屏幕上,但是在某些情况下,用户界面可能会作为游戏世界本身的一部分渲染到 世界中的一个表面上。最明显的用户界面示例是游戏启动时显示的主菜单, 或者玩家暂停游戏时显示的暂停菜单。但是,其他的用户界面可能在游戏过程中显示。这些用户界面 可以用于显示游戏中或者在更复杂的情形(比如RTS或RPG)中两个玩家之间的对话, 它们可以集成到游戏本身中,允许玩家选择武器、弹药、要建立的战队等。

# 区别

  • HUD Canvas是来自UE3(甚至可能更老)的东西,在UMG被引入之前处于UE4中,并作为选项保留。
  • UMG是Slate的蓝图延伸,Slate是为编辑器专门制作的窗口UI框架(以前的UE编辑器使用了Window的库),但它也可以用在游戏中。但是Slate类不在UObject环境之外而在反射系统之外,这就是为什么它需要UMG包装来使其与蓝图一起工作。

# 其他概念

  • GameViewportClient:是一个高层级的抽象接口,它负责一些具体平台的渲染,audio,以及输入子系统,它是引擎到游戏视口的接口。一般每个游戏的实例只创建一个GameViewportClient,到目前为止唯一有一个游戏实例却有多个GameViewportClient的场景是由多于一个PIE窗口运行。
  • GameUserSetting:可用于用户全局游戏设置。可设置分辨率,锯齿。
  • WorldSetting:用于当前关卡的设置。

# 坐标系

# 方向

UE4和Unity3d都是左手坐标系,只不过不同是代表前后不一样:

方向 UE4轴 Unity3d轴
前方 x轴 z轴
上方 z轴 y轴
右方 y轴 x轴

# 旋转角度

有三个概念:

  • Pitch:围绕Y轴(右方),向上向下看。
  • Yaw:围绕Z轴(上方),以圆旋转,北方为正,南方为负。
  • Roll:围绕X轴(前方),顺时针为正,逆时针为负。

在UE4面板上,设置旋转为roll,pitch,yaw。但是在C++的FRotator中时pitch,yaw,roll。这点要尤其注意。