Skip to content

feat: 支持静态组合立绘与相对动画 - #1027

Open
starrybamboo wants to merge 3 commits into
OpenWebGAL:devfrom
starrybamboo:codex/composite-figures-relative-animation
Open

feat: 支持静态组合立绘与相对动画#1027
starrybamboo wants to merge 3 commits into
OpenWebGAL:devfrom
starrybamboo:codex/composite-figures-relative-animation

Conversation

@starrybamboo

@starrybamboo starrybamboo commented Aug 10, 2026

Copy link
Copy Markdown

关联 #1010

概要

本 PR 提交以下引擎侧能力:

  • 使用新的 character 脚本管理静态组合立绘;
  • 支持 非绝对位置播放的animation(就是立绘 transform之后,仍然能正常播放animation,经测试,dev分支并不能这个样子);
  • 通过现有 Figure Target 与 Pixi 演出链路,异步合成立绘并交付到舞台。

character 脚本

每条角色声明包含角色名,以及本次需要使用的完整、有序部件列表:

character: yuki/summer_uniform,face_smile -left;
character: yuki/summer_uniform,face_sad -id=yuki -enter=jump;

可以按角色名清除,也可以清除所有由 character 管理的立绘:

character: yuki -clear;
character:none;

同名角色同时只能占用一个 Figure Target。将角色移动到其他位置或 ID 时,会先清除旧目标,再更新新目标。

这个可能需要商榷,我个人是希望能这样的,这样就不用手动管立绘的退场了。(方便AI写脚本)

位置、ID、transform、transition、duration、z-index 与 blend mode 等行为继续委托给现有 changeFigure 链路。

角色模板

模板固定从以下路径加载:

game/figure/<角色名>/figure.json

最小的 Version 1 模板示例:

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

部件与预设会按声明顺序递归展开,重复引用会被保留;后出现的图层绘制在先出现的图层之上。

部件可以使用图片原始尺寸、正数等比 scale,或成对指定 widthheight。目前支持静态 PNG、WebP、JPG/JPEG,部件路径必须保持在角色目录内。

模板、已完成的组合结果和正在执行的合成任务均使用内存缓存。加载与合成不会阻塞剧本流程。

舞台状态与存档中保存的是稳定的角色逻辑来源。生成结果作为普通图片交付,因此 figure.json 不会进入 Live2D 资源解析链路。

相对动画资源

原有的纯帧数组动画格式保持不变,但加载后直接使用相对语义:

[
  {
    "position": { "x": 0, "y": -30 },
    "duration": 100,
    "ease": "linear"
  }
]

动画帧会与目标已解析的 transform 组合:

  • positionrotation 使用加法;
  • scalealpha 使用乘法;
  • 其他变换字段保持原有的赋值行为。

这是对原有数组格式的语义迁移,不新增 { "effects": [...] } 包装格式,也不增加用户侧 frameMode 或绝对帧开关。只有引擎内部生成且未标记的 transform、进场与退场时间线继续使用绝对值,避免内部关键帧被重复叠加。

为什么放在同一个 PR

组合立绘复用了 Figure 的变换与动画,但最终 Pixi 图片是在脚本完成舞台状态演算后才异步生成的。

图片准备完成时,延迟演出必须以命令执行时解析出的原始 transform 为基准重新构造动画。因此,相对动画帧映射与组合立绘交付会在同一个演出边界相交:异步替换 Pixi 图片后,仍需保留 Figure 的位置基准并重放其入场动画。

将两者放在同一个 PR,可以继续复用统一的 Figure 动画链路,不需要增加一套 Character 专用动画实现。

兼容性与范围

  • 原有纯数组动画资源保持原格式,并统一使用相对语义;
  • 普通图片、Live2D 与 Spine 继续走现有 Figure 同步链路;
  • 只有 Character 内部逻辑来源会被异步组合适配器接管;
  • Character 复用现有 Figure Target、effect、animation setting、metadata 与存档状态;
  • 不加入持久化缓存、IndexedDB、localforage、预取/预热线路或任何新依赖;
  • Terre 侧的可视化编辑能力不在本 PR 范围内。

验证

  • yarn parser:test --run:2 个测试文件、29 个测试通过;
  • yarn build:Parser Rollup、WebGAL TypeScript 与 Vite 生产构建通过;
  • git diff --check:通过;
  • Terre 实际预览:
    • 组合立绘可以正常显示在左右位置;
    • 异步合成后仍保留显式 transform;
    • BA-jump-twice 相对动画结束后回到原 transform 基准位置。

Copilot AI lite review requested due to automatic review settings August 10, 2026 18:29
@starrybamboo starrybamboo changed the title feat: add static composite characters and relative animations feat: 支持静态组合立绘与相对动画 Aug 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds engine support for static composite “Character” figures (managed by a new character script command) and introduces structured animation resources whose frames can be interpreted relative to a target’s resolved/base transform, enabling deferred (async) figure delivery while preserving animation behavior.

Changes:

  • Added a new character script command and a Character composition pipeline (template parsing/validation, async image composition, and Pixi delivery sync).
  • Added “relative frame” animation support via structured animation resources and runtime frame composition on top of a resolved base transform.
  • Extended figure/animation state to preserve a baseTransform for deferred presentation replay.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/webgal/src/Core/parser/sceneParser.ts Registers the new character script tag in the runtime script map.
packages/webgal/src/Core/Modules/stage/stageInterface.ts Adds baseTransform to stage animation settings to preserve a resolved baseline transform.
packages/webgal/src/Core/Modules/animations.ts Adds structured animation resource parsing and relative-frame marking via createUserAnimation.
packages/webgal/src/Core/Modules/animationFunctions.ts Composes relative animation frames against a computed/resolved source transform (incl. optional override).
packages/webgal/src/Core/initializeScript.ts Uses createUserAnimation when loading user animation JSON resources.
packages/webgal/src/Core/gameScripts/character.ts Implements the character script command and delegates presentation to changeFigure.
packages/webgal/src/Core/gameScripts/changeFigure.ts Persists baseTransform into animation settings and refactors transform baseline handling.
packages/webgal/src/Core/controller/stage/pixi/syncPixiStageState.ts Integrates Character figure sync and deferred presentation into Pixi stage synchronization.
packages/webgal/src/Core/controller/scene/sceneInterface.ts Adds commandType.character to the runtime command enum.
packages/webgal/src/Core/character/characterTemplate.ts Defines/validates character templates and resolves component/preset selections into ordered layers.
packages/webgal/src/Core/character/characterImageComposer.ts Loads component images and composes them into a single bitmap for delivery.
packages/webgal/src/Core/character/characterFigureTarget.ts Resolves and enumerates figure targets (positional slots + free figures) for Character management.
packages/webgal/src/Core/character/characterFigureSourceSync.ts Handles async composition requests and applies only the latest result per target (epoch gating).
packages/webgal/src/Core/character/characterFigureSource.ts Serializes/parses stable Character figure sources and collects Character targets from stage state.
packages/webgal/src/Core/character/characterFigureService.ts Loads/validates templates and caches in-flight + completed compositions in memory.
packages/webgal/src/Core/character/characterDeferredPresentationRuntime.ts Replays deferred enter animations using stored baseTransform when the composed image arrives.
packages/parser/src/interface/sceneInterface.ts Adds commandType.character to the parser-side command enum.
packages/parser/src/config/scriptConfig.ts Registers character as a supported script token in the parser config.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +200 to +202
if (sourceUrl && parseCharacterFigureSource(sourceUrl)) {
return;
}
Comment on lines +49 to +54
for (const target of listFigureTargets(state)) {
const source = parseCharacterFigureSource(target.source);
if (source) {
targets.push({ key: target.key, position: target.position, source });
}
}
Comment on lines +23 to +27
for (const { layer, image } of loadedLayers) {
const { width, height } = resolveDrawSize(layer, image);
context.drawImage(image.source, layer.x, layer.y, width, height);
}
return canvas.toDataURL('image/png');
Comment on lines +20 to +29
export function createUserAnimation(name: string, resource: UserAnimationResource): IUserAnimation {
if (Array.isArray(resource)) {
return { name, effects: resource };
}
return {
name,
effects: resource.effects,
...(resource.frameMode !== 'absolute' ? { frameMode: 'relative' as const } : {}),
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants