让玩家做主机我们做出的第二个重要的决定是让玩家成为游戏中的“主机”。我们之前选择只发送玩家操作的数据的意思是,每个玩家在点对点系统中对游戏的操作,将会传送给其他的所有玩家。因为因特网没有广播和多播的能力,所以每一条消息都会被复制然后发送N-1次,N是指游戏中玩家的数量。这也就是说28K的调制解调器的带宽会被分割成和玩家数量相应的份数。 如果有一个玩家扮演“主机”的角色,那么我们就能够明显的减少其他玩家的带宽压力,同时只是稍微增加了做主机的玩家的带宽压力。每一个玩家把数据发给主机,主机把所有的数据压缩进一个大的数据包,然后发送给每一个客户端。这种方法的优势在于,如果主机有一个快速的网络连接,那么游戏就可以支持许多低速连接的玩家。在游戏正式发布之后,这一点节省了我们大量的开销,只要主机玩家的带宽能够同时支撑起8名通过调制解调器连接游戏的低速玩家就没问题。 这种方法的另一个好处是我们不需要去考虑如何在不同玩家的机器之间同步数据,相反,我们只需要关心每名玩家如何把数据同步给主机。我们希望这个方法能够更早的实现“中途加入游戏”的机制,但是可惜的是,我们的游戏没能实现这一点。 尽管我们的测试是非常轻松就通过了,但是我们不认为我们接下来的工作也能这么容易。我们知道这种方法有它自己的问题。其中最大的一个问题也是我们最为担心,就是我们之前在“VCR”功能里已经看到过的,在回放玩家操作的时候,会产生出一些和原本战斗完全不同的结果。在以前,这样的差异bug当然不会带来什么样的害处。比如说,我们也许用了一个本地的布尔变量来表示无人机可能的两种行为。如果这个变量被意外的设置过,那么结果将会是依赖于栈上这个变量的值而产生出随机的结果。这种类型的bug通常是不被注意的,除非是在放一部电影。但是在放电影的时候,即使这个bug能够引起一艘飞船采取与记录的时候不同的行为。但这个细节会被电影中的其他部分掩盖掉,就好像那艘飞船本来就应该那样。 如果这种事情发生在多人游戏中,玩家将会敏锐的发现两种截然不同的结果。我们希望用两种方法来解决这个不一致的问题。首先,我们希望能够找到这个系统尽可能多的bug,这样也许可以提前避免这种问题的出现。其次,我们开发了一种方法能够检测到这个问题的发生,然后把需要同步的数据再发送一次。 这个方法的最大优势是只需要非常小的带宽。但我们还需要处理延迟的问题。在做了一些快速的测试之后我们意识到100毫秒的延迟,我们就无法控制飞船了。当你想要射击一个目标的时候,错过了准心而你又已经开火了,会让人产生巨大的挫败感。我们没有去改变游戏的操作方式,而是设计了一种系统,让玩家在他们的操作和飞船的行为反馈上感觉不到任何延迟。这项技术的关键在于使用一种类似所谓的“航位推算”的技术。 我们的解决办法是维持两份同样的游戏数据。第一份游戏数据只基于每一个玩家的操作,并且只在数据能够获取到的时候进行更新。第二份游戏数据是当前时间点的游戏状态,并且每帧都会进行更新。第二份游戏数据不能代表玩家的操作,因为玩家操作的数据因为因特网的原因造成了延迟。然而,这份游戏数据是基于对于玩家行为的预测将会产生什么样的结果。延迟越高,两份游戏数据的差距越大,预测的版本就越不准确。 我们的方法看起来解决了两个最常见的网络问题:带宽和延迟。带宽限制了我们能够发送玩家操作数据的最小值。延迟会造成游戏世界的数据异常(也就是我们常说的不同步),但是不会影响玩家的战斗操作。我们对自己的办法非常满意,而且认为我们确实做的非常聪明。 实现这个设计我们的第一步就是去实现这个网络模型并在我们的局域网进行测试。这个过程进展的非常顺利。第一个版本是一个简单的“同步”模型,在这个模型下所有的玩家必须等待,直到接收到了所有玩家的操作,然后才开始进行游戏世界的绘制。第一份游戏世界的拷贝所需传输的数据量非常小,只要很小的带宽就行,但是在明显的网络延迟下根本就无法工作。当某个玩家帧数比较慢时,还会出现拉回现象,全同步的意思就是所有玩家都向最慢的那个玩家同步。 这个版本非常容易去编码实现,因为我们没有实现对游戏世界的预测,并且我们甚至没有尝试去解决延迟问题。此外,我们还使用DriectPlay,这样在实现一个游戏场景的绘制,并让玩家加入的工作中,就只剩下很少的工作要做了。我们让这个版本非常快的上线了,这样我们的关卡设计师就可以开始设计多人关卡了。事实上这个同步的版本我们用了很长时间。因为这个版本很容易被测试,所以在当时的开发进度来看,实现一个网络版本优先级就没有那么高了。当我们最终开始编写我们的网络模块时,我们已经拖后了进度,并且影响了我们之后的一些决定。这也意味着之后我们要完全投身于网络模型,以及用户界面的开发。 快速实现我们的第一个版本带来的另一个大的好处是我们能够做出一些非常漂亮的处理方法去应对那些不同步的bug。由于这些方法的存在以及长时间的测试,我们解决了大部分的bug。我们还能够实现了多次同步的方式,并且在局域网的情况下,多次同步的速度非常之快,以至于你根本无法注意到不同步的bug出现。 当我们开始编写网络模块的时候,我们知道,我们的首要任务是基于第一个游戏世界的拷贝,来创建第二份拷贝。不幸的是,我们的游戏引擎不支持这个特性,所以实现这个功能花费了很大的代价。但是,一旦我们实现了这个特性,我们就给我们的延迟问题带来了奇迹般的效果,在局域网测试的时候,它工作的非常之好! 我们现在拥有了一个在局域网工作的非常好的游戏的版本。它只需要非常小的带宽,而且它还能够容忍500毫秒的延迟,而你压根感觉不到。鼓起勇气,我们开始在因特网上简单的测试它。发现它居然还行!直到数周之后我们开始认真的做一些测试时,才意识到我们的错误。 |