特色博客 或者也可以是:精选博客 / 专题博客 (具体哪种表述更合适,可根据语境确定)

我们即将讨论的案例正是备受赞誉的佳作《名为EOS的恒星》。在这篇关于主机移植的文章中,我们将详细探讨在移植过程中为转换存档系统所找到的解决方案。事不宜迟,让我们直接进入主题。

在《名为EOS的恒星》中,系统操作是直接且同步执行的,毫无异步交互的迹象。这是一项极具挑战性却又令人兴奋的任务。例如,PlayStation 5通过内存挂载来处理存档,而这并非即时完成。关键在于,存档只有在卸载内存区域后才算完成。实际上,对于PlayStation而言,可以使用PlayerPrefs,其工作方式与内存挂载类似,但这一切都在幕后进行,我们无法控制。然而,这种方法有一个重大缺陷——由于该功能的主要目的是保存游戏设置,可用内存量有限。因此,这个限制是可以预料的。不过,由于存档使用了截图,这个限制就显得不够用了,所以第一种存档选项仍然是主要的。

那么Xbox呢?Xbox目前主要使用GDK API,在项目启动时使用它时,总是会与云数据进行同步。这已经影响到了项目的另一个元素——初始化。但这不是我们在这里要讨论的内容。在Xbox上处理存档的主要思路是,每次读写时,都需要打开一个容器,执行必要的操作,将更改通知给GDK API(如果有更改的话),然后关闭容器。

Switch呢?它与PlayStation的情况几乎相同:挂载和卸载都需要时间。

我们正在处理的原始游戏中数据是如何保存的呢?存档的创建方式如下:保存数据,拍摄屏幕截图,并将其记录在存档中。每次删除存档或创建新存档时,都会重新读取数据。

我们为这个项目创建了一个统一的存档系统,作为任何平台的单一入口点。每个平台都有自己的SDK和处理存档的方法,因此创建一个统一的系统对于确保一致性至关重要。结果,我们编写了一个单一入口点脚本,它使用适配器模式工作,并为每个平台控制实体,确定使用的是哪个平台并运行相应的脚本。

现在,我们对主要挑战有了一个大致完整的了解。它们是什么呢?

项目代码必须能够在不破坏核心执行逻辑的情况下释放主线程。

我们需要尽量减少对存档系统的调用次数,因为即使是异步调用也可能导致卡顿。

由于我们有异步调用,我们必须确保主要条件——一次只能对存档系统进行一次调用。

一个简单且相当有效的解决方案是使用Task.Async。为什么呢?因为它允许我们暂停原始逻辑,并在需要时恢复它。就项目性能而言,这是最好的解决方案吗?不是。它会提供最快且最理想的结果吗?是的。

当然,这种方法在编译后会生成大量额外的代码,但它确实给了我们预期的结果。现在,我们需要删除所有对文件系统的直接调用,并将它们替换为对我们在Task.Async上开发的“新存档系统实现”的新调用。

之后,我们将所有调用存档系统方法的方法改为异步,这样它们就可以暂停执行,直到所有存档或加载操作完成。如有必要,我们还会部分修改调用层次结构中的更高级别方法。这样,第一个也是最关键的问题就得到了解决。

图片[1]-特色博客 或者也可以是:精选博客 / 专题博客 (具体哪种表述更合适,可根据语境确定)- 小树基地-小树基地

《名为EOS的恒星》的截图。图片来自Pingle Studio。

接下来呢?

在这个阶段,我们遇到了Unity的“Player Prefs”的问题。为了解决这个问题,我们创建了一个自定义的类似物,它的工作方式类似,但将数据保存到文件中,这样就可以在任何平台上使用,包括Switch。这个解决方案是必要的,因为任天堂Switch不支持Unity的“Player Prefs”,只能使用原生的任天堂SDK进行保存。

然后,我们必须尽量减少对存档系统的调用。原始项目是这样实现的:我们有一个类似PlayerPrefs的东西,它被写入一个文件——一个包含存档名称的字典,用于访问截图和存档数据。每次读取、写入或删除文件时,都会从头开始读取。在个人电脑上,特别是使用固态硬盘时,这不是问题,所以可以忽略优化,但在有十几个存档的主机上,这可能会导致严重问题。

这个问题有几个解决方案:

捆绑大量数据,并通过一批操作来访问存档(这需要重新编写原始逻辑,可能会花费很多时间)。

为已加载的存档创建一个缓存,并在重复访问时使用缓存数据。

第二种选择在实现上要方便得多,所以我们将其作为主要方案。当一个对象被重写几十次或同时进行读写操作时,它不会创建额外的交互场景。这些情况可能不是很明显,但从一开始就避免它们更好,这样它们以后就不会成为一个重大挑战。

所以,我们还剩下最后一个挑战——多个同时进行的写入、读取和删除操作。使用异步调用时,这很容易管理。每次我们要处理一个文件时,可以使用一个指示器,比如信号量或一个简单的变量,来表示队列仍然繁忙。由于我们为Task.Async重写了原始逻辑,我们的代码会等待进一步的调用,一个简单的变量就足够了,在保存操作开始时递增,结束时递减。这样,我们就可以确保多个操作不会同时发生,交互逻辑消除了入口点的竞争。

希望你读得愉快,也许还能学到些什么。下次再见……

阅读更多关于:

博客 博客 博客 头条新闻

THE END
喜欢就支持一下吧
点赞1223 分享