Bevy 0.18 中 .after() 和 .chain() 有什么区别?
结论
在 Bevy 0.18 中,.after() 和 .chain() 都可以指定系统的执行顺序。只要 system_a 已添加到同一个 schedule,且没有重复实例,两种写法都能让 system_b 在 system_a 之后运行。普通的 .after() 也会在需要时安排 ApplyDeferred,所以“.after() 不应用 Commands,只有 .chain() 会应用”的说法并不准确。
两者的区别在于添加系统的方式:.chain() 会把序列中的系统加入 schedule 并按顺序排列;.after() 只为当前配置的系统添加相对于目标系统或 system set 的约束,不会自动添加目标系统。Bevy 的调度配置文档和 Bevy 0.17 到 0.18 迁移指南介绍了这些行为。
.after() 也会处理延迟命令
Commands 等延迟操作不会在系统调用命令时立即修改 World,而是在 schedule 的适当位置应用。如果 system_b 需要看到 system_a 通过 Commands 排队的更改,就要建立正常的排序依赖:
app.add_systems(FixedUpdate, system_a)
.add_systems(FixedUpdate, system_b.after(system_a));
这里,system_a 已显式添加到同一个 FixedUpdate schedule。system_b.after(system_a) 建立了依赖,Bevy 会在必要时插入 deferred 同步点,让后续系统看到前序系统产生的延迟效果。
.chain() 同样会建立排序依赖,并在需要时插入同步点:
app.add_systems(FixedUpdate, (system_a, system_b).chain());
只要添加位置和依赖关系相同,这两种写法对这两个系统的执行顺序和延迟命令可见性通常是等效的。如果不希望依赖边自动应用延迟操作,可以使用 .after_ignore_deferred();普通 .after() 不具备这种行为。
Bevy 0.18 移除了旧的 SimpleExecutor。迁移指南提醒,若系统需要使用前序系统命令产生的结果,应通过 before、after 或 chain 等配置显式指定顺序,不要依赖系统的添加顺序。
.after() 不会自动添加目标系统
下面的配置只添加 system_b,并声明它在 system_a 之后运行:
app.add_systems(FixedUpdate, system_b.after(system_a));
如果没有其他代码把 system_a 添加到同一个 schedule,这条约束既不会让它运行,也无法保证 system_b 在它之后执行。如果目标系统来自 Bevy 或插件,应确认它属于同一个 schedule;否则就显式添加两个系统,或使用 .chain()。
这些排序配置不能建立跨 schedule 的执行关系。若两个系统不在同一个 schedule,排序调用会被忽略。因此,FixedUpdate 中的系统不会因此与 Update 中的系统形成执行顺序。
重复添加同一个系统会运行多次
schedule 不会根据系统函数自动去重。分别添加下面两组配置,会创建两个 system_a 系统实例:
app.add_systems(FixedUpdate, (system_a, system_b).chain())
.add_systems(FixedUpdate, (system_a, system_c).chain());
因此,每次运行 FixedUpdate 时,这两个实例都可能执行。如果 system_a 有副作用,就可能执行两次。
系统函数还会对应一个由系统类型形成的 system set。如果同一系统类型在一个 schedule 中有多个实例,再通过 .after(system_a) 或 .before(system_a) 指向该类型,目标就可能含糊。Bevy 的调度图不一定能判断你指的是哪个实例,构建时可能会报告歧义。因此,不要用系统函数类型来表示“只依赖其中某一个重复实例”。
要避免意外重复注册,可以集中管理系统的添加位置,确保每个系统只添加一次。若要为多组系统设置可复用的顺序,可以定义显式的 SystemSet 并对 set 排序。这样能表达组之间的关系,但组中的系统仍需按预期添加。
适用于 system sets 的规则
对于 system sets,规则相同:.before() 和 .after() 添加依赖关系,不会自动把目标 set 或其中的系统加入 schedule;.chain() 用于排列配置序列,通常也更方便同时添加其中的内容。set 中的成员会继承相应的集合级顺序约束。如果需要让前序系统的 Commands 生效,应使用会应用 deferred 的正常排序配置,而不是 *_ignore_deferred() 变体。
备注:内容仅供参考。
