假如网络延迟始终保持理想,那真是万事大吉了。但是现在我面临一个很棘手的问题。
《PC游戏编程:网络篇》中关于延时同步等问题有几个核心观念:
1,客户端需要将操作命令发给服务端,然后等待确认发回才执行。
2,服务端提前一段时间将命令发回,以保证两边能在接下来的某一时刻同步开始执行。
3,发送的信息中包含服务端的执行周期,假如因为一些意外导致客户端落后于服务端,采取特殊方式补救。
第三点先放在一旁不作考虑。光就前两点来说,我的理解是信息一来一回的时间必须小于一个执行周期,即保证本周期内客户端发出的命令,本周期内能得到计算执行。但是我的动画帧,决定了两帧间播放的时间间隔不能太久,否则动作看起来极慢,实际来看不能高于100ms。而经验告诉我,网络游戏的延迟,可能经常达到200-300ms甚至更高。这样就存在矛盾了。
大致想了想,解决办法可能是需要一个操作触发两帧动画或更高,即按一下键盘人物走两步以上。
进一步深入思考,网络延时还可能随时变动,一会快一会慢的,程序可能就要考虑如何适应这种变化,真是头大啊。
(2012.8.12注:这里采用缓冲区可能有帮助)
(2014.3.27注:所谓“缓冲区”纯属胡扯,具体相关问题还需深入研究才知一二)
我先不管那么多,先来做一件事——获取延时值。就是常见的在游戏大厅建立游戏等待客户端加入时会不断显示已接入客户端的延时多少多少ms,或是打CS时按tab键随时能查看自己的延时状况那种。
具体我希望能在建立连接后,双方都能不断显示当前延时值,然后按space键表示准备完毕,当双方都准备后一起倒数五秒进入游戏。
这个问题看似简单,实现起来还是有点麻烦的,要考虑不断准确的发送和应答,还有按准备键的先后顺序变化等。
我一度试图寻找现成好用的ping函数,发现网上实现此功能的方法多靠一个叫ICMP的玩意,就是IP协议族下有个子协议专门负责传递一些控制信息,这个用来做ping很合适。参考了一些源程序,大致了解了方法。
不过最后想来想去还是决定用自己的土办法,因为考虑了一下用新办法可能要多开一个socket,到时又不知道会遇到什么新麻烦,节外生枝的试验一些新玩意感觉会绕远路,我就不信仅靠简单的send、recv函数实现不了ping功能。
另外也找到了一本书叫Programming.Multiplayer.Games,里头对网络游戏编程的各方面知识有较完整的介绍。还有大量代码演示,可惜似乎并不完整,比如里头列出的sendping函数,在最终的应用程序示例中未见使用。也没有中文版。
技巧:
测试过程中,不知什么原因,运行完程序后经常导致自己的电脑任务栏无响应,以至于得反复重启电脑,这真是件令人崩溃的事。后在网友的提示下,我多安装了一台虚拟机,然后在两台虚拟机上测试,让主机置身事外,这样就高枕无忧了。
期间经历了一次很狗血的bug。我在添加ping功能时错写了转入时的GAME_STATE值,让服务端进入了客户端处理函数,于是导致游戏开始后完全无法操控。为此还专门去研究了vs2008的一些调试方法,然后在程序中设置断点,结果却无法中断,由此才觉悟到是状态弄错了…
头疼的是,可能由于在ping过程中发送了一些额外的数据而对方来不及接收,残留在缓存中,导致游戏开始后,人物会出现自动动作,进一步导致各种控制异常bug。对此我并不确定,因为对收发的缓存机制并无深入了解。
试着在游戏开始前,加入了一段收空缓存区的代码,果真就恢复正常了,看来推测没错。
细细揣摩了下,大致看懂了为什么会有额外的数据,因为服务端在通知客户端已按下Space准备好之后,客户端知晓此事有一定延时,而在这段时间内,客户端仍在不断发送自己已准备好的信息。
但是当我完善程序,使服务端也能显示delay值后,出现了总是有一点尾巴收不干净的状况,实在搞不明白为什么。最后我在5秒倒计时之后再次收空缓存,问题就得到了解决。
这让我意识到,有必要对命令包添加一些头部辨识标志,这样好对接收到的信息先进行过滤,抛弃那些莫名其妙的垃圾信息。
我迫不及待的去互联网上找人测试这个程序的效果。
对方在杭州,用笔记本通过无线路由上网。整个功能基本都能跑通,显示他的delay值在200-600ms间波动,这个比较严重了,而实际感觉延时据说能有5秒!!…
经过这次测试,我忽然意识到一个问题,我完全无法获得实际网络测试环境,而远程同人沟通又仍是极为困难且无法获得准确信息。那么凭空假想出的诸多问题就算都解决掉了,最终也是无法得到验证的。当然肯定有专业的工具能模拟出所需的网络环境。但实际的网络状况错综复杂,到底需要解决哪些问题,又非我这个纯菜鸟所能预料。
所以我决定暂时不在网络这一块过多纠缠了。前面还有好多东西要学,而假如将来做一个客户端逻辑程序员,对网络的要求可能仅仅是了解就够了。
尽管这段网络程序还存在诸多漏洞和缺陷,我得暂时扔下这个烂摊子,进入下一阶段的工作了。
备忘下网络部分程序有待完善的几点:
1,健全各种错误处理机制,即便发生错误时也能妥善顺利退出。
2,对发送的信息进行封包处理,有利于接收端判断正确性及完整性。
3,采用更优的信息传递流程以解决现行方法对延时的扩大化问题,即信息最好是实时收发,在程序中不要再加上人为的停留。
4,针对可能出现的恶劣网络环境作相应处理。
对目前程序收发数据的机制做一个备忘:
每3个游戏循环作为一个网络周期。每个周期内第1、2、3个循环程序分别做以下事情。
服务端:
1,更新键盘输入
2,接收客户端命令并汇总,发回
3,解包命令,计算执行
客户端:
1,更新键盘输入,发送命令给服务端
2,无
3,接收命令,解包,计算执行
目前每个循环周期为30ms,所以网络延时也在30ms内为理想状况。至于超出此范围产生的后果,未作进一步分析。
接下来我想做一些相对轻松的工作,大致了解人工智能及游戏数学物理方面的知识,之后完成计划的第三部分——编写一个AI。
再之后就是多线程,然后可能回来重写部分网络程序。
再之后就是MFC及菜单的制作,还有资源打包等。
最后是动画效果、音效的完善等。
对了,差点把directx的3D部分还有Unity3D给忘了…要做的事可真多啊!
另外基础的C++特性及算法等东西也要加倍重视,可以看到在这段时间的学习过程中,很多这方面的东西自然而然都接触到并有了更深的了解。