大家好,欢迎来到IT知识分享网。
1 理解Gameplay
最近的面试中(Unity和Unreal的都有),我也会适当的问一些候选人类似的问题,比如你所理解的Gameplay和3C是什么?大多数学习过Unreal会说 3C 就是指Character,Control和Camera,不过也就仅此而已;而对Gameplay的表述则会混乱一些,大致会把Unreal的流程讲一遍。而只有Untiy经验的有很大一部分是表述不出来这些概念的。这也会让我进行一些思考,为什么不同的引擎开发人员对游戏开发概念会出现这么大的偏差呢?
随着Unreal 引擎的逐渐普及,Gameplay的概念也得到了广泛的传播。因为开源的关系,Unreal在招聘的时候会刻意区分是引擎岗位还是“客户端开发”岗位,而这个“客户端开发”在广义上就是指Unreal的Gameplay。
那么为什么我要区分“广义”和“狭义”呢?因为划分的标准不一样。
- 广义的Gameplay。以引擎源码为界限,需要改动引擎源码才能实现的会被划归为引擎开发。而基于引擎开发游戏或者玩法的称之为Gameplay。所以若以广义的划分标准来看,绝大多数的Unity开发都是“Gameplay”开发。而绝大大多数的Unreal开发,多多少少都会改动到引擎,是不是就是“引擎开发”了呢?
- 狭义上的Gameplay。以Unreal 4.27 提供的Gameplay框架作为参考,它其实包含的就是游戏的规则和状态,3C和用户界面,也就是表达一个游戏玩法的最基础元素(但实际上一个复杂的游戏考虑的远远不止这么些)。那么问题来了,比如我们所说的战斗,剧情,AI,载具这些就不属于游戏玩法了吗?就不是Gameplay了吗?
注:UE5的Gameplay扩展了Actors,移动组件,游戏功能和模块化,定时器等内容,但对于理解Unreal的Gameplay上没有什么大的概念上的变化。Unreal 5.1 的 Gameplay框架 。
综上所述,无论是从广义上还是狭义上的Gameplay划分都是不太科学合理的。
- 对于Unity项目而言,它的AssetBundle机制很不好用,某同学研发了一套自己的资源组织规则;又或者某位TA同学基于URP,重写了一套延迟渲染的流程,这些在我看来都属于引擎开发的范畴。
- 对于Unreal项目而言,某位同学没有动到引擎代码,从业务层设计了一套更高效的动画存储和加载框架,那么它是属于引擎开发的,而某位同学为了更方便的进行业务开发,从引擎层开放了一个面板参数进行数据配置,虽然改动了引擎源码但也不算是引擎开发。
所以我认为,如果某位同学的职能更多的是基于框架和系统来做玩法内容和乐趣体验的会被归为Gameplay;如果他的职能更多的是为游戏开发提供底层的扩展能力、优化框架和系统,增加游戏开发的技术边界等部分的内容可以算作引擎开发(如果只分引擎和Gameplay类别的话),当然如果愿意的话,也可以分更多的细类比如性能优化,工具开发,系统管线等。简单来说,提供能力的是引擎,提供内容的是Gameplay。
2 Unreal Gameplay 框架介绍
关于框架的理解,必然每个人还是有自己的看法。这里我们先就只讨论一下Unreal在文档中标记的GamePlay框架的内容,即:
- 游戏规则
- 角色
- 控制
- 相机
- 用户界面和HUD
兔子与蜗牛赛跑。
游戏框架的基础是GameMode。
GameMode 设置的是游戏规则,如首个跨过终点线的玩家即是冠军。其同时可生成玩家。
在
PlayerController 中设置一名玩家,其同时会产生一个Pawn。
Pawn 是玩家在游戏中的物理代表,控制器则拥有Pawn并设置其行为规则。本范例中共有2个Pawn,一个用于蜗牛而另一个用于兔子。兔子实际为
角色(Character),是pawn的一个特殊子类,拥有跑跳等内置移动功能。另一方面,蜗牛拥有不同的移动风格,可从Pawn类处直接延展。
Pawn可包含自身的移动规则和其他游戏逻辑,但控制器也可拥有该功能。控制器可以是获取真人玩家输入的PlayerController或是电脑自动控制的AIController。在本范例中,玩家控制的是蜗牛,因此PlayerController拥有的是蜗牛Pawn。而AI则控制兔子,AIController则拥有兔子角色,其中已设有停止、冲刺或打盹等行为。
相机(Camera)提供的视角仅对真人玩家有效,因此PlayerCamera仅会使用蜗牛Pawn的其中一个CameraComponent。
进行游戏时,玩家的输出将使蜗牛在地图中四处移动,同时
HUD将覆盖在相机提供的视角上,显示目前游戏中的第一名和已进行的游戏时间。
2.1 GameMode
在上面这个例子中,GameMode 决定的是游戏规则,即拥有两个角色,先跨过终点线的玩家为冠军。衍生的部分还有比如是否允许观战以及观战的人数最多为多少?玩家如何进入游戏,以及使用哪张比赛地图?游戏是否可以暂停,以及暂停之后如何恢复?游戏是否允许使用道具,又或者是否可以在游戏中作弊等,这些规则都是跑在服务器上的,确保规则的权威性和安全性。
GameMode在Unreal里的实现是AGameModeBase类(用A开头是因为它继承于Unreal的AActor,这是Unreal的类命名规则,可以查看代码规范),它是AGameMode的基类。一个项目可以拥有任意多的GameMode来设置各种各样的玩法,但同一时刻只能使用一个GameMode。
AGameModeBase提供若干基础的、可被override的接口:
- InitGame。 在这里做所有游戏规则的初始化工作。
- PreLogin 。登录前的预处理。由于GameMode只会跑在服务器上,可以在这里检查玩家的合法性,判定是否允许玩家登录服务器。
- PostLogin。登录后的后处理。玩家成功登录服务器之后的调用。
- HandleStartingNewPlayer。一般登录成功之后就会创建玩家在服务器上的对象,对象创建成功之后会调用该函数,可以在这里对玩家进行初始化,比如获取玩家的PlayerState。
- RestartPlayer。创建玩家的实体对象(可操控的,场景上可见的Pawn对象)。
- Logout。玩家退出或者服务器被销毁时调用。
其他的还有很多,这里只列举了一部分。
- 在工程的Project Setting下进行设置。
- 在DefaultEngine.ini的文件里进行设置
2.2 Game State
几个比较重要的函数:
- GetServerWorldTimeSeconds 服务器版本的游戏时间,权威可靠的,会被同步在客户端。
- PlayerArray。所有APlayerState的列表,对游戏中玩家执行操作和逻辑时候非常有用。
- BeginPlay。
还有一些其他的接口,如下。
需要注意的是,这仅仅是Unreal 从引擎侧实现的最小版本,在项目开发的时候,你可以使用它来扩展任意的Game State数据,并进行远程客户端的数据推送。
2.3 Camera
- 《英雄联盟》中,盖伦使用R斩杀了敌人之后,画面会表现出气浪冲击波的效果。
- 《尘埃》赛车游戏中,通过切换不同视角来完成第一人称和第三人称的驾驶体验。同时可以通过额外的摄像机渲染来完成后视镜的效果。
- 《黎明杀机》中,屠夫(第一视角)和逃生者(第三视角)的游玩视角不一样。屠夫可以通过佩戴“鹰眼”的技能来让视野变成类似于水滴透镜的效果,从而得到更开阔的视野。
- 《鬼泣》中,通过切换固定摄像机视角来完成走廊到房间的视角切换。或者模拟一个虚拟演唱会上的导播相机调度。
- 飞行游戏中可以通过设置轻微的动画来模拟穿过气流的颠簸感。航海游戏可以通过设置轻微的动画来表达海浪对船造成的轻微摇摆。常规的3D游戏可以使用弹簧臂的形式,让玩家躲在墙角或者被建筑遮挡的时候,相机不会穿模。
- 射击游戏中,通过改变相机的FOV参数完成狙击枪的模拟。格斗或者动作游戏中可以通过调用相机震动来调优“打击感”。
关于相机提升基础体验,总结为两点:
- 如何正确使用UE提供的相机和相机组件 使用摄像机
- 如何通过配置/开发相机动画完成 摄像机动画
2.4 Character
- 它的位置、旋转和缩放由 SceneComponent 中定义的Transform信息所决定。
- 它的可视化样子由 StaticMeshComponent 决定。
- 它如果发光就需要绑定一个粒子组件ParticleSystemComponent 。
- 它如果需要和周围环境进行交互,有实际的物理体积就需要绑定一个碰撞盒组件BoxComponent 。
回到角色上来,一个Character就是一个特殊的,可以行走的Pawn,一般代表垂直站立的玩家。也就是说它比Pawn多了 CharacterMovementComponent,同时,因为一个可行走的模型需要提供一些行走动画,所以还需要SkeletalMeshComponent 组件来提供骨骼框架,由于人的形状和盒子差别很大,所以在物理碰撞上用胶囊体CapsuleComponent来替换碰撞盒。
角色组件是一个Avatar,代表玩家在和游戏场景交互。并且可以在场景中行走、跑动、跳跃、飞行和游泳等,同样作为一个Actor,它也包含基础的网络功能,并接受玩家的输入控制。当然可以可以任意扩展和使用Character。
关于角色的拓展可以做的非常非常的深,包含动画,场景交互,物理等维度都是可以的。比如不使用刚体物理即可行走、跑动、跳跃、飞行、坠落、摔倒、游泳和攀爬等,比如在空气、水、沼泽,沙漠、雪地、太空等场景下中行进的速度、浮力、重力值,以及角色能对物理对象施加的物理作用力(魔法,科技等)等。再比如一些动画相关的表现:RootMotion,MotionMatching 新一代动画技术:Motion Matching,IK/FK等。
其他关于Character的基础介绍可以查阅:Setting Up a Character 。
2.5 Controller
过去我们在谈论UI框架的时候,一个被提及的最多的模式就是MVC。它把一个系统结构分为数据-视图-控制三个不同的关系层。目的是为了减少逻辑耦合,并让每个层的职能更加的专一化。相同的概念我们也可以引入到一些战斗的设计中,比如逻辑-表现分离,用事件或者协议来传递数据并驱动逻辑执行。
那么到Gameplay框架中,我们仍然能找到一个比较合适的部分来套用这套模式。比如我们现在的M就是Player State,我们的V就是Character,那么C自然就是马上要介绍的Controller了(如果要看系统性的介绍请看这篇 《InsideUE4》GamePlay架构(五)Controller)。
AController继承自AActor,也就是说它并没有场景实体,是一个场景不可见的对象。它拥有一个PlayerState,一个Pawn,如果这个Pawn同样是Character的话,那么它还有一个不为空的Character对象。
- Player Controller 。代表玩家的输入和控制。
- AI Controller 。代表AI或者远程玩家在本地的镜像。
其中Player Controller是玩家直接操控角色的逻辑类,因此非常复杂。大体可以分为Camera管理,Input响应,UPlayer关联和操控,HUD显示,关卡切换的逻辑处理,音效部分等等。而AI Controller因为不需要接受玩家操控,因此对Camera、Input、UPlayer关联,HUD显示,Voice、Level切换等部分都不是必须的,但对应的它增加了一些额外的模块,比如Navigation(导航),行为树,Task系统等实现。
2.6 HUD 和UI
2.7 其他
- Actors。不得不再次搬出大钊的文章《InsideUE4》GamePlay架构(一)Actor和Component,强烈建议大家系统性的学习他的“GamePlay架构”系列。因为文章视角不一样,我这里基本不会展开讨论细节。Actor除了继承自UObject的序列化、反射、内存管理等能力之外,额外实现的是组件的组合能力,Tick能力,网络复制能力和对生命周期的管控Actor 生命周期。
简单介绍一下上面这张图,它展示了Actor的三种实例化方式,但无论它是怎么“来”的,它“走”的流程是一样的。
三种模式是:
- 从磁盘加载
- Play in Editor
- Spawn
其中1和2十分相似,1是从磁盘里加载,2是从编辑器中复制。当实例化之后都会执行Post(Load || Duplicate)逻辑,InitializeActorsForPlay(UWorld 调用),再到RouteActorInitialize(Actor自己的组件初始化),再到关卡开始的逻辑调用BeginPlay。
3的逻辑不同,它是通过运行时生成的,所以执行的是PostCreate,然后需要执行对应的构造逻辑ExecuteConstruction来创建蓝图变量,然后用PostActorConstruction来执行Actor自身的组件初始化(其实和RouteActorInitialize 的主要一样),然后就是一样的BeginPlay。
虽然创建逻辑有差异,但销毁逻辑一致,执行了EndPlay之后,Actor就会被标记为RF_PendingKill,并在下个垃圾回收周期中被解除分配,然后有垃圾回收器将其回收。
- Timer 。不是很明白,为什么要把定时器单独归类到Gameplay框架中来。可能是因为AActor中提供了GetWorldTimerManager函数来获取FTimerManager的实例?定时器可以设置使用指定时间,或者指定帧来作为触发器。
- Movement Components 【图解UE4源码】其一 UCharacterMovementComponent的移动逻辑。除了人物移动之外,还有表示发射物/子弹移动的组件 ProjectileMovementComponent,以及一些特定的运动组件,比如RotatingMovementComponent 用来展示飞机螺旋桨,风车或者任何可以旋转的东西。
3 Unreal Gameplay 框架Runtime流程
在上一篇Unreal Engine 的启动流程 中,我们留了一个大坑。引擎的Init和Tick我们就只介绍了一点皮毛,也就是EngineLoop自身阶段的逻辑情况,那么真正跟开发者相关的部分还是EngineLoop调用了EditorEngine或者是GameEngine之后的Gameplay部分。
因为整个逻辑引擎的tick太多了,我们只聊一下跟Gameplay初始化相关的部分。先翻出这张包浆图:
求同存异,我们从共同的部分开始整(图上蓝色部分)。看一下UWorld::BeginPlay这个函数的介绍:Gameplay(梦)开始的地方,开始GameMode逻辑并且调用所有Actors的BeginPlay函数。
逻辑实现如下:
- 初始化所有World类型的Subsystem并调用它们的OnWorldBeginPlay函数。
- 根据服务器类型生成服务器的Actors
- 调用GameMode的StartPlay
- 如果有AISystem,那么StartPlay
- 进行WorldBeginPlay事件广播
- 初始化物理系统
到这里,我们的第一个Gameplay的元素GameMode已经开始工作了。那么接下来往下就是GameMode的StartPlay逻辑了。
GameState对所有Actors派发了BeginPlay事件,并广播了OnWorldMatchStarting事件。
那么其余的部分是在哪里初始化的呢?答案是在BeginPlay之前。不做全流程的代码细节分析了,贴两个前人已经做好的,想了解细节的可以看这两篇或者直接看源码。
UE4 Gameplay之GameMode流程分析(一)
UE4 Gameplay之GameMode流程分析(二)
上面的提到了GameMode的StartPlay流程,但它必须先初始化才能够执行StartPlay。它的初始化逻辑就写在 StartPlayInEditorGameInstance函数中,也就是当我们在编辑器里按下Play按钮之后。
本地玩家先要完成登录验证,然后会返回一个PlayerController,这个对象在Login逻辑中生成。
在Handle的字调用栈里就会去创建一个Pawn(Character)来跟Controller进行绑定。
然后创建HUD(事实上HUD的调用逻辑比 Pawn 早一点点,都是在 AGameModeBase::PostLogin里做的)。
到这里,Controller,Character,HUD都已经出现了,加上之前提到的GameMode和GameState,狭义上的Gameplay就只剩下Camera了。
Camera 因为关联着渲染,本身逻辑会复杂很多,并且初始化的时机也要提前很多,大部分时候和跟随场景一起加载了。但Camera并没有那么多花样,它就是视口和transform的信息,再加上渲染好的renderTexture用作后处理。Gameplay向的相机玩法更多的是做相机的动画和功能用途,比如跟随,切换视角,平滑轨迹或者用小型的摄像机动画模拟各种显示场景来达到沉浸感。
@大钊
画的好,大家直接看他的就好了《InsideUE4》GamePlay架构(十)总结。
4 Lyra工程中的Gameplay部分
严格来说它一点都不初级,甚至非常高级,说是当前Unreal 5的最佳实践也不过分。它向开发者展示了如何去重写一个项目的Gameplay,展示了最新的Unreal 5的特性和使用方式,甚至写了一些完全可以独立复用的Plugin插件。
本篇的重点还是Gameplay部分,所以我们着重聊一下ModularGameplayActors这个自定义的Gameplay扩展插件和Lyra基于它的业务逻辑。
其实这个思想很好,它用一套自己的Modular来隔离引擎和项目层,我自己的开发理念也是相似的,能不动引擎的尽量不动,自己写一些继承和扩展,既有自由度,又不会在未来引擎升级或者业务修改的时候造成兼容性的麻烦。
这个Plugin其实没有任何实质的内容,就是对所有涉及到的引擎原有模块做出继承,也就是充当了项目和引擎之间的缓冲带。
比如 AModularGameModeBase,AModularGameStateBase 等分别如下:
他们都只做了最基本的继承而已。那么重要的其实是在Lyra下的实现,我们一一来看。
最开始的自然是GameMode了,由于所有的基础模块都重写了,所以在构造函数里,需要将它们一一重新指定初始化。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/149909.html