Expand description

Insert dependency trees of startup systems into Bevy Apps.

Define dependency trees of startup systems for a Bevy App with the startup_tree macro. Insert trees into an App with the AddStartupTree::add_startup_tree extension method. It is strongly recommended that the macro is used to generate the data structure that is consumed by add_startup_tree.

This is useful in scenarios where the startup logic is complex and would benefit from being broken up into multiple systems. Some of this startup logic can be run in parallel; others may require that certain systems run in a particular order. For example, a system that spawns a complex bevy_ui can get very large, deeply nested, and difficult to maintain. Such a system can be divided into multiple that work together to create the complex entity hierarchy. Systems that spawn children entities must run after the one that spawns the parent; this is where bevy_startup_tree becomes useful.

Bevy Compatibility

bevybevy_startup_tree
~0.10>=0.2
~0.9~0.1
<0.9Not supported

Behavior

The systems that make up a startup tree, or nodes, are grouped by depth. The startup_tree macro generates a 2-D array where each row with index i contains the nodes at depth i in the tree. This 2-D array is consumed by add_startup_tree where each depth sub-array is combined into a SystemSet.

startup_tree! {
    sys_1_a,
    sys_1_b => sys_2
}

This macro invocation would generate the following 2-D array:

[ [sys_1_a, sys_1_b], [sys_2] ]

The sets for each sub-array run in order, between the StartupSets StartupFlush and PostStartup. Additionally, each set has its own flush set that runs after it, containing only the apply_system_buffers system. The startup phase of an app with the above tree would be:

  • StartupSet::PreStartup
  • StartupSet::PreStartupFlush
  • StartupSet::Startup
  • StartupSet::StartupFlush
  • Depth 0 tree set
  • Depth 0 tree flush set
  • Depth 1 tree set
  • Depth 1 tree flush set
  • StartupSet::PostStartup
  • StartupSet::PostStartupFlush

Example

The following is an example Bevy App with a startup tree. Note that the app will go through the startup phase, run a single frame cycle, and then exit.

use bevy::{log::LogPlugin, prelude::*};
use bevy_startup_tree::{startup_tree, AddStartupTree};

fn main() {
    App::new()
        .add_plugin(TaskPoolPlugin::default())
        .add_plugin(LogPlugin::default())
        .add_startup_system(begin.in_base_set(StartupSet::Startup))
        .add_startup_tree(startup_tree! {
            sys_1_a,
            sys_1_b => sys_2_a,
            sys_1_c => {
                sys_2_b,
                sys_2_c => sys_3_a,
            },
        })
        .add_startup_system(end.in_base_set(StartupSet::PostStartup))
        .run();
}

fn begin() { info!("[Begin]"); }
fn sys_1_a() { info!("1.a"); }
fn sys_1_b() { info!("1.b"); }
fn sys_1_c() { info!("1.c"); }
fn sys_2_a() { info!("2.a"); }
fn sys_2_b() { info!("2.b"); }
fn sys_2_c() { info!("2.c"); }
fn sys_3_a() { info!("3.a"); }
fn end() { info!("[End]"); }

Output

2023-01-08T19:38:41.664766Z  INFO example_app: [Begin]
2023-01-08T19:38:41.664906Z  INFO example_app: 1.b
2023-01-08T19:38:41.664937Z  INFO example_app: 1.c
2023-01-08T19:38:41.664959Z  INFO example_app: 1.a
2023-01-08T19:38:41.665104Z  INFO example_app: 2.c
2023-01-08T19:38:41.665133Z  INFO example_app: 2.a
2023-01-08T19:38:41.665141Z  INFO example_app: 2.b
2023-01-08T19:38:41.665204Z  INFO example_app: 3.a
2023-01-08T19:38:41.665264Z  INFO example_app: [End]

Note that all of the logs for a depth (those with the same number) are grouped together. This is because all of the systems at some depth in the tree are in the same set. The sets run in order, but the systems within them do not.

The begin and end systems demonstrates when the tree runs during startup. To run a system before the tree, insert it into the StartupSet::Startup base set. To run a system after the tree, insert it into the StartupSet::PostStartup base set.

Macros

Traits