Skip to content

线程与高频更新

Live2D 的场景树和 Cubism 节点属于 Godot 主线程。API 把“有顺序的命令”和“可合并的连续状态”分成两条路径。

普通异步调用

InvokeAsync 适合需要完成状态、返回值或异常的操作:

csharp
await Live2DApi.InvokeAsync(() => model.PlayMotion("TapBody", 0), cancellationToken);
var snapshot = await Live2DApi.InvokeAsync(() => model.Snapshot, cancellationToken);
  • 已在主线程时立即执行。
  • 排队期间取消会阻止回调开始。
  • 已经开始的回调不会被强制中断。
  • 回调异常通过返回的 Task 重新抛出。

Post 是低频 fire-and-forget 通知;异常只写入 Live2D 日志:

csharp
Live2DApi.Post(() => model.SetVisible(true));

初始化前先检查 Live2DApi.IsDispatcherReady。不要在主线程同步等待一个需要主线程执行的 Task。

连续状态流

位置跟踪、音频响应或面捕不应为每个采样创建 Task。使用 QueueUpdate

csharp
model.QueueUpdate(update =>
{
    update.Position = trackedPosition;
    update.RotationDegrees = trackedRotation;
    update.Opacity = trackedOpacity;
});

同一模型尚未执行的更新会合并:不同字段全部保留,同一字段最后提交值获胜。该接口适合不需要逐次确认的状态流。 传给 QueueUpdate(Action<Live2DModelUpdate>) 的配置委托会在调用线程立即执行;只有构造完成的更新数据会进入主线程队列。 不要在该委托中读取 Snapshot 或访问其他 Godot 对象。

Parameter 与 Part 队列

csharp
model.QueueParameters(new Dictionary<string, float>
{
    ["ParamAngleX"] = angleX,
    ["ParamMouthOpenY"] = mouthOpen,
    ["ParamEyeLOpen"] = leftEyeOpen,
    ["ParamEyeROpen"] = rightEyeOpen,
});

model.QueuePartOpacity("PartArmL", armOpacity);

ID 以不区分大小写的方式合并,同一 ID 最后值获胜。批次执行时模型不可用则丢弃,不会排队到下一次场景。

选择规则

场景使用
播放动作、设置表情、销毁实例InvokeAsync
需要返回值或捕获异常InvokeAsync
低频且不关心结果Post
连续位置、旋转、透明度、滤镜QueueUpdate
面捕、口型、部件透明度Parameter/Part Queue API

Motion、Expression、Physics 和 Pose 可能在后续帧继续修改 Parameter,需要持续驱动的输入源应持续提交采样。

Last updated: