Fps Sample的源码规模比较庞大,我们先从宏观层面来剖析一下,下一篇会结合源码整个游戏的流程控制做一个讨论,同时未来还会对我认为这个Sample最具价值的部分————网络消息传输,客户端预测以及服务端的延迟补偿进行深入的探讨。
一、Game
Game类(game.cs)是运行时第一个被初始化的类,我们可以认为是整个游戏的入口类,因为FPS Sample中所有其他的代码,都是被Game.Update驱动起来运行的。(这个有点像用C++写游戏时的main.h/main.cpp)
Game类中创建了多个子系统的,例如Audio、Input、LevelManager等。
Game对象内部的IGameLoop接口,用于提供统一的初始化、销毁,以及更新。
目前实现了IGameLoop的接口的类型有ServerGameLoop,ClientGameLoop以及PreviewGameLoop。
- ServerGameLoop
ServerGameLoop(ServerGameLoop.cs)用于处理客户端的连接,以及更新所有的服务端系统
- ClientGameLoop
ClientGameLoop(ClientGameLoop.cs)用于处理客户端到服务器之间的连接,以及更新所有的客户端系统
- PreviewGameLoop
PreviewGameLoop(PreviewGameLoop.cs)把客户端和服务端合并在一起,用于在预览游戏的时候,不用单独构建游戏包,或者单独启动服务端或者客户端。
当我们在编辑器中进入Play模式,在场景加载完成之后,Preview会作为gameloop来驱动游戏运行。
二、主要模块
为了让代码的组织更合理,FpsSample把服务于同样功能的源码,组织到了一起,也就是模块化。
FpsSample中的所有游戏模块的源码包含在:<u>Assets/Scripts/Game/Modules</u>文件夹下。
模块中包含了大量的组件,这些组件由对应的System去负责进行更新。
Player模块
客户端的Player模块处理由服务端广播下来的消息,维护客户端用户的输入指令,以及对于本地操控角色的更新等处理。
服务端的Player模块为新连接到服务器的玩家构建一个Player Entity(玩家实体)。Player Entity包含诸如玩家的ID和名字、控制状态等数据。
每一个客户端程序均包含一个LocalPlayer实例(特别说明,这里的LocalPlayer是指有本地玩家控制的Player实例,而不是代码目录中的Locallayer.cs这个类),LocalPlayer里面拥有本地表现用的组件(例如摄像机控制和界面相关)。例如在CharacterModule中的LocalPlayerCharacterController组件添加到LocalPlayer后,LocalPlayer实例就具备了控制Character的能力。总之,对于一个Entity而言,附加了什么样的组件,就会具备对应的能力,这就是组件的魅力!
ReplicatedEntity 模块
ReplicatedEntity处理GameObject和ECS实体的复制。
对于GameObejct而言,只有附加了ReplicatedEntity组件的Prefab才能被复制,这个约束是为了确保被复制的Prefab注册进RelicatedEntityRegistry的。
对于Entity而言,通过创建一个继承自ReplicatedEntityFactory的ScriptableObjct来复制,这个类也有注册进ReplicatedEntityRegistry。
Character模块
Character模块服务于角色属性,动画以及UI表现。
FPS Sample中的Character通过Prefab来定义的(所有的CharacterPrefab都在Assets>Prefab>Characters下面).
FPS Sample有3个Character类,分别呢是Character、Character1P。其中Character是在服务端和客户端的第三方玩家显示,Character1P用于第一人称相机模式下。
Item 模块
Item模块处理角色的道具。
当道具执行Update时,读取相关属性的状态,用于触发相关的特效。
Effect 模块
- Effect模块控制客户端特效(图形特效和声音特效)的播放。
- 游戏启动后,在一个静态的特效池内创建特效。
- Effect当前支持HitScanEfect和SpatialEffect两种特效。
- 特效属性的通过ScriptableObject来配置。(例如SpatialEffectTypeDefine定义了特效使用的Prefab和特效缓存池的大小。)
- 特效的播放目前是在代码中调用的,通过特效的Request类(例如SpatialEffectRequest)来创建特效的实体。
HitCollision 模块
HitCollision用于处理所有对象的碰撞检测,进行游戏内部的伤害处理。
服务端存储了碰撞体的一些位置和旋转信息,来支持把碰撞体的信息回滚到某一个ServerTick再进行碰撞检测。这个方法用于服务端的延迟补偿。
Projectile 模块
这个模块处理自动推进的武器在服务端的模拟,以及在客户端的视觉表现(例如RPG的发射出火箭炮)。
在FpsSample中,Projectile模块也使用了客户端本地预测的功能,具体未来讨论到。
Ragdoll 模块
Ragdoll即所谓的布娃娃系统,这个模块用于更新游戏中已激活的布娃娃的状态。
SpectatorCam 模块
SpectatorCam模块用于测试控制一个非玩家角色。
三、关于ECS
FPSSample中几乎所有的游戏逻辑都是基于ECS模式来实现的,但是是和原来的GameObject和MonoBehavior现在结合还不太好,例如例如纯ECS创建的实体不能在Hierarchy视图里面查看。因此FPSSample并没有使用纯粹的ECS模式,未来随着ECS模块的成熟,FPSSample团队会把更多的源码基于纯ECS实现。
四、写在最后
接下来,我会花些时间把客户端预测和延迟补偿这部分代码给消化掉,然后分享给大家。
受限于笔者的技术能力有限和英文水平有限,难免有偏颇之处,希望朋友们雅正、谅解!