Skip to content

RFC 0005:WebGAL 静态组合立绘 #1010

Description

@starrybamboo

RFC 0005:WebGAL 静态组合立绘

目标版本: WebGAL 主分支(以 4.6.2 立绘行为为基线)

composeFigure Demo、-composite 参数为个人维护的立绘合成分支的实验性命令,如果看到请无视~~

摘要

本 RFC 为 WebGAL 引入静态组合立绘。作者在单个角色目录中以 figure.json 声明画布、位图部件和可复用组合预设;剧本通过 changeFigure:<角色模板选择符>/<组合列表> 请求组合。引擎将最终有序部件序列预先合成为一张静态图,再完整复用现有图片立绘的位置、变换和进出场演出。

背景与目标

WebGAL 当前立绘演出以单张图片为基本单位。AI 生成资源和常规差分立绘常由底图、服装、表情等静态图片叠加而成;若为每个组合导出完整图片,会增加资源管理和导出成本。

本 RFC 的目标是:

  • 用简单的有序叠加语义表达静态组合,不引入槽位、替换、去重或部件树语义。
  • 复用 WebGAL 既有的立绘定位、变换和演出逻辑。
  • 为常用组合提供内存缓存、可选的持久化缓存和进度预热。
  • 让 Terre 提供可视化编辑、校验、指纹生成和剧本命令生成。

非目标

  • 不支持动态部件、部件级演出、局部替换、口型、眨眼或角色自动管理。
  • 不合成 GIF、APNG、视频、SVG、Live2D 或 Spine,也不组合多个模型。
  • 不更改现有普通图片、Live2D、Spine 立绘的路径语义和行为。
  • 首期不实现本轮合成立绘的 LRU、内存池或容量回收策略。
  • 不通过运行时监听文件变化来发现作者在 Terre 外直接修改的资源。

术语

  • 角色模板:一个角色一份 figure.json,声明部件和组合预设。
  • 角色模板选择符:组合语法斜杠前的名称,如 hana/summer_uniform 中的 hana
  • 组合预设:可递归引用部件或其他预设的有序名称列表。
  • 组合列表changeFigure 中斜杠后的逗号分隔名称列表。
  • 叠加序:组合列表及预设展开后得到的有序部件名称序列;后者绘制在前者之上。
  • 组合逻辑 URI:舞台状态和存档使用的内部字符串,如 webgal-composite:hana/summer_uniform_smile,cry

角色模板格式

角色模板路径固定为:

game/figure/<角色模板选择符>/figure.json

部件资源与模板同处该角色目录,srcfigure.json 所在目录解析。

{
  "Version": 1,
  "fingerprint": "6a7f9b7d1c4f5c4cb9c6b5e45d6fdf41f5bce4f0ddcfd482b8ba8541a357fb90",
  "canvas": {
    "width": 1600,
    "height": 3000
  },
  "components": {
    "body": {
      "src": "body.webp",
      "x": 0,
      "y": 0,
      "scale": 1
    },
    "uniform_summer": {
      "src": "parts/uniform-summer.webp",
      "x": 0,
      "y": 0,
      "scale": 1
    },
    "face_smile": {
      "src": "faces/smile.webp",
      "x": 0,
      "y": 0,
      "scale": 1
    }
  },
  "presets": {
    "summer_uniform": ["body", "uniform_summer"],
    "summer_uniform_smile": ["summer_uniform", "face_smile"]
  }
}

ABI 与名称

  • 根字段 Versionfingerprintcanvascomponentspresets 均为必填。
  • 首期仅接受 Version: 1。缺失字段、未知版本或非法指纹是模板错误,不进行尽力兼容。
  • 角色模板选择符、部件名和预设名均匹配 [A-Za-z0-9_-]+,大小写敏感。
  • 选择符必须与角色目录名逐字一致。
  • 部件名与预设名共享唯一名称空间,重名无效。

画布与部件

  • canvas.widthcanvas.height 为正整数,单边不超过 8192,总像素不超过 16,777,216
  • 每个部件的 srcxy 为必填;scale 可省略,默认值为 1
  • xy 必须是有限数,scale 必须是正有限数,允许小数。
  • x/y 表示部件左上角相对组合画布左上角的设计像素。图片按原始尺寸绘制后等比应用 scale
  • src 只能是角色目录内的相对路径。允许子目录和非 ASCII 文件名;禁止绝对路径、URL 和 .. 路径段。
  • 静态部件格式只接受 PNG、WebP、JPG、JPEG,扩展名不区分大小写。
  • 可声明暂未被任何预设使用的部件;同一 src 可由多个不同部件引用。

预设展开

  • 每个预设是非空名称数组,数组项可引用部件或其他预设。
  • 预设在引用位置进行深度展开,数组顺序就是绘制顺序。
  • 最终展开必须至少包含一个部件。
  • 重复引用保留,例如 ['body', 'face_smile', 'face_smile'] 绘制三层。
  • 缺失引用、循环引用、空预设和空展开结果均为模板错误。

剧本语法

组合立绘使用既有 changeFigure 命令:

changeFigure:hana/summer_uniform_smile,cry,sad -left;
changeFigure:hana/summer_uniform_smile -id=hana-copy -enter=universalSoftIn;
  • 斜杠前是角色模板选择符,斜杠后是一个或多个逗号分隔的部件/预设名称。
  • 列表项按书写顺序与预设展开结果组成最终叠加序。
  • -id 时继续使用左、中、右固定舞台槽位;有 -id 时继续使用同名自由立绘。
  • 同一个角色模板允许在多个舞台目标同时出现,不建立角色唯一性。
  • changeFigure:none-clear 保持既有清除语义,并优先于组合解析。
  • 含图片或模型扩展名的内容继续被当作原有文件路径处理。例如 changeFigure:hana/stand.webp 不是组合语法。
  • -composite 与现有 composeFigure Demo 不属于正式语法,将与 Demo 代码一并移除。

支持与拒绝的参数

组合立绘支持位置、-id-transform、缓动、进出场、时长、-zIndex-blendMode 和现有默认演出配置。

下列动态立绘参数与首期静态组合不兼容:-motion-skin-expression-bounds-blink-focus-mouthOpen-mouthClose-mouthHalfOpen-eyesOpen-eyesClose。组合命令使用其中任一项时,引擎记录错误并保持该舞台目标当前立绘,不静默忽略。

渲染与舞台行为

引擎将最终部件序列绘制到组合画布:

  • 使用普通 source-over 叠加;后出现的部件覆盖先出现的部件。
  • 组件允许越出画布,输出在画布边界裁剪。
  • 输出像素尺寸等于 canvas.widthcanvas.height,保留透明通道,采用普通图像平滑,不按显示设备 DPR 放大。
  • 合成结束后,输出作为一张普通图片立绘交给现有 Pixi 舞台。

因此,缓存未命中时旧立绘会持续显示,新组合准备完成后才按现有切换和进场演出替换;首次出场则在准备完成前保持为空。合成或校验失败时保留当前立绘;读档时没有旧立绘可保留的目标保持为空,但其他目标和读档流程继续完成。

模板加载、缓存与持久化

模板加载

figure.json 是角色资源中的静态声明文件,不是运行时注册表。某角色第一次被请求时,引擎读取、校验并放入本次进程的模板缓存;同一角色后续切换、预热和合成复用该缓存。游戏重启后首次使用时重新读取模板,以获得当前指纹。

引擎还对已读取的原始模板内容计算摘要。该摘要参与缓存验证,使 Terre 外直接修改 JSON 后不会错误采用旧缓存;直接替换图片后仍必须通过 Terre 保存模板或重新导出游戏重建资源指纹。

缓存键与查询顺序

组合缓存键由以下内容组成:

角色模板选择符 + 资源指纹 + SHA-256(最终展开后的有序部件名称序列)

资源指纹已覆盖画布、坐标、缩放、模板定义和资源内容,所以缓存键不重复写入 x/y/scale。舞台状态和存档始终保存作者原始的组合逻辑 URI,不保存 Blob URL、运行时对象 ID 或缓存路径。

查询顺序为:

本轮已生成、可直接显示的合成立绘 -> 持久化 WebP Blob -> 重新合成

持久化 Blob 解码失败时删除该项并重新合成;失败结果永不缓存,后续相同请求可重试。

持久化缓存

  • 默认关闭,由玩家在设置页按游戏开启。
  • 使用独立的 localforage 存储库,以游戏键隔离,不写入频繁保存的用户设置对象。
  • 持久化格式为浏览器原生 Canvas 编码的 WebP Blob,编码质量参数取最高值。
  • 开启后,已在本轮生成的组合在后台重新栅格化、编码并补写;不为等待开关长期保留高内存原始画布。
  • 关闭时立即删除持久化项但保留本轮可显示的合成立绘。
  • “清理组合缓存”只清理当前游戏的持久化 Blob 和索引,不改变当前画面。
  • 编码或写入失败时,游戏继续使用本轮缓存,向玩家提示一次,并自动关闭开关。

已知编码限制

首期使用浏览器原生 canvas.toBlob('image/webp', 1),不新增 WASM 编码依赖。Web 平台没有可移植的 losslessexact 控制项,质量参数 1 也只是编码器提示;因此持久化 WebP 不能保证逐像素无损,可能与本轮直接合成的画面存在轻微差异。该限制只影响持久化缓存,不能生成 WebP 或写入失败时按既有错误策略回退本轮缓存。

未来若引入可控制的 libwebp WASM 编码器,必须自动使现有原生编码缓存失效并重新生成,不能要求玩家手动清理缓存。

预取、预热与快进

现有资源预取仍以 <link rel="prefetch"> 为基础:初始场景窗口为前 24 行,运行中按当前语句后 20 行继续预取。组合立绘在模板尚未加载时先读取模板,随后预取最终需要的普通部件图片;在这些资源可用后才进入组合预热。

  • 后台预热在浏览器空闲时间运行,一次只合成一个组合。
  • 实际要显示的组合优先于未开始的预热任务,并与相同缓存键的预热共享结果。
  • 持久化 WebP 编码和写入始终在后台,不延迟画面切换。
  • 快进时,同一舞台目标尚未开始的旧组合任务由最新请求替换。
  • 已开始的绘制或编码不强行中断;其结果只可进入缓存,不能覆盖该舞台目标的最新请求。

存档、诊断与资源更新

读档从组合逻辑 URI 按当前模板和资源指纹恢复,不保存历史合成像素。因此,作者更新立绘后旧存档会显示当前组合,而非旧图。

恢复舞台时,引擎等待所需组合准备完成后再继续游戏。模板、资源或合成错误记录选择符、组合列表和原因,供作者诊断;这类错误不向玩家弹窗。只有持久化编码或写入失败才提示玩家,因为它影响玩家主动开启的本地空间功能。

Terre 保存某角色模板时重建该模板指纹;导出游戏前扫描、校验并重建全部角色模板。Terre 外直接修改 figure.json 可由运行时模板摘要发现;Terre 外直接替换图片则需要作者保存模板或重新导出游戏。资源指纹改变后旧持久化项不再命中,并异步清理。

Terre 交付

Terre 与引擎同一期交付角色模板编辑器。

  • 双击 game/figure/<角色模板选择符>/figure.json 打开专用编辑器;其他 JSON 保持普通文本资源行为。
  • 编辑器从角色目录选择静态图片,编辑画布、部件坐标和等比缩放,维护有序预设引用,并提供实时预览。
  • 保存时执行模板校验并自动生成资源指纹;无效模板不能保存或导出。
  • 资源侧的 ChangeFigure 面板提供“普通图片 / 组合立绘”模式;组合模式选择模板和可排序名称列表,可保留重复项,并可跳转模板编辑器。
  • 保存模板后,Terre 通过现有预览同步通道通知预览引擎失效该选择符。预览引擎清除该模板的本轮缓存,仅重新合成舞台上正在使用该选择符的组合,不刷新预览页面。

建议:共享规则与手写模板支持

无论最终采用共享包、共享源码或其他交付方式,Terre 与引擎必须使用行为一致的模板校验、展开和指纹规则。

建议在 WebGAL 仓库提供一个版本化、纯 TypeScript 的组合规则包,供 Terre 锁定兼容版本,而不复制规则源码。该包可以负责:

  • 模板校验。
  • 组合选择符解析。
  • 预设展开。
  • 缓存输入规范化。
  • 资源指纹算法。

兼容性与迁移

  • 没有 figure.json 的游戏、既有普通图片路径和既有单模型立绘不受影响。
  • figure.json 不进入普通立绘资源 URL;否则现有 .json 立绘识别会将其视作 Live2D 模型。
  • 组合语法仅由严格的 角色模板选择符/组合列表 识别,含资源扩展名的内容继续视为路径。
  • 当前 composeFigure Demo、-composite 参数、相关解析分支和测试从主线移除,不提供与正式语义并存的兼容层。

实现顺序与验收

  1. 建立共享规则包、Node 指纹工具和覆盖有效/无效模板、展开、规范化、指纹的测试夹具。
  2. 移除 composeFigure Demo,在 WebGAL 解析器、运行时、舞台状态、存档和缓存层实现正式组合语义。
  3. 接入预取、空闲预热、快进合并、持久化 WebP 和玩家设置。
  4. 在 Terre 实现模板编辑器、剧本命令面板、导出扫描和预览失效通知。
  5. 验收普通图片回归、组合顺序与重复项、缓存命中/失效、读档、快进、持久化开关、Terre 保存及导出,并验证 Web、Electron、Android 资源路径与大小写行为。

结论

静态组合立绘以角色模板、顺序叠加、预合成单图和可选持久化缓存为核心。它扩展静态差分立绘的表达力,同时保持 WebGAL 现有单图片立绘架构、舞台演出和存档模型稳定;动态角色能力留给后续独立设计。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions