跳转到内容

Steam

Steam 是桌面目标下的一个发行渠道,不是一个单独的平台。它不定义运行时、不定义渲染器、 不定义资源格式——它定义的是这份构建去哪儿。所以没有“Steam 构建”这回事:只有桌面构建, 外加一个开关,让导出顺带写出 SteamPipe 需要的东西。

桌面构建是原生应用——引擎的 C++ 核心,内嵌 Dawn(macOS 走 Metal,Windows 走 D3D12,Linux 走 Vulkan)与 QuickJS-ng,窗口与输入来自 SDL3。进程里没有浏览器, 这也是 Steam 覆盖层、Steam Input 和独占全屏能如玩家预期那样工作的原因。

  1. 一个 Steamworks 应用。 合作伙伴后台给的 App ID——depot、启动选项和成就都住在那里, 任何构建都写不到。

  2. Steamworks SDK,从合作伙伴站点自行下载。Estella 不随包携带:它的再分发只授权给 Steamworks 合作伙伴,而运行时模板是所有用这个引擎的游戏共用的同一个预编译二进制。

  3. steamcmd,Valve 的上传工具。先在终端里登录一次,让 Steam Guard 通过并缓存下来—— Estella 不保存任何密码。

打开 文件 → Build…,在目标列表里选 Steam——它列在「桌面」下面。Steam 是桌面构建的 一个去处,所以它从那个目标分叉出来,而不是与它并列。这个渠道要填的东西都在这一页上。

打包项目对话框的 桌面 → Steam 页 Steam,以及 Steam 分组里的 App ID、Steamworks SDK 和各 OS 的 depot id

设置 含义
发行渠道 Steam——构建旁边会写出 depot 脚本。在目标列表里点 Steam 就是在设它,两者是同一个值。
Steam App ID 来自合作伙伴后台。不填就什么也不写:用猜出来的 id 生成的脚本指向的是别人的游戏。
Steamworks SDK 你下载的那份 SDK。它 redistributable_bin/<os>/ 下的库会被打进应用。
Depot ID 留 0 则用 App ID + 1、+2…。这些由 Valve 分配,生成的清单会提示你去核对。

成就 id 不在这一页,它在 项目设置 → 打包 里——因为每个商店都有这份同样的清单, 只是名字不同。声明它们,才能让「解锁一个后台根本没有的 id」在发生处就被拒绝, 而不是被接受、然后悄无声息地丢掉。

然后点 打包。应用旁边会得到:

dist-desktop/
MyGame.app/ ← macOS,以及/或者
MyGame/ ← Windows:MyGame.exe + Content/
← Linux: MyGame + Content/
steam/
app_build_<appid>.vdf
depot_<id>_<os>.vdf
STEAM.md ← 本次构建的确切取值

值得读的是 STEAM.md。通用说明属于本文这样的文档;查不到的是这一个构建需要把什么粘到哪里—— 每个 OS 的启动可执行文件、这次用的 depot id、游戏真正写入的 Auto-Cloud 路径,以及要去后台建的成就 id。

Terminal window
steamcmd +login <你的账号> +run_app_build "…/steam/app_build_<appid>.vdf" +quit

脚本里写着 Preview "1",所以第一次跑什么也不会上传——它只报告它会做什么。要真上传就把它去掉。 SetLive 是刻意留空的:发布到某个分支是另一个决定,在后台做,而且那正是无法靠“再传一次”撤销的那个。

AchievementsAdsLeaderboard 一样是引擎服务,所以有没有商店,游戏代码都一样:

import { Achievements } from 'esengine';
const achievements = world.getResource(Achievements);
achievements.setStat('enemies_defeated', count);
await achievements.unlock('FIRST_BLOOD');
await achievements.store(); // 推到 Steam;商店在这一步后面做批量提交

它永远可用。背后没有商店时,解锁记在本地——那正是你自己的成就界面要读的同一份数据—— 而 available 告诉你商店是否也会收到,UI 据此决定是用平台的弹窗还是自己画。

成就静默失效有两种方式,引擎各自的处理:

  • 后台没有这个 id。 Steam 会接受它然后什么也不做,最后是玩家在几个月后发现。所以 id 在项目设置里声明一次,越界的 unlock 在发生处就被拒绝,并把已声明的清单一并报出来。
  • 包里没有 Steam 库。 把 SDK 那一项指向你的下载;没有的话导出会警告,STEAM.md 开头也会写明。

Storage 写在操作系统指定的位置,而那正是 Steam Auto-Cloud 能同步的路径,游戏侧一行代码都不用写。 在 Steam Cloud → Auto-Cloud 里填 STEAM.md 打印出来的根目录——它们是照应用实际写到哪得出的, 不是抄自某份文档里的表:

OS 根目录 子路径
Windows WinAppDataRoaming Estella/<名字>
macOS MacHome Library/Application Support/Estella/<名字>
Linux LinuxHome .local/share/Estella/<名字>
  • macOS 构建是 ad-hoc 签名的,而且只能在 Mac 上签。公开发布前请用你自己的 Developer ID 公证。
  • steamcmd 没有做进编辑器。 清单里给出的是可以照抄的确切命令;一个在这里验证不了的功能, 不如一行你能复制的命令诚实。
  • 创意工坊、DLC、内购、Steam Input 动作集不在覆盖范围内。它们都能挂在同一套 flat API 和同一个 服务面上,但每一个都是自己的需求,不该由“支持 Steam 发布”一次性带出来。