在之前的文章中我们提到,在云端平台免费积分的支援下,我们建构了一个可容纳3万用户的单一虚拟世界。关于伺服器部分的详细介绍,可以参考我之前的贴文。本文将重点放在分享我们在过程中遇到的问题以及如何解决这些问题。
整体上这次实验并不成功。不过,为了让有兴趣的开发者能够体验到实施这些解决方案后的成果,我们将保持虚拟世界https://demo.mb-funs.com/运行至 28 日。
下面,我将分享我们所面临的问题,以及针对这些问题的未来思考。由于我们团队最初专注于 2D 游戏,因此对 3D 开发非常陌生,这导致了几个基本的错误。
在本次实验中我们遇到了以下主要问题:
地图设计不佳,导致运行一段时间后,单次可视范围内就会出现超过 5,000 个角色。
快速建立和释放对象,但垃圾收集(GC)无法处理。
同一萤幕上的物体过多,导致CPU无法处理所有的骨骼动画计算。
Unity Emscripten 对键盘输入的处理,阻止了 WebSocket 事件的触发。
问题一:地图设计不佳
当我们最初规划地图时,我们的目标是在简单的环境中创建显著的地形变化,让使用者感受到 3D 空间的感觉。然而,我们忽略了一个事实,我们只为机器人设计了简单的逻辑。这使得机器人随着时间的推移开始聚集在地形的峡谷区域。
此外,我们的机器人使用了真实连接的独立模拟,这意味着它们无法相互协调或避开。我们的伺服器和客户端采用了9格同步可见性范围。在这个版本中,我们测量了单个可见范围内存在的超过 5,000 个字符,这远远超出了 Web 平台的显示能力。
首先,我们希望维持现状并取得最佳效果,即聚类仍可进行,但显示器仍可正常运作。我们开始实现LOD(细节层次)、多边形减少、蒙皮优化、基于效能的动态显示距离、动画调整等,然而我们忽略了WebGL相较于其他平台的优化能力有限。
最终,我们透过移除狭窄的峡谷来修改地形,并调整机器人的移动逻辑以减少聚集的机会。修改后的版本,在后续的测试中,单一可视区域的字元数量一般被控制在3000个以下。
未来计划:
我们预计未来将引入 GPU Skinning 来减少 CPU 开销。这是因为,随着人工智慧的发展,我们观察到较新的行动处理器中的 GPU 效能显著提升。此外,我们计划进一步增强动态调整,根据玩家关系和场景内玩家的权重结合伺服器和客户端的决策。这将有助于确定是否应该显示其他玩家。
这样,大多数玩家都能够享受游戏乐趣,且游戏体验不会受到影响,解决了传统伺服器技术中好友分伺服器的问题,打造自然流畅的社交互动体验。
问题 2:快速建立和释放对象,记忆体过载
演示本身相当无聊,因为它只是为了让用户在高负载条件下与同事或朋友互动。然而,当测试人员进入场景后,大多数人会迅速走向人群,导致角色模型和体素的快速创建和发布。由于垃圾收集(GC)不及时,导致记忆体快速积累,最终超出装置的负载并迫使浏览器关闭页面。
最初的设计是为了避免触发 Safari 对 iPhone 的严格记忆体限制,但最终我们不得不放弃对一些较旧的 iPhone 型号的支援。为了解决这个问题,我们实施了快取回收。进入场景后,我们预先载入了 1,500 个角色、超过 7,000 个体素区块以及其他各种常用资源,这导致基本记忆体使用量高达 1.6GB。这意味着大多数早期的 iPhone 型号不再受支援。
未来计划:
我们想尝试将目前的 Unity GameObject 系统转换为实体元件系统 (ECS),结合 GPU Skinning,看看是否可以解决每个角色必须包含模型资料的问题。但我们对这个地区不太熟悉。虽然几年前当 GPU Skinning 首次出现时我就编写了着色器进行测试和验证,但时间已经过去很久了,所以我们可能需要花费大量时间来研究和试验它。
问题 3:同一萤幕上的物件过多
由于我们这边的机器资源有限,在部署到云端之前我们只测试了2000个机器人。这导致我们严重低估了处理在 Web 平台上移动的大量角色模型的效能需求。结果,一开始的操作非常卡顿,甚至连摄影机都无法流畅移动。
最终,我们透过启用 Unity 的 Web 多执行绪功能解决了这个问题。然而,一旦启用,就会出现一系列编译失败。出现这些问题是因为我们修改了我们的 2D 游戏专案来建立此演示,其中包括一些使用旧的 dynCall 方法建立的 jlib 相关函数。此外,我们收集的资讯表明,官方 Unity 文件不建议在此上下文中使用运行 C# 多执行绪的功能。我们不得不花费大量时间来解决和排除每个问题。
未来计划:
我们相信这个问题很可能会随着问题 1 的解决方案一起解决,因为这两个问题都与最佳化效能和资源管理有关。
问题 4:Unity Emscripten 键盘输入影响 WebSocket
启用多执行绪后,我们注意到在 PC 装置上运行时出现了明显的卡顿。这种卡顿不是由视觉效果或角色动画问题引起的,而似乎是网路资料包延迟(角色仍在移动,但似乎没有收到新命令,导致重复的行为预测)。
一开始我们怀疑是伺服器的问题,但是在行动装置上并没有出现同样的问题,而且检查了伺服器状态后也没有发现异常。经过多次测试我们发现,每当按下键盘按键时,即使没有触发任何事件,透过JS建立的WebSocket也不会触发onmessage事件。此问题仅发生在字元密度较高的区域。
我们怀疑 Unity 内部的一些键盘相关操作在主执行绪负载过重的情况下占用了 CPU 资源。为了解决这个问题,我们尝试强制 Unity 的执行阶段逻辑释放 CPU 资源。果然,一旦我们做出这样的调整,口吃就停止了。
解决方案:
var requestFrame = window.requestAnimationFrame;
window.requestAnimationFrame = 函数(回呼){
setTimeout(()=> requestFrame(回呼),1);
};
此解决方案强制在 requestAnimationFrame 操作中产生间隙,从而解决了该问题。希望这篇文章能够在 Unity 提供修复之前帮助遇到相同情况的任何人。
上述是主要遇到的问题。希望这些可以为其他人从我们的失败中学到一些教训。接下来,我们将利用这次演示的经验来开发一款多人互动休闲社交游戏。在这个游戏中,玩家可以聚集在共享空间中,建造房屋,进行简单的冒险等等。如果有人有更好的想法欢迎分享。