Rust 元宇宙 1 —— 创世纪
Why Rust ?
为什么选择 Rust 语言,这是一个复杂的设计决策,从无数考虑的理由中,我选择下面三个,作为我自己认为最重要,最有说服力的理由。
安全
我的第一个 Rust 程序是 360 游戏开放平台的一个广告匹配系统,可以认为是一个简化版的竞价排名,这是我一次写 rust 程序,而且那个时候,rust 的编译器远没有今天那么智能(成功的去掉了大多数生命周期的定义),成功编译花了整整一个月的时间,神奇的是,一次性上线之后,在数个月的正式环境运行过程中,这个简单的 daemon 甚至坚持到最终机房搬迁,一次 crash 也没有。
强大的表达能力
利用 trait 和 现代函数设计语言的 N 多模式,充分利用 fold,iter,map,collect 等等函数表达式,我们能够将注意力的焦点从数据结构的操作切换到数据结构本身,比如说 map 使得我们可以对结构整体进行变换和处理,而不用去管内部的遍历器和算子是如何起作用的。我们的实践表明,Rust 能够比 C/C++节省 2/3 的代码量。
性能
这对硬件厂商不是一个好消息,我们的实践再次表明,相对于 Java ,Rust 完成同样任务所需要的硬件配置要比 Java 低一个数量级。
而且没有 GC,程序的运行行为也是可以预期的,不会有超出想象之外的 Stop the World。
我们采用 自底往上 的建构方法,
但是在进行任何设计决策的时候,我们必须考虑元宇宙本身的普适性,也就是说,我们的实现可以非常细节和具体,任何一步思考的环节都可以看到可运行可检验的成果,但是我们所做的任何决策,都必须,至少自很长一段时间内必须,可以成为最终元宇宙的一部分。
元宇宙 是 虚拟的世界,这么一个庞大的名词,我们必须从一个最简单基础开始构建。
所以简化到极致是什么?
最简单的元宇宙场景是什么?
整个世界由多个场所组成
现实世界的人类,生活在一个无限的宇宙,对于个人来说,就是近乎无限的地球。
但是任何绝大多数的行为,都发生在某一个具体的场所(考虑到飞速移动的汽车/火车/轮船/飞机,上面的人类也是处在一个有限的场所)。
所以,我们的元宇宙简化为,无数个有限大小的场所,通过可快速通过的道路相连接。
这里面隐含了两种可能性
一个内容提供商构建了足够多的场所,并通过道路将它们连接起来
多个内容提供商共同构建了无限的场所,并通过一套开放的规范将他们连接起来
真正的元宇宙应当然是后面这种可能性,但是作为从底往上的构建,我们先作为第一个建筑师,开始构建我们的 元宇宙
第一步,是构造一个有限大小的场所
在端游的早期,大家为了追求所谓的无限大的世界,设计了无数的技术方案来实现所谓的无缝地图切换,其中尤以坑人的 BigWorld 为甚,这方面网易的同学有惨痛的经验,Bigworld 在所谓的无缝大世界中花费了太多的精力,以至于最后没有开发出一款真正流行的游戏(我印象中,网易的《天下》项目组就使用了 Bigworld,毫无悬念的销声匿迹了)。
二维还是三维,这就是问题?
也许大家会疑惑,为什么会提出这个问题呢,我们难道不是生活在一个三维的世界吗?但是仔细想想,我们的世界是三维的,我们日常生活是三维的吗?
我们都生活在地球的表面,我们必须站在地面上/甲板上/飞机的地板上,当我们告诉别人某一个位置的时候,我们使用的是二维的坐标,经度加上纬度,而没有高度。
所以实际上,在绝大多数情况下,我们是二维的,当然具体到某一个大楼,某一个商场的时候,简单的经纬度不够了,我们需要加上楼层。
这也就是从魔兽世界开始绝大多数 MMORPG,不约而同的选择使用二维坐标来描述玩家位置的核心原因。
在野外,玩家是二维的,在复杂的副本或者楼房里面,我们要加上楼层甚至高度。
作为一个合理的简化,我们认为场所是二维的。
想想我们的现实世界吧,当我们进入某一个场所的时候,我们是不是会看到很多自然风景/建筑物,但是这些东西在一定的时间是不会变化的。那么变化的是什么呢?
当然是来来往往的人们(或者包括他们的宠物)
所以我们的元宇宙的第一步,就是模拟一个场所,场所里面有来来往往的人群(玩家)
所以我们定义最基础的结构 Position
表示玩家的位置,我们使用 f32 作为 位置,在一个有限大小的场所,f32 足够了
我们需要一个方法来计算两个 位置之间的距离,根据简单的勾股定理,我们知道
但是我们知道开平方根是非常耗时的操作,大多数情况下,我们只需要比较距离的大小,不需要计算出距离,所以,我们定义下面的函数,能够获取距离的平方就足够了。
对于一个玩家来说,除了自己的位置以外,最重要的是什么呢?
除了长期不变的自然/人工风景以外,最重要的,当然是来来往往的人,我们知道,人的视野是有限度的,只有在视野范围内的人/宠物 才会在当前场所跟 自己发生联系,所以我们需要知道一个玩家的邻近玩家列表。
理论上来说,只要遍历整个场所的所有玩家,我们就能知道 跟 自己距离在某一个可视距离之内的玩家,如果所有玩家静止不动还好,虽然耗时,但是遍历 N x N 次也就足够了,问题是,玩家是不断移动的,即使我们不追求实时更新这个邻近玩家,比如说每隔五秒一次 N x N 的计算距离操作,对服务器也是一个巨大的开销。
更可怕的,当玩家做了某个动作,比如说,原地翻了个筋斗,这个动作需要让他周围的玩家都看到,这个时候,我们需要知道该告诉谁,需要计算邻近玩家的列表,每个动作都要计算一次,这显然是不可能的。
所以,我们必须保存玩家的邻近玩家列表
考虑到每个玩家都需要保存 这个列表,我们需要尽可能的减少内存占用,所以我们使用 32 位整数 u32 来唯一标识一个玩家,所以,我们一个玩家(考虑到宠物,我们用角色这个中性词),角色信息定义如下:
版权声明: 本文为 InfoQ 作者【Miracle】的原创文章。
原文链接:【http://xie.infoq.cn/article/78f77d7fcd2f877747348c424】。未经作者许可,禁止转载。
评论