TiXL Lib.flow ExecRepeatedly 操作符完全指南:用 RepeatCount 与 SkipFrameCount 精确控制子图执行频率
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
ExecRepeatedly 是 TiXL(t3)实时运动图形引擎中Lib.flow流程控制库的核心操作符,用于精确控制子图(sub graph)的执行频率。本文以其官方文档与仓库源码为主线,完整讲解三个输入参数的含义、默认值与取值范围,深入剖析其执行时序与钳制逻辑,并结合Execute、ExecuteOnce、Loop等兄弟操作符给出实战选型建议。读完本文,你将能在模拟加速/减速、抽帧优化、帧步进等场景中自如使用该操作符。
功能定位:控制子图的执行频率
根据官方文档,ExecRepeatedly 的作用是"控制子图被执行的频繁程度"(Controls how frequently the sub graph is being executed),并明确指出它"可用于提高或降低模拟速度"(This can be useful for increasing or slowing down simulation speeds)。在 Lib.flow 的算子目录中,它被描述为:Controls how frequently the sub graph is being executed,归属于流程控制(flow)命名空间。
这意味着它不是普通的"每帧执行一次"的开关,而是一个可编程的执行节流器:通过两个整数参数,你可以让一段子图在一帧内重复执行多次(加速模拟),也可以让它每隔若干帧才执行一次(减速模拟、节省开销)。
输入参数详解
官方文档列出了三个输入参数,结合仓库源码与默认配置,它们的完整语义如下:
| 参数名 | 类型 | 说明 | 默认值 | 运行时钳制范围 |
|---|---|---|---|---|
| Command | Command(多输入槽) | 要被执行/节流的子图命令,可连接多条输入 | null | — |
| RepeatCount | Int32 | 每次触发时子图重复执行的次数,0 表示禁用 | 1 | 0 ~ 100 |
| SkipFrameCount | Int32 | 触发前跳过的帧数(调用次数),实现抽帧减速 | 0 | 0 ~ 10000 |
三个参数的默认值均可在序列化文件 ExecRepeatedly.t3 中确认:RepeatCount默认 1、SkipFrameCount默认 0、Command默认为空。
RepeatCount:单帧内的重复次数
- 每次触发后,Command 子图会被连续执行
RepeatCount次,从而在一帧内完成多次模拟步进,等效于加速模拟(例如把物理/粒子模拟跑得更快)。 - 设为
0时操作符直接返回,不执行任何命令,可用于临时"静音"一段子图。 - 源码中使用
RepeatCount.GetValue(context).Clamp(0, 100)将输入钳制在 0~100 之间,超出范围的数值会被安全截断。
SkipFrameCount:触发前的跳过帧数
- 每帧(每次 Update 调用)计数器
_callsSinceLastRefresh自增 1;当计数<= SkipFrameCount时操作符直接返回不执行,直到超过该值才触发一次执行并重置计数器。 - 默认
0表示每帧都触发,保持与父图同步的逐帧行为。 - 增大该值可让子图每隔 N+1 帧才执行一次,实现"抽帧"式减速与性能节省(例如低频轮询、低频刷新数据)。
- 源码中该值被钳制在 0~10000。
执行时序与底层实现
ExecRepeatedly 的完整执行逻辑在 ExecRepeatedly.cs 的Update方法中实现。核心流程如下:
private void Update(EvaluationContext context) { _callsSinceLastRefresh++; var repeatCount = RepeatCount.GetValue(context).Clamp(0, 100); if (repeatCount <= 0) return; var skipFrames = SkipFrameCount.GetValue(context).Clamp(0, 10000); if (_callsSinceLastRefresh <= skipFrames) return; _callsSinceLastRefresh = 0; var commands = Command.CollectedInputs; // 1. 准备阶段:调用所有命令的 PrepareAction for (int i = 0; i < commands.Count; i++) commands[i].Value?.PrepareAction?.Invoke(context); // 2. 执行阶段:重复 repeatCount 次,每次遍历所有命令取 GetValue for (int repeation = 0; repeation < repeatCount; repeation++) { for (int i = 0; i < commands.Count; i++) commands[i].GetValue(context); } // 3. 清理阶段:调用所有命令的 RestoreAction for (int i = 0; i < commands.Count; i++) commands[i].Value?.RestoreAction?.Invoke(context); Command.DirtyFlag.Clear(); }三段式命令生命周期
与 Command.cs 中定义的PrepareAction/RestoreAction委托对应,ExecRepeatedly 严格遵循"准备 → 执行 → 清理"的三段式生命周期:先通过PrepareAction完成前置准备,再按RepeatCount循环取用命令值,最后以RestoreAction恢复上下文。这种结构保证了即便重复执行多次,命令的副作用也能被正确归位。
计数器语义:触发后立即重置
值得注意的实现细节:_callsSinceLastRefresh在执行前被重置为 0,而判定条件是<= skipFrames。因此:
SkipFrameCount = 0时,每次调用计数为 1 > 0,每帧都执行;SkipFrameCount = 2时,第 1、2 次调用被跳过,第 3 次触发执行,形成"每 3 帧执行一次"的节奏;- 触发当帧本身也算作一次调用,因此实际执行周期为
SkipFrameCount + 1帧。
触发语义:逐帧持续触发
与 ExecuteOnce.cs(仅在Trigger输入变脏时执行一次,并向外暴露OutputTrigger)不同,ExecRepeatedly 没有外部触发输入,只要计数器条件满足就会持续执行,属于节流型持续执行器而非一次性触发器。
与同族操作符的定位对比
在Lib.flow流程控制家族中,ExecRepeatedly 与以下操作符承担不同职责,选型时可对照:
| 操作符 | 核心职责 | 关键输入 | 典型用途 |
|---|---|---|---|
| ExecRepeatedly | 控制子图执行频率 | RepeatCount、SkipFrameCount | 加速/减速模拟、抽帧优化 |
| Execute | 按使能开关执行子图 | Command、IsEnabled | 分组、启停部分图(见 Execute.cs) |
| ExecuteOnce | 仅在触发时执行一次 | Command、Trigger | 初始化缓冲、重置状态(见 ExecuteOnce.cs) |
| Loop | 循环迭代子图并暴露索引/进度变量 | Command、Count、IndexVariable、ProgressVariable | 迭代绘制函数(见 Loop.cs) |
- 若只想"每帧执行"或"按开关执行",用
Execute; - 若想"只在某个信号触发时跑一次",用
ExecuteOnce; - 若想"在一帧内按变量迭代多次",用
Loop; - 若想"以可控频率反复执行"(含重复多次与抽帧两种模式),用ExecRepeatedly。
典型实战场景
场景一:加速模拟
把粒子系统、弹簧动画等模拟子图接入Command,将RepeatCount设为 3~10(注意源码钳制上限 100),即可让同一帧内模拟步进多次,产生"时间加速"效果。适合用于爆炸、快速生长等需要数倍速模拟的画面。
场景二:减速 / 抽帧执行
将RepeatCount保持为 1,SkipFrameCount设为 4,则子图每 5 帧才执行一次,常用于低频更新的辅助逻辑(如远程数据轮询、低优先级刷新),在不影响主渲染的前提下显著降低每帧计算开销。
场景三:结合 Command 生命周期做受控资源更新
由于 ExecRepeatedly 遵循 Prepare/Execute/Restore 生命周期,即使RepeatCount > 1,也能安全用于需要成对准备与恢复的资源更新场景,不会因多次执行破坏上下文状态。
使用建议与注意事项
- 合理利用钳制范围:
RepeatCount上限 100、SkipFrameCount上限 10000,超限值会被静默截断,设计参数动画(如用曲线驱动这两个输入)时注意范围。 - 组合使用:可将 ExecRepeatedly 与
Loop嵌套,先节流外层频率、再在单次触发内迭代,形成"低频 + 多次"的复合节奏。 - 默认值即逐帧执行:新建节点时
RepeatCount = 1、SkipFrameCount = 0,行为等价于普通逐帧执行,可放心作为起点逐步调整。 - 性能权衡:加速模拟的本质是用单帧 CPU 时间换取更多模拟步进,
RepeatCount过大可能拉长单帧耗时,建议结合实际帧率(TiXL 的Playback.LastFrameDuration类时序数据)逐步调试。
参考资源
- 官方算子文档:.help/docs/operators/lib/flow/ExecRepeatedly.md、.help/docs/operators/lib/flow/README.md
- 核心实现:Operators/Lib/Symbols/flow/ExecRepeatedly.cs
- 默认配置:Operators/Lib/Symbols/flow/ExecRepeatedly.t3
- 命令数据类型:Core/DataTypes/Command.cs
- 同族操作符:Execute.cs、ExecuteOnce.cs、Loop.cs
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考