如何在不从头开始重写的情况下升级一款已有15年历史的实时游戏?——Playkot专栏
如何将运行中的Flash游戏转换为HTML5,而不从头重写客户端也不破坏玩家的现有体验?在App2Top的专栏中,《SuperCity》的开发负责人Kiril Ivanov讲述了项目的逐步现代化、保留旧工具的原因,以及自动化流程如何将某些任务从几天缩短至几小时。
从理论上讲,升级带有过时代码的应用看起来很简单:识别问题技术、用现代技术栈替换它,去除旧的依赖,继续开发。但实践中更为复杂。在《SuperCity》的现代化过程中,我们就遇到了这一挑战——这款游戏已经运行了15年。
《SuperCity》于2011年推出。如今,其客户端构建在TypeScript和PixiJS上,后端仍然在使用PHP。同时,系统的某些部分仍保留了Flash时代的解决方案。艺术家们依然使用Adobe Animate进行创作,内容必须与现有的生产流程兼容,还有部分浏览器基础设施需要支持最初为Flash Player设计的交互界面。
因此,对我们来说,现代化从来不意味着替换每个旧组件。我们的任务是:在不再得到行业支持的情况下,逐步放弃技术,保留玩家熟悉的游戏。
首次迁移解决了玩家的问题,但没有解决开发的问题
我们首个重要步骤是在浏览器开始停止支持Flash时进行的。尽管《SuperCity》必须在没有Flash Player的情况下继续运行,但我们没有立即重写整个客户端。
相反,我们使用了一个转换层,将现有的ActionScript代码转换为JavaScript,以便游戏能够工作。同时,我们调整PixiJS以再现游戏期望Flash Player的行为。对用户来说,问题得到了解决:《SuperCity》可以在没有旧插件的浏览器中继续运行。
但在开发过程中,Flash并未消失。我们依然用ActionScript编写代码并在构建时进行转换。每当有变动时,还须考虑额外的层次,这增加了调试和维护的难度。
随着时间的推移,另一个问题愈加明显:Flash成为一种孤立的技术。越来越少的工程师愿意使用它,几乎没有出现新的工具,生态系统不再发展,我们可获得的外部专业知识也在减少。
在某个时刻,我们意识到没有现实的替代迁移方案。没有围绕《SuperCity》技术的发展,支持游戏本身将变得愈发困难。然而,这并不意味着完全重写是正确的解决方案。
成功的在线游戏不仅仅是代码
对于成熟的游戏来说,重写客户端会造成其他软件产品所没有的风险。玩家并不是为了完成某项任务而与界面短暂交互。他们可以年复一年地回到同一款产品。他们知道每个地方的位置,理解系统的工作方式。他们几乎是潜意识地学习了视觉模式和交互规则。这些累积的知识已成为产品本身的一部分。全新的客户端可以复刻相同的机制和数据,但可能让人觉得是另一款游戏。
我认为,这种差异对于游戏尤为重要。
在实用程序中,界面变更可能带来不便,但用户通常能掌握达到相同结果的新方法。在游戏中,界面、反馈、节奏和积累的熟悉感本身就是体验的一部分。若同时替换这些,可能会迫使老玩家重新学习他们从未要求改变的东西。也存在更传统的工程问题。经过多年,《SuperCity》累积了大量的集成和平台依赖。重新构建它们全部、重新验证和对整个产品进行完整的回归测试将需要大量投资。实际上,团队将拿起功能正常的游戏,重建其大量部分,然后再支付费用证明新版本与旧版本一样能运行。同时,没有任何保证能创造出等价的商业价值。
因此,我们选择了渐进式变更:衡量每个步骤的效果,不在没有明确理由的情况下触碰正常运行的系统部分。
保持接口不变,只更改其背后的内容
我们的一项关键架构决策是保留游戏所依赖的交互接口。这使得我们不需立即重写所有相关系统。
Flash Player实际上扮演了契约的角色:通过已知调用,游戏能够要求它绘制对象、播放或更改声音,或执行其他操作。放弃Flash后,我们重建了这个契约。其下的实现发生了变化,但调用代码无需了解新环境的细节。
如果游戏请求展示动画,代码的这部分无需关心最终使用哪种技术绘制它——Flash Player或Pixi。
新层能接受熟悉的命令并提供预期的行为。这样,我们替换了技术栈中至关重要的部分,而无需同时重构所有关联系统。
这种方法有其代价:兼容层保留了技术债务,随着时间需要额外适应。但重写所有相关系统的代价会更高,且风险更大。
兼容层可能与我们为新游戏选择的架构不同。然而,在升级现行产品时,必须考虑一切已在运作的东西。
迁移到TypeScript需要整个生态系统的转移
我们的下一个大步骤是放弃直接在开发中使用ActionScript,转移到TypeScript。此过程在首次浏览器迁移数年后完成。
原因之一是优先级。在工程师们更新技术基础的同时,在线游戏继续发布内容。事件要推出,服务器要应对负荷变化。外部平台引入硬性要求,新产品功能出现又消失。在不同阶段,这些任务比内部迁移更为紧急。
另一个原因是依赖的规模。在开发多年间,我们围绕《SuperCity》打造了用于资产转换、构建流程、内部应用和各种专家工具的实用程序。对Flash的依赖不限于客户端。
如果我们仅将客户端迁移到TypeScript而不更改其他内容,瓶颈只会转移到生产过程的另一部分。
迁移不仅涉及到玩家在浏览器中启动的内容。我们还必须重构游戏周围的许多内部流程。
这部分不那么显眼的变化本身已成为重大工程任务。
旧的不一定是不对
艺术管道显示了为什么我们不认为现代化是删除所有与Flash相关事物的理由。经过多年的《SuperCity》发展,累积了大约80GB的图像资产:建筑、装饰、角色和其他内容。艺术家们依然使用Adobe Animate。
从技术角度看,这个工具可能显得是明显的替换对象。但从生产角度考虑,计算方式不同:新工具必须至少提供同样的质量和速度。
如果资产质量下降,产品受损;若质量保持,但每次发布需更多时间,生产受损。我们研究了替代方案,但尚未找到能证明重构整个流程的替代品。
对我们来说,技术的年龄本身不是替换的充足理由。一旦某个组件开始造成显著约束,就成为现代化的优先事项。
有时更好地优化周边流程,而不是工具本身
保持Adobe Animate不意味着图像工作未变化。过去,艺术家常难以准确预见资产在游戏中的表现,直到开发人员准备好工作构建。这拉长了反馈周期。
艺术作品常在新功能发布前很久便创建。如果转换问题、渲染工件或其他视觉差异仅在准备测试版时被发现,修正往往为时已晚。
我们创建了浏览器预览工具——游戏的简化版。艺术家可上传资产并立即查看其在游戏环境中的表现。颜色更改、像素失真及其他转换问题立即显现。
这改变了责任分配。曾经,开发者负责收集资产、准备构建并协助解决发现的问题。现在,资产作者可自行检查结果,并将其准备到下一个阶段。
质量控制仍在,工程师无需手动联系生产的每个阶段。遗留下来的工具依然存在,但周围的流程已改变。
更大的目标是让开发者不再从事不需要开发者参与的工作
随着时间的推移,该原则逐渐超越图像工作。在过去几年里,我们重组了大量内部工具,形成了一个我称之为游戏的内部ERP系统。
历史上,工程师几乎参与每个发布准备阶段。翻译、艺术、游戏及促销配置等材料最终在开发者处汇集,才能构建出工作版本。工程团队成为瓶颈。
新工具划分了责任区域。负责游戏配置的专家可以自行发布配置,系统知道如何构建最终结果。游戏设计师可以更改平衡或准备新功能原型,调试修改并测试,无需开发者手动将每个工件通过流水线。
过去需要数天、协作会议和工程师参与的流程,现在只需几个小时。在某些情况下,互动简化为在内部工具中的几个操作。
主要结果不是所有人都成为技术专家。恰恰相反。游戏设计师不再需要理解Git的分支来测试平衡更改。叙事专家不再需要理解内部流水线结构来更新内容。艺术家不再需要开发人员来查看资产在游戏中的表现。
Canvas对自动化测试的需求不同
切换到HTML5并没有自始带来浏览器自动化测试的问题。Flash以前也缺乏对标准工具的兼容性。但迁移后,我们失去了对Flash的调试能力,而HTML5 canvas带来了自己的限制。
对于浏览器自动化来说,canvas可能被看作一个黑色矩形。它没有便捷的DOM元素对应每个建筑或界面组件。
因此,视觉检查需要其他方法。我们有用于研究渲染层的内部调试工具。部分检查可以通过图像和截图比较来执行。然而,许多情形比普通网页接口更难自动化。
我们已经自动化了基本的端到端情形:可以展开构建、启动游戏、将游戏置于所需状态并验证如建筑放置或资源收集。
更复杂的问题在于live-游戏的状态。两位用户可能因平台、进度、群体和时间而看到不同的窗口、事件、优惠和界面。今天通过的测试可能因不同的事件或LiveOps优惠而在明天遇到问题。
自动化必须不仅仅考虑点击的位置,还要考虑为什么玩家状态会与预期不同。对于某些场景,今天全面解决此问题的成本会高于手工测试。
我们根据是否消除实际限制来评估自动化。对于我们而言,自动化过程本身的增长份额并不是目标。
有时需要在工作环境中进行测试
在长期存在的游戏中,外部平台的限制可能使我们无法在发布前完全复现所需条件。在最近一次平台的强制迁移中,我们遇到了这种情况:特定行为只能在工作环境中进行正确检验。
这种方法是由平台条件驱动的。我们逐步对小部分用户启用变更,观察系统并随着信心的提高扩大范围。
这个例子表明,逐步现代化有助于管理风险。大型迁移中不可避免会有未知因素。逐步引入变更、测量效果、调整或回退的能力可避免同时将整个用户群置于风险之中。对于live-游戏,部署更新的机制本身也成为现代化的一部分。
主要优势不在于TypeScript本身
放弃Flash改变了开发语言,但我看到的主要提升之一是扩大了能够参与《SuperCity》的专业人士范围。
找到既了解Flash、愿意继续使用它、对游戏行业感兴趣、且愿意从事成熟产品工作的工程师越来越难。TypeScript在很大程度上减轻了这一限制。
同样重要的结果是减少了日常生产中的工程师参与。开发者不再需要手动支持内容准备的每个阶段。艺术家可以自行检查更多资产,而游戏设计师可以创建原型并调整游戏系统。
内部工具聚合了此前需要一整条人力和技术操作链的材料。某些流程由数天缩减到几小时。对于我们而言,这是一项更具意义的现代化指标,而非代码库中完全没有过时组件。
我们的目标是使游戏更易于持续发展。
遗留系统的现代化永无止尽
《SuperCity》中仍然存在旧代码、我们希望改进的系统以及可能随着时间而被替换的技术。同时,我们甚至重新审视了相对新建的游戏部分。
我们的方法关注于衡量指标而非技术年龄。如果某些内容加载缓慢,我们会研究问题。解决最主要的瓶颈后,下一个问题显得更明显——工作继续进行。
结果影响产品的两个方面。玩家获得更快的加载、更少的故障和更快的响应。基础设施在此过程中可能需要更少的资源来确保相同的工作质量。
我们无法宣布架构为现代化然后就不再考虑它。对于长期存在的产品来说,现代化是一项持续进行的工作,以维护发展的可能性。
从理解什么绝对不能破坏这件事开始
如果我咨询另一支已有10到15年历史的成功产品团队,我会从定义边界开始,而不是选择新的引擎。团队想要实现什么目标?考虑到人员、技术和资源,它们能够改变什么?在过程中不能破坏什么?
对于成熟的在线游戏,最后一个问题涉及到基础设施和用户群。技术上优雅的迁移可能因破坏玩家已经习惯的体验而导致失败。
我还对为模仿而更换技术持怀疑态度。其他工作室向Unity、Cocos或其他流行技术栈的迁移,本身并不反映《SuperCity》的需求。每个成熟产品都有自己的限制。
我的建议是:首先明白你想要什么、可以做什么以及无法容忍破坏的是什么。其他的一切都需要经过明智的判断来处理。
我们逐步消除现有游戏的限制。Flash消失于客户端的日常开发中。兼容层在实现改变时保护了正常运行的系统。内部工具使专业人士可以独立完成先前依赖于工程师的任务。
在自动化带来切实效果的地方,我们进行了自动化;在手动测试仍有经济意义的地方保留了手动测试。此方法使得在不迫使用户或团队重头再来的情况下,能在运行中的游戏下更换技术。





