unity响应服务器消息,[从零开始的Unity网络同步] 5.服务器将状态同步给客户端(状态缓存,状态插值,估算帧)...
在上一篇文章中,已经可以在服务器上直接根据服务器自己的操作指令,模拟得出结果,修改球的位置了,接下来,将要考虑如何将服务器模拟的位置如何同步到客户端.
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.服务器将状态同步给客户端(状态缓存,状态插值,估算帧)...相关推荐
- dns服务器没显示,win10连接网络显示dns服务器没有检测到响应
小编整理了以下2种解决方法大家可以参考以下! 方法一: 有可能是由于无线网络连接里面的DNS服务器错误,导致的网卡断网 解决方法: 1.在电脑右下角右击连接的网络图标,如图,选择"打开网络和 ...
- 反恐精英出现服务器消息,cs你已被禁用次服务器 | 手游网游页游攻略大全
发布时间:2016-06-13 有玩家在论坛发帖说明明有人口,地上有个科技资源,人口正常工作,然后在地上造了个基础科学研究所,奇迹发生了,人口瞬间失业,提示该建筑已经被禁用,下面为大家分析下. 群星 ...
- 电脑中显示dns服务器可能不可用,Win7网络诊断“DNS服务器可能不可用”怎么解决?-电脑自学网...
Win7旗舰版显示dns配置错误怎么办?相信使用过win7旗舰版的用户可能都或多或少遇到过没有办法正常联网的现象,那么这是因为什么原因呢?通过网络诊断后可以发现是dns配置出错引起的无法上网的问题,不 ...
- Linux高并发服务器开发---笔记4(网络编程)
0705 第4章 项目制作与技能提升 4.0 视频课链接 4.1 项目介绍与环境搭建 4.2 Linux系统编程1.4.3 Linux系统编程2 4.4 多进程 1-9 10.进程间通信☆☆☆ 4.5 ...
- 04、用浏览器显示一个网页时,是否只发了一个网络请求给服务器?
目录 服务器 思考 服务器 用户量.数据访问量越大,对服务器的性能要求越高 当我们在浏览器上输入网址的时候,比如baidu.com,那就会发送请求给这个服务器,如果同时有一亿个用户在浏览器输入百度网址 ...
- ntp授时服务器(NTP网络时间服务器)应用公交数据网络
ntp授时服务器(NTP网络时间服务器)应用公交数据网络 ntp授时服务器(NTP网络时间服务器)应用公交数据网络 随着城市经济社会发展以及人口和机动车快速增长,建设新城市交通体系,改善城市公共交通运 ...
- 网络开发——Unity中的消息分发器
消息分发器 当客户端接收到服务器数据的时候,Controller或者Model层 通过订阅的方式,可以从数据中心回调到数据,减小代码的耦合程度. /// <summary> /// 网络通 ...
- UniRx - Unity响应式编程插件
本文首发于"洪流学堂"公众号. 洪流学堂,让你快人几步!你好,我是你的技术探路者郑洪智,你可以叫我大智(vx: zhz11235). 本文译者:郑洪智 - 你的技术探路者 翻译日期 ...
- 网络断网服务器未响应,断网服务器未响应
断网服务器未响应 内容精选 换一换 ELB的常见异常返回码有400.403.502.504等.若遇到这些返回码建议您先直接访问后端云服务器,查看是否是后端云服务器的异常.若后端云服务器响应正常,请参考 ...
最新文章
- 计算机性能指标ppt,计算机网络分类与主要性能指标.ppt
- 瑞士银行开户条件有哪些,瑞士银行开户的流程及注意事项是什么?
- JSP(1)—基础知识
- Node.js 目录操作
- 《火球——UML大战需求分析》(第1章 大话UML)——1.5 小结和练习
- Java并发编程之ThreadGroup
- pythonjson安装_python安装simplejson
- 1486mysql,mysql_error.md
- 据说这些基础知识90%的人都回答错了,你呢?
- Python(六)基于 TCP/UDP 协议通信的简单套接字编程
- opencv 证件照背景替换-KMeans
- [无视][mark]退役记
- 联想e470加装固态硬盘_ThinkPad E470C怎么安装固态硬盘?
- 筑讯建筑:北京玫瑰博物馆——镂空的玫瑰盒子,从建筑外观判断建筑功用
- Python数据可视化之南丁格尔玫瑰图
- 《自控力》与《学习之道》
- 【Vue作业]---Vue登录注册界面
- 涂色问题 阿里编程机试题目
- java代码借助插件生成组织架构图并实现导出功能
- 电子商务计算机网络安全技术教案,网络安全技术教案.doc
热门文章
- Flink 异常 - 12.java.lang.IncompatibleClassChangeError: Implementing class X StreamTableEnvironment
- LaTeX 算法环境宽度调整 Algorithm Width
- datax,datax-web使用
- 部标监控平台指令开发
- php工程师工作内容描述,php程序工程师岗位职责
- Hbase系列-3、Hbase高级
- 发基因组学、生物医学《SCI》《Nature》的最好方向!
- IDEA常用快捷设置
- linux设置快捷命令
- win7系统怎么分区【系统天地】