联网很难
从联网的角度来看,游戏是一个相当宏伟庞大的项目,需要客户端之间实现无缝实时同步。正由于如此,客户端/服务器的双向通信必不可少。在现代互联网架构中,这种通信机制由Web Sockets来提供,它在TCP上提供了薄薄的一层,把许多繁琐的细节隐藏起来,不让实现者看到。为进一步隐藏网络堆栈方面的细节,我使用了socket.io库,该库为整个游戏提供了一种异常简单的事件驱动抽象层。遗憾的是,目前不支持二进制数据,不然可以大大压缩消息大小——拿《星噬》来说,压缩后也许可以减少一两个数量级。
广州网站建设,网站建设,广州网页设计,广州网站设计
经过一番研究,包括我与知名的HTML5开发专家Rob Hawkes进行的那次深入讨论后,清楚地发现:要获得任何一种共享体验,最简单的模式就是在服务器上有真正的游戏状态,让客户端定期与它进行同步。这方面需要取舍的主要是同步质量与所需的网络流量。
在一个极端情况下,如果游戏逻辑完全在服务器上,以每秒60帧的速度将更新内容(或者可能仅仅是屏幕截图)发送到客户端,就可以编写游戏,但是由于这种模式需要数量庞大的带宽,所以这个做法一般行不通。在相反的极端情况下,你可以设想这种网络架构:客户端连接,获得初始状态,然后基本上各自独立自主。
实际上,有一种很好的折衷方法——许多支持多人玩的游戏采用这种方法,那就意味着复制客户端和服务器中的重要代码。幸好,由于我们处在无所不在的JavaScript时代,再也不需要复制功能,而是只要用JavaScript编写游戏引擎就可以共享代码,然后在客户端上的浏览器中和服务器上的node.js中运行即可。
共享的JS模块
如前所述,osmus使用在客户端与服务器之间共享的物理引擎。因而有人可能会想:在两者之间共享JavaScript代码会易如反掌,实际上不是那么容易。
模块加载器有一大堆。有CommonJS规范、RequireJS库和node.js require方法,没有一个可以很好地协同使用。如果你不用模块加载器,就想在客户端和服务器之间共享代码(这是服务器上JS的一大优点),那么你可以使用这个有点变通的模式:
- (function(exports) {
- var MyClass = function() { /* ... */ };
- var myObject = {};
- exports.MyClass = MyClass;
- exports.myObject = MyObject;
- })(typeof global === "undefined" ? window : exports);
这个变通方法靠的是这一点:node.js定义了global(全局)对象,而浏览器没有定义。有了这个变通方法,node.js require()会很高兴,你还可以在<script>标签中加入文件,不会污染你的名称空间,当然假设没有其他JS以window.global对象污染你的名称空间!
遗憾的是,这个方法只适用于一个共享模块。一旦你有了多个彼此依赖的模块(通过node-land中的require方法和browser-land中的global对象),节点的名称空间与浏览器的加入之间的差异会变得异常明显,需要更多的变通方法。
广州网站建设,网站建设,广州网页设计,广州网站设计
另一个方法是使用browserify,捆绑所有JS,在浏览器里面模拟require。这种方法依赖node.js来提供生成的JS,这并不理想,因为静态文件应该由专门为该用途优化的web服务器来提供。不过,node.js+ browserify可以进行配置,以便编译可以静态提供的JS,不必依赖节点来提供。这个方法带来了一些开销:
1. 多出了构建这个步骤,以便部署。
2. 无论browserify使用什么机制来支持require()调用,都需要性能开销。
总的来说,这个方法在我看来比较好,我希望在将来编写的osmus版本中试用一下。
原文:Developing Multiplayer HTML5 Games with Node.js



