在上一篇文章中,已经可以在服务器上直接根据服务器自己的操作指令,模拟得出结果,修改球的位置了,接下来,将要考虑如何将服务器模拟的位置如何同步到客户端.

1.服务器向客户端发送单位实体(Entity)状态

首先需要设定一个发包的频率(SendRate),目前设置的是每10个模拟帧发送一次,对于60模拟帧每秒的游戏世界来说,这也相当于6个包每秒.这个包的数据应该是描述Entity在当前模拟帧的状态.

public class State

{

public int frame; //模拟的帧号

public Entity entity; //所属的Entity

public List properties; //需要描述的属性

public int Pack(Packet packet)

{

packet.Write(frame);

//将属性数据写入消息包packet

}

public int Read(Packet packet)

{

frame = packet.ReadInt();

//从消息包中取出属性数据

}

}

发送的方法:

public void FixedUpdate()

{

if (Core.frame % SendRate == 0) //每隔10帧发送一次

{

foreach (var conn in connections)

{

conn.Send();

}

}

}

//connection中发送的方法

public void Send()

{

Packet packet = PacketPool.Get();

foreach(Entity entity in entities)

{

entity.currentState.Pack(packet); //将当前状态数据写入消息包

}

_connection.Send(CustomMsgTypes.InGameMsg, packet.msg_untiy); //通过UnityEngine.Networking组件的Connection发送数据

}

这样就把Entity的状态打包发向所有的客户端了.

2.客户端接收到服务端的状态包

客户端接收到服务端的数据包,然后从数据包中拿到描述Entity状态的数据后,需要考虑的是,如果是第一个状态,可以直接拿来应用到Entity上,如果不是第一个状态的话,那就不能直接应用,因为网络传输抖动的因素,服务端虽然是每隔10帧发一个包,但是客户端收包频率不一定是每隔10帧就收到的,如果直接应用的话,必然会导致抖动.这个时候,我们就需要在客户端对服务器端进行状态缓存(StateBuffer)和状态插值(StateInterpolation).

1.为什么需要状态缓存和状态插值

客户端收到的状态包都是带帧号(Frame),帧号表示了这个状态是服务器在那帧模拟得到的状态,客户端想要,去除抖动,平滑的渡过的状态之间的时间的话.就需要在State_A与State_B进行插值计算.插值计算的公式应该是这样

Current = MathUtils.Interpolate(State_A, State_B, ???? / (State_B.frame -State_A.frame ))

在公式右侧,除了????,其他都是已知的,想要得到插值结果,那么????应该是什么呢?

因为分母的两个状态的帧号差,所以分子应该也是帧号才对,客户端的帧号跟服务端帧号不一致(因为服务器肯定早就启动了,客户端是后来才连接服务器的),这个时候就要新增一个变量用来表示客户端估算出来的服务器帧(RemoteEstimatedFrame).

这个估算帧用来表示客户端在本地估测服务器模拟的帧号,它的第一次赋值应该是客户端收到服务器的帧号时,

// 调整远程估算帧

public void AdjustRemoteEstimatedFrame()

{

if (packetsReceived == 1)

remoteEstimatedFrame = remoteActualFrame; //当收到第一个包时,将包的帧号赋值给估算帧

}

估算帧也是按照模拟频率一直累加的,但是估算不一定总是准的,有时提前收到包,有时延迟收到包,甚至丢包.所以如果收到的包帧号跟估算帧相差太大的时候,就需要对估算帧重新调整

public void AdjustRemoteEstimatedFrame()

{

if (packetsReceived == 1)

remoteEstimatedFrame = remoteActualFrame; //当收到第一个包时,将包的帧号赋值给估算帧

else

{

remoteDiffFrame = remoteActualFrame - remoteEstimatedFrame;// 差异=实际收到的帧号-估算帧

if (remoteDiffFrame < minDiff || (remoteDiffFrame > maxDiff) //如果差异太大的话,估算帧就要重新赋值

{

remoteEstimatedFrame = remoteActualFrame;

}

}

}

效果如下:

server 1.gif

从这个图可以看出,服务器移动很平滑,但是客户端移动可以明显看出抖动的情况,问题在哪呢?其实问题是出在估算帧的设置问题,从状态A插值到状态B的过程,由于估算帧等于(或者接近)状态A的帧号,而状态B的包客户端还没有收到,这就造成了在状态B到来之前,客户端没办法插值,只好原地等待,当状态B的包到来的时候,立即设置了位置,所以造成了抖动,那么如何解决这个问题呢?

做法是故意让估算帧的帧号在实际的状态包帧号之前,让客户端滞后:

public void AdjustRemoteEstimatedFrame()

{

if (packetsReceived == 1)

remoteEstimatedFrame = remoteActualFrame - delay; //当收到第一个包时,估算帧 = 包帧号 - 延迟

else

{

remoteDiffFrame = remoteActualFrame - remoteEstimatedFrame;// 差异=实际收到的帧号-估算帧

if (remoteDiffFrame < minDiff || (remoteDiffFrame > maxDiff) //如果差异太大的话,估算帧就要重新赋值

{

remoteEstimatedFrame = remoteActualFrame - delay;

}

}

}

将delay = 10(因为服务器每10帧发个包)这样尽可能的预留出一个状态包用来做插值计算了,看看效果:

server 2.gif

可以看到客户端的抖动几乎看不出来了,但是代价是延迟比较大了(为了更好的表现,这个牺牲是必要的)

3.小结

服务端模拟结果,下发状态给客户端基本就完成了,需要补充的是,在估算帧的计算中,可以根据估算帧和实际帧的差距动态的调整本地模拟的频率,比如:

如果估算帧滞后太多了,那客户端就每帧加2,甚至加3(默认是每个模拟帧加1)来追赶.

如果估算帧超前很多,那客户端就估算帧的累加可以暂停来等待,通过这样的方式来缓和.

现在客户端通过插值,实现了比较平滑的表现,但是有比较明显的延迟,这个可以通过加大发包的频率来缓解这个问题.

后续实现了客户端的预表现后,这个问题也就不那么重要了.

unity响应服务器消息,[从零开始的Unity网络同步] 5.服务器将状态同步给客户端(状态缓存,状态插值,估算帧)...相关推荐

  1. dns服务器没显示,win10连接网络显示dns服务器没有检测到响应

    小编整理了以下2种解决方法大家可以参考以下! 方法一: 有可能是由于无线网络连接里面的DNS服务器错误,导致的网卡断网 解决方法: 1.在电脑右下角右击连接的网络图标,如图,选择"打开网络和 ...

  2. 反恐精英出现服务器消息,cs你已被禁用次服务器 | 手游网游页游攻略大全

    发布时间:2016-06-13 有玩家在论坛发帖说明明有人口,地上有个科技资源,人口正常工作,然后在地上造了个基础科学研究所,奇迹发生了,人口瞬间失业,提示该建筑已经被禁用,下面为大家分析下. 群星 ...

  3. 电脑中显示dns服务器可能不可用,Win7网络诊断“DNS服务器可能不可用”怎么解决?-电脑自学网...

    Win7旗舰版显示dns配置错误怎么办?相信使用过win7旗舰版的用户可能都或多或少遇到过没有办法正常联网的现象,那么这是因为什么原因呢?通过网络诊断后可以发现是dns配置出错引起的无法上网的问题,不 ...

  4. Linux高并发服务器开发---笔记4(网络编程)

    0705 第4章 项目制作与技能提升 4.0 视频课链接 4.1 项目介绍与环境搭建 4.2 Linux系统编程1.4.3 Linux系统编程2 4.4 多进程 1-9 10.进程间通信☆☆☆ 4.5 ...

  5. 04、用浏览器显示一个网页时,是否只发了一个网络请求给服务器?

    目录 服务器 思考 服务器 用户量.数据访问量越大,对服务器的性能要求越高 当我们在浏览器上输入网址的时候,比如baidu.com,那就会发送请求给这个服务器,如果同时有一亿个用户在浏览器输入百度网址 ...

  6. ntp授时服务器(NTP网络时间服务器)应用公交数据网络

    ntp授时服务器(NTP网络时间服务器)应用公交数据网络 ntp授时服务器(NTP网络时间服务器)应用公交数据网络 随着城市经济社会发展以及人口和机动车快速增长,建设新城市交通体系,改善城市公共交通运 ...

  7. 网络开发——Unity中的消息分发器

    消息分发器 当客户端接收到服务器数据的时候,Controller或者Model层 通过订阅的方式,可以从数据中心回调到数据,减小代码的耦合程度. /// <summary> /// 网络通 ...

  8. UniRx - Unity响应式编程插件

    本文首发于"洪流学堂"公众号. 洪流学堂,让你快人几步!你好,我是你的技术探路者郑洪智,你可以叫我大智(vx: zhz11235). 本文译者:郑洪智 - 你的技术探路者 翻译日期 ...

  9. 网络断网服务器未响应,断网服务器未响应

    断网服务器未响应 内容精选 换一换 ELB的常见异常返回码有400.403.502.504等.若遇到这些返回码建议您先直接访问后端云服务器,查看是否是后端云服务器的异常.若后端云服务器响应正常,请参考 ...

最新文章

  1. 计算机性能指标ppt,计算机网络分类与主要性能指标.ppt
  2. 瑞士银行开户条件有哪些,瑞士银行开户的流程及注意事项是什么?
  3. JSP(1)—基础知识
  4. Node.js 目录操作
  5. 《火球——UML大战需求分析》(第1章 大话UML)——1.5 小结和练习
  6. Java并发编程之ThreadGroup
  7. pythonjson安装_python安装simplejson
  8. 1486mysql,mysql_error.md
  9. 据说这些基础知识90%的人都回答错了,你呢?
  10. Python(六)基于 TCP/UDP 协议通信的简单套接字编程
  11. opencv 证件照背景替换-KMeans
  12. [无视][mark]退役记
  13. 联想e470加装固态硬盘_ThinkPad E470C怎么安装固态硬盘?
  14. 筑讯建筑:北京玫瑰博物馆——镂空的玫瑰盒子,从建筑外观判断建筑功用
  15. Python数据可视化之南丁格尔玫瑰图
  16. 《自控力》与《学习之道》
  17. 【Vue作业]---Vue登录注册界面
  18. 涂色问题 阿里编程机试题目
  19. java代码借助插件生成组织架构图并实现导出功能
  20. 电子商务计算机网络安全技术教案,网络安全技术教案.doc

热门文章

  1. Flink 异常 - 12.java.lang.IncompatibleClassChangeError: Implementing class X StreamTableEnvironment
  2. LaTeX 算法环境宽度调整 Algorithm Width
  3. datax,datax-web使用
  4. 部标监控平台指令开发
  5. php工程师工作内容描述,php程序工程师岗位职责
  6. Hbase系列-3、Hbase高级
  7. 发基因组学、生物医学《SCI》《Nature》的最好方向!
  8. IDEA常用快捷设置
  9. linux设置快捷命令
  10. win7系统怎么分区【系统天地】