我正在build一个金融模拟器作为副业项目 - 它变得足够大了,以至于我遇到了一些有趣的架构壁垒,并希望分享我所学到的知识,以便在任何其他人建造复杂度的浏览器游戏方面提供帮助。
设定: 使用TypeScript(∼95K行)的全客户端游戏引擎,Rust/WASM用于重型数学, Zustand用于状态,IndexedDB用于持久性。没有服务器,只有Supabase的排行榜。游戏模拟一个股票市场,108家公司,宏观经济,期权定价,总裁模式和人工智敌。
有效的方面:
- 纯的引擎层,无framework依赖。所有游戏逻辑都生活在纯函数中,它们接收数据并返回数据。引擎中没有React的导入。这使得测试变得微不足少,2000多个单元测试在不到3分钟内就执行完毕,因为它们没有触摸DOM。
- 在tick管道中,复制-写数组。市场小时,每隔50ms执行tick pipeline。 相反于深度复制状态,我在首次写入时将数组标记为“脏”。减少了GC压力的一半。
- WASM用于正确的事情。Black-Scholes,GARCH波动性和批量价格计算是在Rust中进行的。 Everything else都留在TS中。 关键是先进行性能测试 - 大部分tick时间都在价位模型中,而不是在React中。
我做错了:
- 上帝商店。 主的 Zustand商店在 2000 行之前就分离了。 应该从一开始就分解的。 最终我将类型和初始状态解析为单独的文件,但动作定义仍然是单一的。
- 双数据结构。 我在公司的Map < id, Company > 和 Company[] 中保持了两个月 - Map用于 O (1)查找,array用于迭代。 每次tick都重建了Map。 最终删除了Map并仅使用了.find() 。
我的疑问: 有其他人在构建复杂的模拟型浏览器游戏吗? 我很想知道如何在100多个实体更新每个框架时处理状态管理。 Zustand的选择器工作得很好,但选择器的数量变得不可捉摸(在我这里有400多个,跨60个文件)。
评论 (0)