fix: 修复运行时参数写入逐帧累加 - #85
Open
Enterpr1se0 wants to merge 2 commits into
Open
Enterpr1se0 wants to merge 2 commits into
Enterpr1se0 wants to merge 2 commits into
Conversation
The engine saves the current parameter values as its baseline at the start of every frame and restores them at the end, so a write made after `internalModel.update()` is baked into the next frame's baseline. `add` writes are relative to the current value, so every frame stacked on top of the previous frame's own result: the parameter reached its limit within a few frames and stayed there (head pinned at +/-30, `breath` pinned at 1). - apply `add` as `engine baseline + contribution` instead of `current value + contribution` - release the contribution once a writer stops - `reset()` drops pending writes and bookkeeping on a model switch - `override` semantics unchanged Also drops the per-parameter `filter()/filter()/reduce()/reduce()` allocations.
Enterpr1se0
force-pushed
the
fix/parameter-accumulation-and-idle-motion
branch
from
September 21, 2026 10:38
f67d06b to
8daccf7
Compare
LIlGG
requested changes
Sep 24, 2026
LIlGG
left a comment
Owner
There was a problem hiding this comment.
感谢提交 PR。 不过目前通过扣除旧贡献的方法可能会留下偏移,我建议使用引擎已有的 beforeModelUpdate 来处理这段逻辑可能会更加合适,这样也无需通过数值来猜测上一帧的贡献。
Enterpr1se0
force-pushed
the
fix/parameter-accumulation-and-idle-motion
branch
2 times, most recently
from
September 24, 2026 07:01
de61f4c to
8daccf7
Compare
…event The engine saves the parameter values as a baseline every frame and restores that baseline at the end of the same frame, so a write made after `internalModel.update()` became part of the next frame's baseline: an `add` write stacked on top of its own previous result until the parameter reached its limit. The previous approach recovered the baseline by subtracting our own previous contribution, which can leave an offset behind - a fading motion blends on top of our leftover, so the recovered value is only approximate for the duration of that fade. Apply the writes from the engine's `beforeModelUpdate` event instead: it runs after the baseline was saved and before the model is rendered with those parameters, so a write is visible for exactly one frame and the engine drops it afterwards. No contribution bookkeeping or value guessing is needed. - `add` is applied on top of the engine's current value - `override` keeps its conflict resolution, now resolved in the same single pass - drops `AppliedAdd`, `appliedAdds`, `releaseStaleContributions` and the write-back comparison Note: a one-shot `override` (the DevTools parameter slider) is now applied for the frame it was queued in; a caller that needs it to hold has to queue it every frame.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #84
背景
看板娘的鼠标追踪会甩到极限后卡住不回来、呼吸恒定在 1。定位到问题出在参数写入:
add写入被引擎每帧的基线吸收而逐帧累加(详见 #84)。改动(1 个 commit)
fix: stop additive parameter writes from accumulating across frames引擎每帧
saveParameters()会把「当前值」(含插件上一帧写入)存成基线,因此add不能叠在「当前值」上。add改为写入引擎基线 + 贡献的绝对值;引擎这一帧自己改过的参数则交还给引擎。reset(),在controller.initialize()中调用,避免换模型时把上一个模型的待写入/贡献带到新模型。override语义不变(含同优先级「先入队赢」的规则)。resolveParameter里每参数每帧 4 次数组分配(filter()/filter()/reduce()/reduce())换成单趟遍历,使本次修复的开销 < 0.2µs/帧。为什么改在 coordinator:插件有 8 个参数写入点(procedural / motionLayer / FSM ×2 / emotion / animator / devtools / controller),由于
setSemantic()只要挂了 coordinator 就改走queueWrite(),它们全部汇聚到ParameterCoordinator.flush(),所以这是唯一的汇聚点,不需要改动语义层、调用方,也不需要 patch 引擎内部。验证
单测:188 个,187 通过(
packages/live2d/src/runtime/controller/__tests__/coordinator.test.ts新增 8 个)。唯一失败的Live2dDevTools > toggle switches visibility state是仓库既有问题(测试调用了组件里不存在的_toggleVisible()),与本 PR 无关。类型检查:
tsc -bexit 0。浏览器实测(真实后端 + 真实模型 + 真实鼠标事件,读语义层参数;鼠标移到画布右侧再移回中心):
Motions)Potion-Maker/Pio,自带 idle)兼容性
queueWrite/flush/getConflictLog/clearConflictLog签名不变,仅新增reset()。override、参数钳位、add求和、override + add、冲突解析与冲突日志顺序/内容均与原实现一致(已逐项对照,并有测试覆盖)。add的参考点(当前值 → 引擎基线)、写入方停止后撤回贡献、换模型时丢弃待写入。values * (1 - w) + motion * w,会把我们上一帧贡献的(1 - w)漏进基线,此时基线还原只是近似(已在代码注释中说明)。已知未处理
引擎自带的
CubismBreath每帧用addParameterValueById给ParamAngleX/Y/Z、ParamBodyAngleX叠加正弦摆动(振幅 15/8/10/4),与插件自己的呼吸模块重复。这是设计取舍(关掉会让模型明显变呆),如需处理建议单独 PR + 配置开关。上表Potion-Maker/Pio的angleY会漂到 -30 上限也来自这里(未打补丁的上游同样如此)。