我们一开始的游戏逻辑层是基于网络包驱动的,也就是将 client 消息定义好结构打包发送出去,然后再 server 解析这些数据包,做相应的处理。
写了一段时间后,觉得这种方案杂乱不利于复杂的项目。跟同事商量以后,改成了非阻塞的 RPC 模式。
首先由处理逻辑的 server 调用 client 的远程方法在 client 创建出只用于显示表现的影子对象;然后 server 对逻辑对象的需要client 做出相应表现的操作,变成调用 client 端影子对象的远程方法来实现。
这使得游戏逻辑编写变的清晰了很多,基本可以无视网络层的存在,和单机游戏的编写一样简单。
本质上,这样一个系统跟网络包驱动的方式没有区别;但是从编码表现形式上要自然很多。正如 C 语言也可以实现面向对象,但却没有 C++ 实现的自然一样。在这个系统中,引擎封装了对象管理的部分,使得逻辑编写的时候不再需要处理讨厌的对象数字 id ;还隐藏了消息发送或广播的问题。
我把玩家控制的角色,和服务器上你的角色分做两个东西。即,你控制的你,和服务器认为的你就分开了。服务器认为的你,你看见的服务器上的其他人是一类东西。操作自己的角色行动时,你通过 client 上的控制器的远程方法向服务器发送指令;而服务器通过远程调用每个角色的远程方法让 client 可以收到感兴趣的所有角色的行为。
这样,client 永远都是通过一个控制器调用其远程方法来告诉服务器"我要干什么",而服务器的逻辑层则通过调用其上所有逻辑对象的远程方法来改变每个对象的状态。而引擎就根据每个链接的需要,广播这些消息,使得每个 client 上对应的影子对象可以收到状态改变的消息。
这些,就是半个月来我跟同事一起做的工作。当然,由于我们用脚本编写逻辑层,这样,脚本接口可以比 C 接口实现的漂亮的多。
首先是自定义格式的接口描述文件,用自编写的工具自动编译成对应脚本代码。我们只需要在脚本中编写对应的类,就可以自动响应远端调用的方法了。而调用远程方法,也跟本地方法保持同样的形式,写起来跟本地函数调用没有区别。这在以前用 C/C++ 编写逻辑的时候是很难做到的。
其次,引擎内部做好对象的管理工作,负责把通讯协议上的 id 转换成逻辑层中的对象传递给逻辑层使用。
再次,enum 这样的类型再也不需要用一些数字的常数了,也不需要在脚本额外的定义出来。可以在接口文件中定义好,经过引擎的处理后,逻辑层可以直接用更为友好的字符串代替,而不失去效率。
编写逻辑的程序员不再需要关心网络的问题后,就可以把心思放在细节上。
最后,对于实现行为预测来补偿网络延迟的特性上。在先前的版本中,我们为了实现这个,花了不少的气力。主要是将时间戳信息放在基础通讯协议中来辅助实现。具体的消息包收到后,再计算延迟时间来推算当前的状态。现在,可以把时间信息封装到 RPC 中,让每个远程方法自动带有延迟时间,方便计算。按模拟程序的实际效果上看,单单位置同步的预测策略,可以让延迟在 8 秒之内的玩家可以忍受;而延迟小于 1 秒的时候,几乎不会受到滞后的影响了。
关于每个链接感兴趣的信息的问题,决定了每个逻辑对象的状态改变要通知哪些人。目前的想法是独立到单独进程去处理,我们在处理连接的服务器和处理逻辑的服务器之间设置单独的服务器来管理每个链接感兴趣的对象,这个任务相对单一且责任重大,独立出来可以大大减轻逻辑服务器的复杂度。
posted on 2009-09-09 10:42
暗夜教父 阅读(857)
评论(1) 编辑 收藏 引用 所属分类:
Game Development