# UE4架构
参考文献:
# 总览
UE4启动会加载Level,Level可以看成是一个World容器,其他类之间的关系如下:
下面是对它们的说明:
使用玩家输入或者AI逻辑控制Pawns
- Controller是一个负责指导Pawn的Actor。它们通常有两种版本,AIController和PlayerController。控制器可以“拥有”一个Pawn来控制它。
- PlayerController是Pawn与玩家控制他之间的接口。其代表了玩家的意志。
- AIController是一个可以控制Pawn的模拟意志。
代表世界的玩家、朋友、敌人
- Pawn是一个actor。Pawn可以被Controller持有,他们可以接收输入,可以做很多游戏逻辑。
- Character是一个人形风格的Pawn,继承自Pawn。他默认自带一个胶囊体碰撞器和角色运动组件。他可以做到基本的人形移动,他可以平滑的复制移动并且有一些动画相关的功能。
向玩家展现信息
- HUD是一个抬头显示器。可以显示健康、弹药等,每个PlayerController通常有一个。
- Camera相当于玩家的眼球并且管理他的行为。每个PlayerController通常也有其中一个。
设置与追踪游戏的规则
- 游戏模式(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
有几种方法可以为一个关卡设置游戏模式,从最低优先级到最高优先级排序:
设置/Script/EngineSettings.GameMapsSettingsDefaultEngine.ini文件部分中的GlobalDefaultGameMode条目将为项目中的所有地图设置默认游戏模式。
[/Script/EngineSettings.GameMapsSettings] GlobalDefaultGameMode="/Script/MyGame.MyGameGameMode" GlobalDefaultServerGameMode="/Script/MyGame.MyGameGameMode"
1
2
3要覆盖单个地图的项目设置,请在编辑器的世界设置选项卡中设置GameMode覆盖。
URL可以传递给可执行文件,以强制游戏加载特定选项。使用该game选项设置Game Mode。
UE4Editor.exe /Game/Maps/MyMap?game=MyGameMode -game
1最后,可以/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有关的几个类图关系如下:
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中有变量需要跨地图传递,可以有如下两种实现方法:
可以在PlayerController中增加相应的变量,然后在PlayerController派生类中的InitPlayerState(重载)方法中,获取该变量,再给PlayerState中对应的变量赋值。
Playerstate切地图后会调用CopyProperties,可以在你的PlayerState的派生类中重写改方法,把上一个地图中的PlayerState需要的属性赋值到新地图的PlayerState中,如下是ShooterGame的代码,跨地图传递TeamNumber。断线重连后也会调用CopyProperties方法,可以通过变量bFromPreviousLevel判定PlayerState是否来自于前一个关卡,如果不是,就可以认为是断线重连属性,这样可以处理玩家掉线后属性丢失的问题。
# Pawn
Pawn是玩家控制的实体,是玩家在游戏中的物理表示。E也是从Actor中再派生出了APawn,并定义了3块基本的模板方法接口:
- 可被Controller控制
- PhysicsCollision表示
- 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。这点要尤其注意。