虚幻引擎的网络架构主要功能即是通过Replication Actor,Variable, function来保持服务器和客户端的信息同步。Unreal的每一个类都有一个复制语句Replication{}。这个块中可以进行变量和函数的复制。
这是在PlayerReplication中的复制语句
Replication { //服务器应该发送到客户断的数据。 if ( bNetDirty && ( Role == Role_Authority ) ) Score, Deaths, bHasFlag, PlayerLocationHint, PlayerName, Team, TeamID, bIsFemale, bAdmin, bIsSpectator, bOnlySpectator, bWaitingPlayer, bReadyToPlay, StartTime, bOutOfLives, UniqueId; if ( bNetDirty && ( Role == Role_Authority ) && !bNetOwner ) PacketLoss, Ping; if ( bNetInitial && ( Role == Role_Authority ) ) PlayerID, bBot; }
在上面代码中:
-bNetDirty表示variable中有值发生了变化才会进行复制。
-bNetInitial应该总是为true。
-如果Actor的Owner是当前客户端的PlayerController则为true。
上面的Replication模块定义了这个Actor中可以从服务器端发送给客户端同步的数据,这样可以只挑选很少的一部分进行传输节省了大量的带宽。还可以把Network编程的部分从代码中剥离出来看。
1.变量复制
一般是变量被服务器发送给客户端,但也存在少数从客户端将数据返给服务器端的情况发生。对于从客户端复制到服务器的情况,这个操作必须是在客户端的PlayerController中完成!
变量复制总是在每次更新的最后执行,并且是Reliable。
2.函数复制
尽管在单人游戏中将总是执行每一个function,但在网络游戏中可不一定是。例如脚底下Buff粒子播放的函数可以只需要给自己看见,而其他玩家不需要看到。因为函数复制和变量赋值先后顺序的问题,你得考虑复制的函数中包含那个要被复制的变量改变,可以将其当做这个函数的参数一并复制哦。
函数复制可以有Unreliable,
3.仿真
在服务器上每一个函数都会被执行,除非是从客户端发送来的。在客户端上,每一个函数都有一个起源,要么是从服务器 到 客户端的复制函数,事件函数或exec 函数。
对于Simulated函数,是为了给客户端支持接近Actor实际运行效果的函数,同时被Server和Client调用。考虑到一个Projectile撞击墙壁,这个函数可被设为Simulated,因为Client可以预测这个事件的发生。这会减少不必要的从服务器复制给客户端的网络传输延迟。大多的PostBeginPlay也是这个情况,在客户端的Actor会初始化料理好自己。
关于优化和提高性能:
对于网络设计,我们总是想以为玩家提高低延迟的游戏服务为宗旨的,所以在做网络编程时应该注意一下几个原则和内容:
- 最少化复制Actors的数量
- 最小化每次Actors在tick时的更新:将Relevancy的Actor数量尽量降到最低(下节讲Relevancy)
- 避免不必要的bNetDirty
- 避免不必要在服务器端的Actor Spawn,例如爆血粒子,让服务器端的玩家看到即可。
- 避免一些游戏性不相关的Relevancy,(好吧,这里还是讲Relevancy,就是说有时候一些Actor离你很远不会处于摄像机内,并影响到你。这时候你就不需要给这些Actor进行网络同步耗费带宽)
- exec function的数量尽可能降到最低
- 总之保持以下原则:1.保持每一个Client的Relevant Acto数量最小
- 减少每次更新的属性数量
- 减少传输包的数量
解压DevNetTraffic查看所有复制Actor的属性记录。Stat Net来查看对应的网络状况信息。