TLDR:我们已经建立了一个多人射击游戏,并使用了Apollo的GraphQL订阅来使游戏状态以很高的频率保持同步。 这是我们的印象!
使用的工具: Angular-铯,Apollo,CesiumJS,Angular
开始游戏: geo-strike.com
好吧,简单地说,因为我们可以!
我们没有努力创建一个特殊的可序列化,高效但又不可读,严格且深奥的二进制协议,而是决定使用定义良好,可读且灵活的基于GraphQL的API,并在需要时对其进行改进。
事实证明,我们可以轻松管理所有客户端中玩家位置,状态和动作的同步!
您可以通过两种方式了解我们的挑战:
- 畅玩游戏-随时打开开发工具上的“网络”标签,并查看
subscriptions
websocket框架。 - 阅读我们的游戏的全部内容。
对于你们中那些还没有玩过游戏的人(至今!),我会解释一下它的含义。
我们的游戏是在时代广场附近进行的多人射击游戏。 游戏模式是一场“团队死亡竞赛”战斗,您在其中与您的团队一起在地图上生成,您需要找到并射击其他团队的玩家。 获胜的球队是一个幸存下来的球队,而另一队的球员已经死亡。 如果死亡,则只能从鸟瞰图观看游戏的其余部分。
我们使用CesiumJS(一种类似Google Earth的开源,地图引擎)作为场景,因此,当我说游戏发生在时代广场时,它是时代广场的真实表示,建筑物使用Cesium的流媒体传输到客户端3d瓷砖。 事实上,没有什么可以让您仅停留在时代广场,您可以想知道到底走到了哪里。 请记住,您将只能看到曼哈顿的建筑物。
因为游戏是多人射击游戏,所以我们需要以用户不会感到“滞后”的速度来同步多人的位置,方向和状态。
为了做到这一点,我们决定将游戏服务器作为唯一的事实来源。 它将每个游戏保存在内存中的简单映射中,因此在需要时应尽快解析此数据。 接下来,我们编写了一个小的GraphQL模式,该模式定义了我们的Game实体。
最终看起来像这样:
此图的某些部分将非常快速地更改(每秒约10次),而其中的某些部分将很少更改。 为了实现这一目标,我们决定使用不经常更改的数据的查询类型和不经常更改的订阅类型来公开此图。
这使客户可以灵活地决定需要将数据图的哪些部分蒸去。
在我们的游戏中,这个订阅查询原来是我们需要经常更新的唯一部分:
刚开始时,我们采用了朴素的实现并进行了设置,以便我们可以随时随地获得任何客户端所做的任何更改。 基本上在突变解析器上发布。 但是我们很快意识到这不是一个很好的方法,因为它不是很可扩展,并且我们很快对graphql服务器施加了过多压力。
我们决定采用的方法通常称为“游戏滴答”。 我们设置了一个计时器,该计时器每200ms触发一次,并将游戏状态发布到gameData订阅中。 这是我们在状态同步过程中为实现低延迟而进行的最重要的更改之一,这得益于GraphQL订阅的工作方式,使用了几行代码。
我们不仅可以减少延迟,而且还可以轻松扩展播放器的数量。
我们剩下的只是处理客户端上的那些高频事件,并使它与其他数据操作正常运行(例如,不发生冲突或滞后)。
但是对于这些挑战,阿波罗和有angular-apollo
让我们得以掩盖! Apollo客户端处理了状态订阅,并无缝更新了标准化的客户端存储。
angular-apollo
将状态显示为Rx Observable,使我们可以将流插入到我们令人惊叹的angular-铯框架中,并让玩家神奇地移动和更新使用CesiumJS渲染的3d场景!
angular-
angular-cesium
是由The Guild的Tomer Moshe和Eitan Frailich创建的框架。 该框架允许使用简单的角度分量来描述复杂的地图视图(3D或2D地图和几何形状)。 它可以订阅数据流,并将地图的状态绑定到数据流-将流上传递的所有更改绘制到地图上。 它以极高的效率和非常小的占地面积来完成所有这些工作。 它使用了超棒的CesiumJS库作为地图引擎,并优化了CesiumJS的绘制过程,以获得最佳性能。 该框架还添加了一系列广泛的工具,例如事件管理工具和几何图形绘制工具。 我们在开发中充分利用了这些优势。 查看文档或Github了解更多信息!或者,访问The Guild的站点以获取更多项目。
如上所述,我们每200毫秒发布一次游戏状态,这对于定位玩家来说是相当不错的,但是我们游戏中的某些事件无法忍受这种延迟,并且仍然让用户感觉良好。
在分析了图表之后,我们确定存在两个此类事件。 射击(显然)和游戏状态通知。 为了尽快处理这些订阅,我们仅添加了客户端应订阅的两个不同的订阅。 这样可以确保一旦有人射击,我们将根据他在所有其他客户端上的相对位置播放射击声音,并且一旦某个玩家被杀死,所有其他玩家都会收到通知,指出这一点。
由于我们的游戏是在与我们自己的地球相同的规模上进行的,因此我们不禁会想到未来的游戏模式,整个部队可以彼此对抗,以接管/拯救世界!
这种大规模的多人在线游戏需要进行一些更高级别的优化。
例如:
- 通过根据玩家当前在世界上的位置过滤玩家状态,优化发送给每个客户端的数据量。
- 通过将GraphQL响应转换为Google的Binary ProtoBuff来优化有效负载大小。
- 使用Apollo Engine的动态持久查询来优化应发送到我们的游戏服务器的数据。
该游戏是Webiks(软件开发公司)之间合作的产物,Webiks是一家专注于高端数据分析和实时态势感知基于Web的应用程序的公司。 还有The Guild,一群自由开发人员,开源贡献者和angular-铯的创建者。
如果您对我们在该项目中面临的其他非图形挑战感兴趣,Webiks的首席执行官Omer撰写了一篇博客文章,展示了所有这些内容。
哦,这都是开源的! 过来,加注星标并作出贡献!