1 游戏实体框架
虚幻四实体管理框架结构如下:
虚幻四中将显示对象与逻辑对象都统一为了Actor的派生类,图中称为物理与非物理Actor,非物理Actor为控制器,对物理Actor的行为进行控制。
2 移动同步方案
UE4移动组件继承关系图:
(1) UMovementComponent: 移动组件的基类, 实现了基本的移动接口SafeMovementUpdatedComponent(),可以调用UpdateComponent组件的接口函数来更新其位置;
(2) UNavMovementComponent:该组件更多的是提供给AI寻路的能力,同时包括基本的移动状态,比如是否能游泳,是否能飞行等;
(3) UPawnMovementComponent: 该组件可以实现接收玩家的输入并根据输入值修改所控制Pawn的位置;
(4) UCharacterMovementComponent: 该组件处理了各种常见的移动状态细节,实现了比较流畅的同步解决方案,各种位置校正,平滑处理达到较好的移动效果(该组件是Epic公司多年积累,较适合做第一,第三人称的RPG游戏)
UE4的角色移动同步通过CharacterMovementComponent实现,主要实现了以下几种类型且可扩展:
行走
导航行走
落下
飞行
游泳
自定义类型
主要采用以下结构:
对于玩家控制的角色来说(UE中称为Autonomous角色),其同步方案说明如下:
(1)客户端通过接收玩家的Input输入,开始进行本地的移动模拟流程,执行PerformMovement移动(TickComponent中执行);
(2)移动预测数据结构FNetworkPredictionData_Client_Character统一管理需要同步的数据,当前移动NewMove信息会存入一个SavedMoves列表中。
同步粒度是一个FSavedMove_Character结构,包括起始和结束位置的速度,旋转,时间等信息,两个位置信息;
(3)此处采用了一个优化带宽的策略,判断当前网络速度是否过慢或上一次发送时间过短,则不会发送当前的NewMove数据,而是将数据缓存至PendingMove延迟发送,否则直接调用ServerMove发送NewMove;
(4)下一次发送会判断当前的新的移动是否能与上次保存的PendingMove合并,若可以,就可以减少一次消息的发送。若不能合并,那么会调用ServerMoveDual,直接给服务器发送一个两次移动,服务器在受到两次移动的时候对第一次移动不进行任何校验,只对第二个移动进行正常的校验。
(5)若收到服务器的ClientAckGoodMove,表示服务器接收了对应时间戳的客户端移动,客户端就将这个时间戳之前的SavedMoves全部移除。
LastAckedMove缓存最近一次正确同步的位置,通过时间截查找LastAckedMove在SaveMoves中的位置AckedMoveIndex,并清理SaveMoves中AckedMoveIndex之前的位置,因此SavedMove中并不是每一个元素都是有效的。
(6)若收到服务器的ClientAdjustPosition调用,那么表示对应这个时间戳的移动有问题,客户端需要修改成服务器传过来的位置,并重新播放那些还没被确认的SaveMoves列表里面的移动。
同样会移除SavedMove列表中这个时间戳之前的数据,并将bUpdatePosition设置为true,在Tick中调用ClientUpdatePositionAfterServerUpdate函数时重新播放SavedMove列表中还没被确认的移动及最近一次修正后的移动,目的是让本地玩家觉得自己的位置拉扯不大。
(6)被清理的移动数据都会回收至FreeMoves中复用。
对于其他网络玩家控制的角色来说(UE中称为Simulate角色),同步流程比较简单:
(1)客户端收到服务器的同步数据时就直接进行设置,主要是通过AActor::OnRep_ReplicatedMovement,客户端在接收到服务器同步的ReplicatedMovement时,会产生回调函数触发SmoothCorrection的执行,从当前客户端的位置平滑的过度到服务器同步的位置。
(2)在没有收到服务器消息的时候根据上一次服务器传过来的数据(包括速度与旋转等)在本地执行Simulate模拟(保证本地表现流畅),等着下一个同步数据到来。
采用这种方式其实是为了减小同步带来的开销。
##参考
备注
完整版本迁移至UE4网络之基本概念