Switch游戏模拟指南

经过我个人测试,大部分 Switch 游戏都能在 PC 上流畅运行。偶有崩溃的情形,但是频率不高。

模拟器

目前最流行的是 yuzu 和 Ryujinx, 两者模拟效果差不多,yuzu 有中文翻译。我这里选了 yuzu。

硬件要求

  1. CPU:五年内的主流 x86_64 CPU 应该都没问题,推荐四核心以上。我用的 AMD 5600X。
  2. GPU:需要 GTX 1060 及以上水平的显卡才能流畅运行。我的 AMD RX 6700 可以跑两倍分辨率。
  3. 手机 SOC:Android 需要骁龙 8+ 以上的 SOC 才能流畅运行马里奥赛车之类的游戏。
  4. 手柄:通用的双摇杆手柄就行。

系统要求

目前仅支持 Windows, Linux 和 Android,苹果用户暂时没有办法玩了。

安装模拟器

官方下载连接

Linux 用户有几种选择:

  1. 从官网下载 AppImage 包 https://yuzu-emu.org/downloads/
  2. 从 Flathub 安装 https://flathub.org/apps/details/org.yuzu_emu.yuzu
  3. 在你发行版的 OBS, AUR, PPA 找一找有没有打包好的

安装好,运行应该是这样子的:

如果启动后界面是英文的,从菜单栏选择 Emulation –> Configure… –> General –> UI –> Interface language 修改成中文。

安装Switch系统固件(非必需)

部分游戏没有固件也能运行。但是安装固件可以让更多游戏能玩,减少一些游戏崩溃的概率。《塞尔达:王国之泪》需要 16.0.1 或更新的固件才能运行。

https://archive.org/download/nintendo-switch-global-firmwares

下载固件 zip,一般用最新的。解压后会得到几百个小文件。

打开 yuzu,通过菜单栏 文件 –> 打开 yuzu 文件夹,然后依次打开 nand –> system –> Contents –> registered 文件夹,将之前解压的所有小文件都复制到这里。

安装 prod.keys 和 title.keys

这两个是关键的密钥文件。去网上搜一搜就好了,这里就不提供下载了。

prod.keys 是必需的系统密钥,缺少了 prod.keys 密钥就玩不了游戏。prod.keys 和游戏无关,不管是新游戏还是老游戏,都不用更新 prod.keys。但是 prod.keys 和固件版本相关,比如 14.0 固件的 prod.keys 在 15.0 固件里就用不了。在网上搜索的时候,要带上固件版本号“prod.keys v16.0.0”。

title.keys 不是必需的,只有联机才需要。

打开 yuzu,通过菜单栏 文件 –> 打开 yuzu 文件夹,然后打开 keys 文件夹,将你找到的 prod.keys 和 title.keys 文件都复制到这里。

下载游戏 ROM

最常见的游戏 ROM 有 NSP 和 XCI 两种格式。yuzu 都能识别。下载完 ROM 之后,建议放到一个文件夹下面。

双击 yuzu 窗口中间,选择你的游戏文件夹,yuzu 会自动扫描文件夹里的游戏。

安装更新和 DLC

如果你还下载了游戏更新和 DLC(通常也是 NSP 和 XCI 格式),点击菜单栏的 文件 –> 安装到 NAND, 选择你的更新和 DLC 文件即可安装。

运行游戏

单击游戏即可运行。

差不多就是这样!

安装 MOD

Switch 性能比较差,像塞尔达这样的游戏,只能锁了 30 帧运行。但是在 PC 上模拟,硬件性能不再是问题。通过安装 60FPS 的 MOD, 我们可以玩到比 Switch 更流畅的体验。

首先打开官方 MOD 下载页面,搜索英文“Zelda”,找到对应的游戏。然后下载名为 60FPS 的 MOD。注意:尽量选择 MOD 支持的游戏版本,以免游戏崩溃。

下载完成后,解压文件,得到一个 60FPS 文件夹。

打开 yuzu,在游戏列表中找到塞尔达,右击,选“打开 MOD 数据位置”。

将之前解压得到的 60FPS 文件夹,复制到此处即可。

然后启动塞尔达,就能够以 60FPS 的流畅度玩了!如果有 3080 / 6900 这样的高端显卡,甚至可以2倍缩放画面,实现 4K@60FPS 的体验。

还有很多其他类型的 MOD, 大家可以自行探索。

浏览器兼容性与 Polyfill 技术

开发者通常在最新的浏览器上进行开发,以使用最新的 JavaScript 语法,浏览器接口,让工作更有效率,代码质量更高,更不容易过时。

而用户则可能仍然在使用旧版的浏览器。比如,某些政府教育系统仍然在使用 Windows XP,用户能安装的最新的浏览器是 Chrome 49。有些用户可能只是没有升级浏览器的习惯,导致他们的 Chrome 还是 86 版本。

为了平衡两方面,开发者通常会借助一些工具,自动转译语法,并 Polyfill 新的接口,以兼容某个特定版本的旧浏览器。通过语法分析,按需插入 core-js 中的 Polyfill 代码。而 core-js 缺少的部分,可以用 polyfill-service 补全。

ES 语法转译

Optional Chaining 这种比较新的 ES 语法在老版本浏览器无法支持,运行时会报错。

const dogName = adventurer.dog?.name;

为了兼容旧版浏览器,必须在构建过程中就将其转译成 ES5/ES3 语法。

var _adventurer$dog;
const dogName = (_adventurer$dog = adventurer.dog) === null || _adventurer$dog === void 0 ? void 0 : _adventurer$dog.name;

转译功能通常由 babel,swc,esbuild 等 JavaScript 转译工具提供。

ES 特性 Polyfill

Promise,Stream 等特性需要插入特定的 Polyfill 代码。

esbuild 无法自动分析并 Polyfill 特性,只能手动导入。而 swc 和 babel 可以自动分析源码,按需自动导入。swc 和 babel 都是基于 core-js 进行 polyfill。

浏览器特性 Polyfill

core-js 只提供 ES 特性的 Polyfill(准确的说,是浏览器和 Node.js 交集的部分)。但是浏览器除了 ES 标准外,还提供其他浏览器特有的特性,比如 ResizeObserver。这就需要一个补充方案。

方案1是手动取导入 Polyfill 代码,比如在项目中 import 'whatwg-fetch' 就可以支持 fetch() 接口。但是这对开发者而言是一个比较大的负担。

方案2是在页面中引用 polyfill.io 的脚本,服务器会根据 UA 判断需要加载哪些 Polyfill 代码。开发者也可以用 polyfill-service 自建服务器。缺点是增加了一些服务器开销,也会阻塞应用本体脚本的加载,造成一些成本提高和性能损失。

CSS 语法转译

很多 CSS 特性在未成为正式标准之前便已经被浏览器支持了。但是在这个成为标准的过程中,CSS 特性的提案可能会修改。浏览器厂商为了防止自己目前的实现与之后的正式标准冲突,会在选择器和属性名之前,加上自己的前缀,Firefox 是 -moz-,Chrome 和 Safari 是 -webkit-,而 IE 是 -ms-。

比如下面这段代码:

:fullscreen {
}

为了兼容不同的浏览器,尤其是老浏览器,需要转译成:

:-webkit-full-screen {
}
:-ms-fullscreen {
}
:fullscreen {
}

转译过程主要通过 postcss 实现。当前流行的前端应用构建工具,比如 create-react-app, vite, parcel, 都集成了 postcss。

横向对比

babelesbuildswcpolyfill servicepostcss
ES 语法转译
ES 特性 Polyfill
浏览器特性 Polyfill
CSS 语法转译

总结

综合来看,目前没有一种技术能够单独达成全自动的浏览器兼容性。建议使用 babel + polyfill service + postcss 之类的融合方案。

当使用一种新特性时,可以先查一下 caniuse.com 支持的浏览器版本。如果需要 Polyfill,要看自己选用的 Polyfill 方案(core-js,polyfill-service)是否支持。如果不支持,则需要手动 Polyfill 这个特性。

实践

Create React App

以 create-react-app 项目为例。首先我们需要配置项目支持的浏览器版本,让底层的 babel 和 postcss 将 JS 和 CSS 转译到目标浏览器支持的语法,并尽可能注入所需的 Polyfill 代码:

{
  "browserslist": {
    "production": [
      "chrome > 49",
      "firefox > 53",
      "ie 11"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

然后,我们需要修改 HTML 模板,注入 Polyfill.io 的脚本,以支持额外的 Polyfill 特性:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://polyfill.io/v3/polyfill.min.js"></script>
  </head>
  <body>
    ...
  </body>
</html>

Vite

Vite 默认只支持 Chrome 87+,这个要求对大部分实际应用可能太过苛刻。如果要支持更早的浏览器,则必须使用 legacy 插件:

import react from '@vitejs/plugin-react';
import legacy from '@vitejs/plugin-legacy';
import { defineConfig } from 'vite';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    legacy({
      targets: [
        "chrome > 49",
        "firefox > 53",
        "ie 11"
      ],
    }),
  ],
});

然后,我们需要修改 HTML 模板,注入 Polyfill.io 的脚本,以支持额外的 Polyfill 特性:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://polyfill.io/v3/polyfill.min.js"></script>
  </head>
  <body>
    ...
  </body>
</html>