1.GameInfo和Network初始化
在一个单独类中是使用WorldInfo.Game来获取当前的游戏类型的,在客户端是没有GameInfo,即WorldInfo.Game=none。而只有服务器端才会具有,因为只有服务器端规定了游戏规则。
在游戏启动的时候,GameInfo会进行以下序列:
- event InitGame(string options,out string ErrorMessage),即服务器启动游戏时调用这个函数,解析URL选项,如 Unreal.exe MyLevel.rnr?Game=unreali.teamgame,其中Option字符串是?game=unreali.teamgame。错误将返回失败
- event PreLogin(string Options,string Address,out string ErrorMessage,out string FailCode), 当客户端登录时调用这个函数,使得服务器可以拒绝玩家,这是服务器可以验证玩家的密码,执行限制。
- event Login(string Portal,string Options,out string ErrorMessage),当调用PreLogin正确后调用,负责使用Option中的参数来生成玩家,成功返回PlayerController,Login和PostLogin也负责在单机游戏中创建PlayerController
- event PostLogin(PlayerController NewPlayer) 可以为函数赋值等操作。
2.预测玩家移动
Unreal不是使用了纯粹的客户端-服务器模式,否则玩家的运动是非常缓慢的,我估计大多数国产网络游戏使用的是这种方式(大牛勿喷哈:D)。
若是传统的网络模式,会是这样:如果网络延迟是300ms,那么游戏延迟将会达到300ms。Unreal使用了QuakeWorld的预测方式,即在UnrealScript中进行实现。这是在PlayerController中实现的一个高级功能,而不是网络代码的功能。
Unreal客户端的运动预测是基于网络代码中的复制功能设计的。在PlayerPawn中可以查看其代码,这种方法被描述为锁步,预测/校正 算法。
客户端考虑了操作键盘,手柄,鼠标,物理力(重力,浮力,区域速度)并把它描述为3D加速度矢量。在复制函数中调用ServerMove,客户端把它的加速度及各种输入相关的信息及它当前的时间戳(WorldInfo.TimeSeconds)发送给服务器端。由于过程详细且重要,我直接将流程表复制以下方便观察:
服务器 | 客户端 | |
---|---|---|
ReplicateMove() | ||
调用这个函数替代 ProcessMove()。 根据玩家输入进行 pawn 物理更新,保存(在 PlayerController SavedMoves 中)并复制结果。 SavedMove 可以是子类,保存游戏指定的运动输入和结果。 ReplicateMove() 也会尝试结合复制的运动来保存上游带宽并改善服务器性能。 | ||
ServerMove() | <- | CallServerMove() |
根据接收到的输入进行 pawn 物理更新,然后将结果与客户端发送的结果对比。 注意根据客户端计时器进行运动更新。 如果客户端已经积聚了一个严重的位置错误,那么请求校正。 否则,请求确认运动适宜。 | 发送一个或两个当前运动(根据帧速率和可以使用的带宽),它们附带客户端时钟时间戳记。 每次发送两个运动可以保存带宽,但是会增加校正的延迟时间。 在包丢失的情况下也可以调用 OldServerMove() 重新发送最近的“重要”运动。 | |
SendClientAdjustment() | -> | ClientAckGoodMove() |
推迟到 PlayerController 记号的末端的客户端相应可以在多个 ServerMoves() 接收到这个记号的情况下避免发送多个响应。 如果没有错误,那么确认运动适宜。 | 根据时间戳记的环回时间更新 ping,通过以前的时间戳记清除保存的运动。 |
服务器 | 客户端 | |
---|---|---|
SendClientAdjustment() | -> | ClientAdjustPosition() |
推迟到 PlayerController 记号的末端的客户端相应可以在多个 ServerMoves() 接收到这个记号的情况下避免发送多个响应。 如果有错误,请调用 ClientAdjustPosition() 确认运动适宜。 | 使用校正时间戳记之前的时间戳记清除 SavedMoves。 将 Pawn 移动到服务器指定的位置,然后设置 bUpdatePosition。 | |
ClientUpdatePosition() | ||
在 bUpdatePosition 为 true 的情况下通过 PlayerTick() 调用这个函数。 重新播放所有未完成的 SavedMoves 使 Pawn 返回当前客户端时间。 |
在服务器端有一些非关联性的动画不需要执行,例如你在看CS中丢手雷,手雷出去但并没有看到手丢的动作,我们使用bUpdateSkelWhenNoRendered和IgnoreControllersWhenNotRendered为false来让其在服务器端不更新。
对于尸体bTearOff=true将不会被复制到客户端,并且在已经复制了这个Actor的客户端上关闭变成一个Role_Authority。当接收到bTearOff,会调用TornOff()
3.武器开火
- 一旦玩家输入开火,客户端将会立即播放开火特效。并调用ServerStartFire()和ServerStopFire()函数来要求服务器开火。
客户端有足够的信息来预测当前是否在开火,例如根据(子弹数量,武器时间状态)来预测武器是否开火。
- 服务器Spawn Projectile,这个Projectile将会被复制到Client。
Projectile:
Projectile也可以很轻易的进行预测。
bNetTemporary=true
当被复制后,Actor Channel将会被关闭,Actor将永远不会被更新,Actor将会被client,Destroy掉。
涉及网络部分的内容还非常的多,只有深入操作才能更好的理解和记忆。下一章将会进行实例代码。